() {
+ public UsbInterface createFromParcel(Parcel in) {
+ int id = in.readInt();
+ int Class = in.readInt();
+ int subClass = in.readInt();
+ int protocol = in.readInt();
+ Parcelable[] endpoints = in.readParcelableArray(UsbEndpoint.class.getClassLoader());
+ UsbInterface result = new UsbInterface(id, Class, subClass, protocol, endpoints);
+ for (int i = 0; i < endpoints.length; i++) {
+ ((UsbEndpoint)endpoints[i]).setInterface(result);
+ }
+ return result;
+ }
+
+ public UsbInterface[] newArray(int size) {
+ return new UsbInterface[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mId);
+ parcel.writeInt(mClass);
+ parcel.writeInt(mSubclass);
+ parcel.writeInt(mProtocol);
+ parcel.writeParcelableArray(mEndpoints, 0);
+ }
+}
diff --git a/core/java/android/hardware/UsbManager.java b/core/java/android/hardware/UsbManager.java
index 18790d254e00c..8fad210619d4a 100644
--- a/core/java/android/hardware/UsbManager.java
+++ b/core/java/android/hardware/UsbManager.java
@@ -17,17 +17,31 @@
package android.hardware;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.HashMap;
/**
- * Class for accessing USB state information.
- * @hide
+ * This class allows you to access the state of USB, both in host and device mode.
+ *
+ * You can obtain an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ *
+ * {@samplecode
+ * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ * }
*/
public class UsbManager {
+ private static final String TAG = "UsbManager";
+
/**
- * Broadcast Action: A sticky broadcast for USB state change events.
+ * Broadcast Action: A sticky broadcast for USB state change events when in device mode.
*
* This is a sticky broadcast for clients that includes USB connected/disconnected state,
* the USB configuration that is currently set and a bundle containing name/value pairs
@@ -39,6 +53,22 @@ public class UsbManager {
public static final String ACTION_USB_STATE =
"android.hardware.action.USB_STATE";
+ /**
+ * Broadcast Action: A broadcast for USB device attached event.
+ *
+ * This intent is sent when a USB device is attached to the USB bus when in host mode.
+ */
+ public static final String ACTION_USB_DEVICE_ATTACHED =
+ "android.hardware.action.USB_DEVICE_ATTACHED";
+
+ /**
+ * Broadcast Action: A broadcast for USB device detached event.
+ *
+ * This intent is sent when a USB device is detached from the USB bus when in host mode.
+ */
+ public static final String ACTION_USB_DEVICE_DETACHED =
+ "android.hardware.action.USB_DEVICE_DETACHED";
+
/**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
@@ -87,6 +117,103 @@ public class UsbManager {
*/
public static final String USB_FUNCTION_DISABLED = "disabled";
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
+ * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
+ * containing the device's ID (String).
+ */
+ public static final String EXTRA_DEVICE_NAME = "device_name";
+
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+ * containing the device's vendor ID (int).
+ */
+ public static final String EXTRA_VENDOR_ID = "vendor_id";
+
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+ * containing the device's product ID (int).
+ */
+ public static final String EXTRA_PRODUCT_ID = "product_id";
+
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+ * containing the device's class (int).
+ */
+ public static final String EXTRA_DEVICE_CLASS = "device_class";
+
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+ * containing the device's class (int).
+ */
+ public static final String EXTRA_DEVICE_SUBCLASS = "device_subclass";
+
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+ * containing the device's class (int).
+ */
+ public static final String EXTRA_DEVICE_PROTOCOL = "device_protocol";
+
+ /**
+ * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast
+ * containing the UsbDevice object for the device.
+ */
+ public static final String EXTRA_DEVICE = "device";
+
+ private IUsbManager mService;
+
+ /**
+ * {@hide}
+ */
+ public UsbManager(IUsbManager service) {
+ mService = service;
+ }
+
+ /**
+ * Returns a HashMap containing all USB devices currently attached.
+ * USB device name is the key for the returned HashMap.
+ * The result will be empty if no devices are attached, or if
+ * USB host mode is inactive or unsupported.
+ *
+ * @return HashMap containing all connected USB devices.
+ */
+ public HashMap getDeviceList() {
+ Bundle bundle = new Bundle();
+ try {
+ mService.getDeviceList(bundle);
+ HashMap result = new HashMap();
+ for (String name : bundle.keySet()) {
+ result.put(name, (UsbDevice)bundle.get(name));
+ }
+ return result;
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in getDeviceList", e);
+ return null;
+ }
+ }
+
+ /**
+ * Opens the device so it can be used to send and receive
+ * data using {@link android.hardware.UsbRequest}.
+ *
+ * @param device the device to open
+ * @return true if we successfully opened the device
+ */
+ public boolean openDevice(UsbDevice device) {
+ try {
+ ParcelFileDescriptor pfd = mService.openDevice(device.getDeviceName());
+ if (pfd == null) {
+ return false;
+ }
+ boolean result = device.open(pfd);
+ pfd.close();
+ return result;
+ } catch (Exception e) {
+ Log.e(TAG, "exception in UsbManager.openDevice", e);
+ return false;
+ }
+ }
+
private static File getFunctionEnableFile(String function) {
return new File("/sys/class/usb_composite/" + function + "/enable");
}
@@ -94,6 +221,9 @@ public class UsbManager {
/**
* Returns true if the specified USB function is supported by the kernel.
* Note that a USB function maybe supported but disabled.
+ *
+ * @param function name of the USB function
+ * @return true if the USB function is supported.
*/
public static boolean isFunctionSupported(String function) {
return getFunctionEnableFile(function).exists();
@@ -101,6 +231,9 @@ public class UsbManager {
/**
* Returns true if the specified USB function is currently enabled.
+ *
+ * @param function name of the USB function
+ * @return true if the USB function is enabled.
*/
public static boolean isFunctionEnabled(String function) {
try {
diff --git a/core/java/android/hardware/UsbRequest.java b/core/java/android/hardware/UsbRequest.java
new file mode 100644
index 0000000000000..ae3a289e11d5a
--- /dev/null
+++ b/core/java/android/hardware/UsbRequest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A class representing USB request packet.
+ * This can be used for both reading and writing data to or from a
+ * {@link android.hardware.UsbDevice}.
+ * UsbRequests are sent asynchronously via {@link #queue} and the results
+ * are read by {@link android.hardware.UsbDevice#requestWait}.
+ */
+public class UsbRequest {
+
+ private static final String TAG = "UsbRequest";
+
+ // used by the JNI code
+ private int mNativeContext;
+
+ private UsbEndpoint mEndpoint;
+
+ // for temporarily saving current buffer across queue and dequeue
+ private ByteBuffer mBuffer;
+ private int mLength;
+
+ // for client use
+ private Object mClientData;
+
+ public UsbRequest() {
+ }
+
+ /**
+ * Initializes the request so it can read or write data on the given endpoint.
+ * Whether the request allows reading or writing depends on the direction of the endpoint.
+ *
+ * @param endpoint the endpoint to be used for this request.
+ * @return true if the request was successfully opened.
+ */
+ public boolean initialize(UsbEndpoint endpoint) {
+ mEndpoint = endpoint;
+ return native_init(endpoint.getDevice(),
+ endpoint.getAddress(), endpoint.getAttributes(),
+ endpoint.getMaxPacketSize(), endpoint.getInterval());
+ }
+
+ /**
+ * Releases all resources related to this request.
+ */
+ public void close() {
+ mEndpoint = null;
+ native_close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mEndpoint != null) {
+ Log.v(TAG, "endpoint still open in finalize(): " + this);
+ close();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Returns the endpoint for the request, or null if the request is not opened.
+ *
+ * @return the request's endpoint
+ */
+ public UsbEndpoint getEndpoint() {
+ return mEndpoint;
+ }
+
+ /**
+ * Returns the client data for the request.
+ * This can be used in conjunction with {@link #setClientData}
+ * to associate another object with this request, which can be useful for
+ * maintaining state between calls to {@link #queue} and
+ * {@link android.hardware.UsbDevice#requestWait}
+ *
+ * @return the client data for the request
+ */
+ public Object getClientData() {
+ return mClientData;
+ }
+
+ /**
+ * Sets the client data for the request.
+ * This can be used in conjunction with {@link #getClientData}
+ * to associate another object with this request, which can be useful for
+ * maintaining state between calls to {@link #queue} and
+ * {@link android.hardware.UsbDevice#requestWait}
+ *
+ * @param data the client data for the request
+ */
+ public void setClientData(Object data) {
+ mClientData = data;
+ }
+
+ /**
+ * Queues the request to send or receive data on its endpoint.
+ * For OUT endpoints, the given buffer data will be sent on the endpoint.
+ * For IN endpoints, the endpoint will attempt to read the given number of bytes
+ * into the specified buffer.
+ * If the queueing operation is successful, we return true and the result will be
+ * returned via {@link android.hardware.UsbDevice#requestWait}
+ *
+ * @param buffer the buffer containing the bytes to write, or location to store
+ * the results of a read
+ * @param length number of bytes to read or write
+ * @return true if the queueing operation succeeded
+ */
+ public boolean queue(ByteBuffer buffer, int length) {
+ boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
+ boolean result;
+ if (buffer.isDirect()) {
+ result = native_queue_direct(buffer, length, out);
+ } else if (buffer.hasArray()) {
+ result = native_queue_array(buffer.array(), length, out);
+ } else {
+ throw new IllegalArgumentException("buffer is not direct and has no array");
+ }
+ if (result) {
+ // save our buffer for when the request has completed
+ mBuffer = buffer;
+ mLength = length;
+ }
+ return result;
+ }
+
+ /* package */ void dequeue() {
+ boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
+ if (mBuffer.isDirect()) {
+ native_dequeue_direct();
+ } else {
+ native_dequeue_array(mBuffer.array(), mLength, out);
+ }
+ mBuffer = null;
+ mLength = 0;
+ }
+
+ /**
+ * Cancels a pending queue operation.
+ *
+ * @return true if cancelling succeeded
+ */
+ public boolean cancel() {
+ return native_cancel();
+ }
+
+ private native boolean native_init(UsbDevice device, int ep_address, int ep_attributes,
+ int ep_max_packet_size, int ep_interval);
+ private native void native_close();
+ private native boolean native_queue_array(byte[] buffer, int length, boolean out);
+ private native void native_dequeue_array(byte[] buffer, int length, boolean out);
+ private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out);
+ private native void native_dequeue_direct();
+ private native boolean native_cancel();
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c635b39f32c3c..d1e7e5cfbb49a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -124,6 +124,8 @@ LOCAL_SRC_FILES:= \
android_media_ToneGenerator.cpp \
android_hardware_Camera.cpp \
android_hardware_SensorManager.cpp \
+ android_hardware_UsbDevice.cpp \
+ android_hardware_UsbRequest.cpp \
android_debug_JNITest.cpp \
android_util_FileObserver.cpp \
android/opengl/poly_clip.cpp.arm \
@@ -202,6 +204,7 @@ LOCAL_SHARED_LIBRARIES := \
libwpa_client \
libjpeg \
libnfc_ndef \
+ libusbhost \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 342b8840ed513..c1c6c91e5adc5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,8 @@ extern int register_android_opengl_jni_GLES20(JNIEnv* env);
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
+extern int register_android_hardware_UsbDevice(JNIEnv *env);
+extern int register_android_hardware_UsbRequest(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
@@ -1275,6 +1277,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_SensorManager),
+ REG_JNI(register_android_hardware_UsbDevice),
+ REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioTrack),
diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp
new file mode 100644
index 0000000000000..22cf3873a80de
--- /dev/null
+++ b/core/jni/android_hardware_UsbDevice.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbDeviceJNI"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+using namespace android;
+
+static jfieldID field_context;
+
+struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice)
+{
+ return (struct usb_device*)env->GetIntField(javaDevice, field_context);
+}
+
+// in android_hardware_UsbEndpoint.cpp
+extern struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint);
+
+static jboolean
+android_hardware_UsbDevice_open(JNIEnv *env, jobject thiz, jstring deviceName,
+ jobject fileDescriptor)
+{
+ int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+ // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
+ fd = dup(fd);
+ if (fd < 0)
+ return false;
+
+ const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+ struct usb_device* device = usb_device_new(deviceNameStr, fd);
+ if (device) {
+ env->SetIntField(thiz, field_context, (int)device);
+ } else {
+ LOGE("usb_device_open failed for %s", deviceNameStr);
+ close(fd);
+ }
+
+ env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+ return (device != NULL);
+}
+
+static void
+android_hardware_UsbDevice_close(JNIEnv *env, jobject thiz)
+{
+ LOGD("close\n");
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (device) {
+ usb_device_close(device);
+ env->SetIntField(thiz, field_context, 0);
+ }
+}
+
+static jint
+android_hardware_UsbDevice_get_fd(JNIEnv *env, jobject thiz)
+{
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGE("device is closed in native_get_fd");
+ return -1;
+ }
+ return usb_device_get_fd(device);
+}
+
+static jint
+android_hardware_UsbDevice_claim_interface(JNIEnv *env, jobject thiz, int interfaceID, jboolean force)
+{
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGE("device is closed in native_claim_interface");
+ return -1;
+ }
+
+ int ret = usb_device_claim_interface(device, interfaceID);
+ if (ret && force && errno == EBUSY) {
+ // disconnect kernel driver and try again
+ usb_device_connect_kernel_driver(device, interfaceID, false);
+ ret = usb_device_claim_interface(device, interfaceID);
+ }
+ return ret;
+}
+
+static jint
+android_hardware_UsbDevice_release_interface(JNIEnv *env, jobject thiz, int interfaceID)
+{
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGE("device is closed in native_release_interface");
+ return -1;
+ }
+ int ret = usb_device_release_interface(device, interfaceID);
+ if (ret == 0) {
+ // allow kernel to reconnect its driver
+ usb_device_connect_kernel_driver(device, interfaceID, true);
+ }
+ return ret;
+}
+
+static jint
+android_hardware_UsbDevice_control_request(JNIEnv *env, jobject thiz,
+ jint requestType, jint request, jint value, jint index,
+ jbyteArray buffer, jint length)
+{
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGE("device is closed in native_control_request");
+ return -1;
+ }
+
+ jbyte* bufferBytes = NULL;
+ if (buffer) {
+ if (env->GetArrayLength(buffer) < length) {
+ env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL);
+ return -1;
+ }
+ bufferBytes = env->GetByteArrayElements(buffer, 0);
+ }
+
+ jint result = usb_device_send_control(device, requestType, request,
+ value, index, length, bufferBytes);
+
+ if (bufferBytes)
+ env->ReleaseByteArrayElements(buffer, bufferBytes, 0);
+
+ return result;
+}
+
+static jobject
+android_hardware_UsbDevice_request_wait(JNIEnv *env, jobject thiz)
+{
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGE("device is closed in native_request_wait");
+ return NULL;
+ }
+
+ struct usb_request* request = usb_request_wait(device);
+ if (request)
+ return (jobject)request->client_data;
+ else
+ return NULL;
+}
+
+static jstring
+android_hardware_UsbDevice_get_serial(JNIEnv *env, jobject thiz)
+{
+ struct usb_device* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGE("device is closed in native_request_wait");
+ return NULL;
+ }
+ char* serial = usb_device_get_serial(device);
+ if (!serial)
+ return NULL;
+ jstring result = env->NewStringUTF(serial);
+ free(serial);
+ return result;
+}
+
+static jint
+android_hardware_UsbDevice_get_device_id(JNIEnv *env, jobject clazz, jstring name)
+{
+ const char *nameStr = env->GetStringUTFChars(name, NULL);
+ int id = usb_device_get_unique_id_from_name(nameStr);
+ env->ReleaseStringUTFChars(name, nameStr);
+ return id;
+}
+
+static jstring
+android_hardware_UsbDevice_get_device_name(JNIEnv *env, jobject clazz, jint id)
+{
+ char* name = usb_device_get_name_from_unique_id(id);
+ jstring result = env->NewStringUTF(name);
+ free(name);
+ return result;
+}
+
+static JNINativeMethod method_table[] = {
+ {"native_open", "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
+ (void *)android_hardware_UsbDevice_open},
+ {"native_close", "()V", (void *)android_hardware_UsbDevice_close},
+ {"native_get_fd", "()I", (void *)android_hardware_UsbDevice_get_fd},
+ {"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDevice_claim_interface},
+ {"native_release_interface","(I)Z", (void *)android_hardware_UsbDevice_release_interface},
+ {"native_control_request", "(IIII[BI)I",
+ (void *)android_hardware_UsbDevice_control_request},
+ {"native_request_wait", "()Landroid/hardware/UsbRequest;",
+ (void *)android_hardware_UsbDevice_request_wait},
+ { "native_get_serial", "()Ljava/lang/String;",
+ (void*)android_hardware_UsbDevice_get_serial },
+
+ // static methods
+ { "native_get_device_id", "(Ljava/lang/String;)I",
+ (void*)android_hardware_UsbDevice_get_device_id },
+ { "native_get_device_name", "(I)Ljava/lang/String;",
+ (void*)android_hardware_UsbDevice_get_device_name },
+};
+
+int register_android_hardware_UsbDevice(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("android/hardware/UsbDevice");
+ if (clazz == NULL) {
+ LOGE("Can't find android/hardware/UsbDevice");
+ return -1;
+ }
+ field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (field_context == NULL) {
+ LOGE("Can't find UsbDevice.mNativeContext");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbDevice",
+ method_table, NELEM(method_table));
+}
+
diff --git a/core/jni/android_hardware_UsbEndpoint.cpp b/core/jni/android_hardware_UsbEndpoint.cpp
new file mode 100644
index 0000000000000..00c82351ac120
--- /dev/null
+++ b/core/jni/android_hardware_UsbEndpoint.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbEndpoint"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include
+
+#include
+
+using namespace android;
+
+static jfieldID field_context;
+static jfieldID field_address;
+static jfieldID field_attributes;
+static jfieldID field_max_packet_size;
+static jfieldID field_interval;
+
+struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint)
+{
+ return (struct usb_endpoint*)env->GetIntField(javaEndpoint, field_context);
+}
+
+// in android_hardware_UsbDevice.cpp
+extern struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice);
+
+static jboolean
+android_hardware_UsbEndpoint_init(JNIEnv *env, jobject thiz, jobject javaDevice)
+{
+ LOGD("open\n");
+
+ struct usb_device* device = get_device_from_object(env, javaDevice);
+ if (!device) {
+ LOGE("device null in native_init");
+ return false;
+ }
+
+ // construct an endpoint descriptor from the Java object fields
+ struct usb_endpoint_descriptor desc;
+ desc.bLength = USB_DT_ENDPOINT_SIZE;
+ desc.bDescriptorType = USB_DT_ENDPOINT;
+ desc.bEndpointAddress = env->GetIntField(thiz, field_address);
+ desc.bmAttributes = env->GetIntField(thiz, field_attributes);
+ desc.wMaxPacketSize = env->GetIntField(thiz, field_max_packet_size);
+ desc.bInterval = env->GetIntField(thiz, field_interval);
+
+ struct usb_endpoint* endpoint = usb_endpoint_init(device, &desc);
+ if (endpoint)
+ env->SetIntField(thiz, field_context, (int)device);
+ return (endpoint != NULL);
+}
+
+static void
+android_hardware_UsbEndpoint_close(JNIEnv *env, jobject thiz)
+{
+ LOGD("close\n");
+ struct usb_endpoint* endpoint = get_endpoint_from_object(env, thiz);
+ if (endpoint) {
+ usb_endpoint_close(endpoint);
+ env->SetIntField(thiz, field_context, 0);
+ }
+}
+
+static JNINativeMethod method_table[] = {
+ {"native_init", "(Landroid/hardware/UsbDevice;)Z",
+ (void *)android_hardware_UsbEndpoint_init},
+ {"native_close", "()V", (void *)android_hardware_UsbEndpoint_close},
+};
+
+int register_android_hardware_UsbEndpoint(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("android/hardware/UsbEndpoint");
+ if (clazz == NULL) {
+ LOGE("Can't find android/hardware/UsbEndpoint");
+ return -1;
+ }
+ field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (field_context == NULL) {
+ LOGE("Can't find UsbEndpoint.mNativeContext");
+ return -1;
+ }
+ field_address = env->GetFieldID(clazz, "mAddress", "I");
+ if (field_address == NULL) {
+ LOGE("Can't find UsbEndpoint.mAddress");
+ return -1;
+ }
+ field_attributes = env->GetFieldID(clazz, "mAttributes", "I");
+ if (field_attributes == NULL) {
+ LOGE("Can't find UsbEndpoint.mAttributes");
+ return -1;
+ }
+ field_max_packet_size = env->GetFieldID(clazz, "mMaxPacketSize", "I");
+ if (field_max_packet_size == NULL) {
+ LOGE("Can't find UsbEndpoint.mMaxPacketSize");
+ return -1;
+ }
+ field_interval = env->GetFieldID(clazz, "mInterval", "I");
+ if (field_interval == NULL) {
+ LOGE("Can't find UsbEndpoint.mInterval");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbEndpoint",
+ method_table, NELEM(method_table));
+}
+
diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp
new file mode 100644
index 0000000000000..710afae643538
--- /dev/null
+++ b/core/jni/android_hardware_UsbRequest.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbRequestJNI"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include
+
+#include
+
+using namespace android;
+
+static jfieldID field_context;
+
+struct usb_request* get_request_from_object(JNIEnv* env, jobject java_request)
+{
+ return (struct usb_request*)env->GetIntField(java_request, field_context);
+}
+
+// in android_hardware_UsbDevice.cpp
+extern struct usb_device* get_device_from_object(JNIEnv* env, jobject java_device);
+
+static jboolean
+android_hardware_UsbRequest_init(JNIEnv *env, jobject thiz, jobject java_device,
+ jint ep_address, jint ep_attributes, jint ep_max_packet_size, jint ep_interval)
+{
+ LOGD("init\n");
+
+ struct usb_device* device = get_device_from_object(env, java_device);
+ if (!device) {
+ LOGE("device null in native_init");
+ return false;
+ }
+
+ // construct an endpoint descriptor from the Java object fields
+ struct usb_endpoint_descriptor desc;
+ desc.bLength = USB_DT_ENDPOINT_SIZE;
+ desc.bDescriptorType = USB_DT_ENDPOINT;
+ desc.bEndpointAddress = ep_address;
+ desc.bmAttributes = ep_attributes;
+ desc.wMaxPacketSize = ep_max_packet_size;
+ desc.bInterval = ep_interval;
+
+ struct usb_request* request = usb_request_new(device, &desc);
+ if (request)
+ env->SetIntField(thiz, field_context, (int)request);
+ return (request != NULL);
+}
+
+static void
+android_hardware_UsbRequest_close(JNIEnv *env, jobject thiz)
+{
+ LOGD("close\n");
+ struct usb_request* request = get_request_from_object(env, thiz);
+ if (request) {
+ usb_request_free(request);
+ env->SetIntField(thiz, field_context, 0);
+ }
+}
+
+static jboolean
+android_hardware_UsbRequest_queue_array(JNIEnv *env, jobject thiz,
+ jbyteArray buffer, jint length, jboolean out)
+{
+ struct usb_request* request = get_request_from_object(env, thiz);
+ if (!request) {
+ LOGE("request is closed in native_queue");
+ return false;
+ }
+
+ if (buffer && length) {
+ request->buffer = malloc(length);
+ if (!request->buffer)
+ return false;
+ if (out) {
+ // copy data from Java buffer to native buffer
+ env->GetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
+ }
+ } else {
+ request->buffer = NULL;
+ }
+ request->buffer_length = length;
+
+ if (usb_request_queue(request)) {
+ if (request->buffer) {
+ // free our buffer if usb_request_queue fails
+ free(request->buffer);
+ request->buffer = NULL;
+ }
+ return false;
+ } else {
+ // save a reference to ourselves so UsbDevice.waitRequest() can find us
+ request->client_data = (void *)env->NewGlobalRef(thiz);
+ return true;
+ }
+}
+
+static void
+android_hardware_UsbRequest_dequeue_array(JNIEnv *env, jobject thiz,
+ jbyteArray buffer, jint length, jboolean out)
+{
+ struct usb_request* request = get_request_from_object(env, thiz);
+ if (!request) {
+ LOGE("request is closed in native_dequeue");
+ return;
+ }
+
+ if (buffer && length && request->buffer && !out) {
+ // copy data from native buffer to Java buffer
+ env->SetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
+ }
+ free(request->buffer);
+ env->DeleteGlobalRef((jobject)request->client_data);
+
+}
+
+static jboolean
+android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz,
+ jobject buffer, jint length, jboolean out)
+{
+ struct usb_request* request = get_request_from_object(env, thiz);
+ if (!request) {
+ LOGE("request is closed in native_queue");
+ return false;
+ }
+
+ if (buffer && length) {
+ request->buffer = env->GetDirectBufferAddress(buffer);
+ if (!request->buffer)
+ return false;
+ } else {
+ request->buffer = NULL;
+ }
+ request->buffer_length = length;
+
+ if (usb_request_queue(request)) {
+ request->buffer = NULL;
+ return false;
+ } else {
+ // save a reference to ourselves so UsbDevice.waitRequest() can find us
+ // we also need this to make sure our native buffer is not deallocated
+ // while IO is active
+ request->client_data = (void *)env->NewGlobalRef(thiz);
+ return true;
+ }
+}
+
+static void
+android_hardware_UsbRequest_dequeue_direct(JNIEnv *env, jobject thiz)
+{
+ struct usb_request* request = get_request_from_object(env, thiz);
+ if (!request) {
+ LOGE("request is closed in native_dequeue");
+ return;
+ }
+ // all we need to do is delete our global ref
+ env->DeleteGlobalRef((jobject)request->client_data);
+}
+
+static jboolean
+android_hardware_UsbRequest_cancel(JNIEnv *env, jobject thiz)
+{
+ struct usb_request* request = get_request_from_object(env, thiz);
+ if (!request) {
+ LOGE("request is closed in native_cancel");
+ return false;
+ }
+ return (usb_request_cancel(request) == 0);
+}
+
+static JNINativeMethod method_table[] = {
+ {"native_init", "(Landroid/hardware/UsbDevice;IIII)Z",
+ (void *)android_hardware_UsbRequest_init},
+ {"native_close", "()V", (void *)android_hardware_UsbRequest_close},
+ {"native_queue_array", "([BIZ)Z", (void *)android_hardware_UsbRequest_queue_array},
+ {"native_dequeue_array", "([BIZ)V", (void *)android_hardware_UsbRequest_dequeue_array},
+ {"native_queue_direct", "(Ljava/nio/ByteBuffer;IZ)Z",
+ (void *)android_hardware_UsbRequest_queue_direct},
+ {"native_dequeue_direct", "()V", (void *)android_hardware_UsbRequest_dequeue_direct},
+ {"native_cancel", "()Z", (void *)android_hardware_UsbRequest_cancel},
+};
+
+int register_android_hardware_UsbRequest(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("android/hardware/UsbRequest");
+ if (clazz == NULL) {
+ LOGE("Can't find android/hardware/UsbRequest");
+ return -1;
+ }
+ field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (field_context == NULL) {
+ LOGE("Can't find UsbRequest.mNativeContext");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbRequest",
+ method_table, NELEM(method_table));
+}
+
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 25d3aca3b6c41..a184a5c922059 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -85,6 +85,8 @@
+
+
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 978946f546a37..62ff064a99a3d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -405,6 +405,7 @@ class ServerThread extends Thread {
Slog.i(TAG, "USB Observer");
// Listen for USB changes
usb = new UsbService(context);
+ ServiceManager.addService(Context.USB_SERVICE, usb);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting UsbService", e);
}
diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java
index 8ef03d436ad20..5c03fb2a76e4b 100644
--- a/services/java/com/android/server/UsbService.java
+++ b/services/java/com/android/server/UsbService.java
@@ -19,10 +19,18 @@ package com.android.server;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.hardware.IUsbManager;
+import android.hardware.UsbConstants;
+import android.hardware.UsbDevice;
+import android.hardware.UsbEndpoint;
+import android.hardware.UsbInterface;
import android.hardware.UsbManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
import android.os.UEventObserver;
import android.provider.Settings;
import android.util.Log;
@@ -32,11 +40,12 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
+import java.util.HashMap;
/**
* UsbService monitors for changes to USB state.
*/
-class UsbService {
+class UsbService extends IUsbManager.Stub {
private static final String TAG = UsbService.class.getSimpleName();
private static final boolean LOG = false;
@@ -68,10 +77,12 @@ class UsbService {
private int mLastConnected = -1;
private int mLastConfiguration = -1;
- // lists of enabled and disabled USB functions
+ // lists of enabled and disabled USB functions (for USB device mode)
private final ArrayList mEnabledFunctions = new ArrayList();
private final ArrayList mDisabledFunctions = new ArrayList();
+ private final HashMap mDevices = new HashMap();
+
private boolean mSystemReady;
private final Context mContext;
@@ -186,8 +197,106 @@ class UsbService {
}
}
+ // called from JNI in monitorUsbHostBus()
+ private void usbDeviceAdded(String deviceName, int vendorID, int productID,
+ int deviceClass, int deviceSubclass, int deviceProtocol,
+ /* array of quintuples containing id, class, subclass, protocol
+ and number of endpoints for each interface */
+ int[] interfaceValues,
+ /* array of quadruples containing address, attributes, max packet size
+ and interval for each endpoint */
+ int[] endpointValues) {
+
+ // ignore hubs
+ if (deviceClass == UsbConstants.USB_CLASS_HUB) {
+ return;
+ }
+
+ synchronized (mDevices) {
+ if (mDevices.get(deviceName) != null) {
+ Log.w(TAG, "device already on mDevices list: " + deviceName);
+ return;
+ }
+
+ int numInterfaces = interfaceValues.length / 5;
+ Parcelable[] interfaces = new UsbInterface[numInterfaces];
+ try {
+ // repackage interfaceValues as an array of UsbInterface
+ int intf, endp, ival = 0, eval = 0;
+ boolean hasGoodInterface = false;
+ for (intf = 0; intf < numInterfaces; intf++) {
+ int interfaceId = interfaceValues[ival++];
+ int interfaceClass = interfaceValues[ival++];
+ int interfaceSubclass = interfaceValues[ival++];
+ int interfaceProtocol = interfaceValues[ival++];
+ int numEndpoints = interfaceValues[ival++];
+
+ Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
+ for (endp = 0; endp < numEndpoints; endp++) {
+ int address = endpointValues[eval++];
+ int attributes = endpointValues[eval++];
+ int maxPacketSize = endpointValues[eval++];
+ int interval = endpointValues[eval++];
+ endpoints[endp] = new UsbEndpoint(address, attributes,
+ maxPacketSize, interval);
+ }
+
+ if (interfaceClass != UsbConstants.USB_CLASS_HUB) {
+ hasGoodInterface = true;
+ }
+ interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
+ interfaceSubclass, interfaceProtocol, endpoints);
+ }
+
+ if (!hasGoodInterface) {
+ return;
+ }
+ } catch (Exception e) {
+ // beware of index out of bound exceptions, which might happen if
+ // a device does not set bNumEndpoints correctly
+ Log.e(TAG, "error parsing USB descriptors", e);
+ return;
+ }
+
+ UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
+ deviceClass, deviceSubclass, deviceProtocol, interfaces);
+ mDevices.put(deviceName, device);
+
+ Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName);
+ intent.putExtra(UsbManager.EXTRA_VENDOR_ID, vendorID);
+ intent.putExtra(UsbManager.EXTRA_PRODUCT_ID, productID);
+ intent.putExtra(UsbManager.EXTRA_DEVICE_CLASS, deviceClass);
+ intent.putExtra(UsbManager.EXTRA_DEVICE_SUBCLASS, deviceSubclass);
+ intent.putExtra(UsbManager.EXTRA_DEVICE_PROTOCOL, deviceProtocol);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ Log.d(TAG, "usbDeviceAdded, sending " + intent);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ // called from JNI in monitorUsbHostBus()
+ private void usbDeviceRemoved(String deviceName) {
+ synchronized (mDevices) {
+ UsbDevice device = mDevices.remove(deviceName);
+ if (device != null) {
+ Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName);
+ Log.d(TAG, "usbDeviceRemoved, sending " + intent);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+
private void initHostSupport() {
- // temporarily disabled
+ // Create a thread to call into native code to wait for USB host events.
+ // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
+ Runnable runnable = new Runnable() {
+ public void run() {
+ monitorUsbHostBus();
+ }
+ };
+ new Thread(null, runnable, "UsbService host thread").start();
}
void systemReady() {
@@ -208,6 +317,21 @@ class UsbService {
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
}
+ /* Returns a list of all currently attached USB devices */
+ public void getDeviceList(Bundle devices) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
+ synchronized (mDevices) {
+ for (String name : mDevices.keySet()) {
+ devices.putParcelable(name, mDevices.get(name));
+ }
+ }
+ }
+
+ public ParcelFileDescriptor openDevice(String deviceName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
+ return nativeOpenDevice(deviceName);
+ }
+
private final Handler mHandler = new Handler() {
private void addEnabledFunctions(Intent intent) {
// include state of all USB functions in our extras
@@ -249,4 +373,7 @@ class UsbService {
}
}
};
+
+ private native void monitorUsbHostBus();
+ private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
}
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index f5a5b4da5e807..be37d5d56d9bd 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \
com_android_server_LightsService.cpp \
com_android_server_PowerManagerService.cpp \
com_android_server_SystemServer.cpp \
+ com_android_server_UsbService.cpp \
com_android_server_VibratorService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
onload.cpp
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
new file mode 100644
index 0000000000000..ef22111d4b2a1
--- /dev/null
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbService"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "utils/Vector.h"
+
+#include
+
+#include
+#include
+
+namespace android
+{
+
+static struct file_descriptor_offsets_t
+{
+ jclass mClass;
+ jmethodID mConstructor;
+ jfieldID mDescriptor;
+} gFileDescriptorOffsets;
+
+static struct parcel_file_descriptor_offsets_t
+{
+ jclass mClass;
+ jmethodID mConstructor;
+} gParcelFileDescriptorOffsets;
+
+static jmethodID method_usbDeviceAdded;
+static jmethodID method_usbDeviceRemoved;
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ struct usb_device *device = usb_device_open(devname);
+ if (!device) {
+ LOGE("usb_device_open failed\n");
+ return 0;
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)client_data;
+ Vector interfaceValues;
+ Vector endpointValues;
+ const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
+
+ uint16_t vendorId = usb_device_get_vendor_id(device);
+ uint16_t productId = usb_device_get_product_id(device);
+ uint8_t deviceClass = deviceDesc->bDeviceClass;
+ uint8_t deviceSubClass = deviceDesc->bDeviceSubClass;
+ uint8_t protocol = deviceDesc->bDeviceProtocol;
+
+ usb_descriptor_iter_init(device, &iter);
+
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+ if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+ // push class, subclass, protocol and number of endpoints into interfaceValues vector
+ interfaceValues.add(interface->bInterfaceNumber);
+ interfaceValues.add(interface->bInterfaceClass);
+ interfaceValues.add(interface->bInterfaceSubClass);
+ interfaceValues.add(interface->bInterfaceProtocol);
+ interfaceValues.add(interface->bNumEndpoints);
+ } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+ struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
+
+ // push address, attributes, max packet size and interval into endpointValues vector
+ endpointValues.add(endpoint->bEndpointAddress);
+ endpointValues.add(endpoint->bmAttributes);
+ endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize));
+ endpointValues.add(endpoint->bInterval);
+ }
+ }
+
+ usb_device_close(device);
+
+ // handle generic device notification
+ int length = interfaceValues.size();
+ jintArray interfaceArray = env->NewIntArray(length);
+ env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array());
+
+ length = endpointValues.size();
+ jintArray endpointArray = env->NewIntArray(length);
+ env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array());
+
+ env->CallVoidMethod(thiz, method_usbDeviceAdded,
+ env->NewStringUTF(devname), vendorId, productId, deviceClass,
+ deviceSubClass, protocol, interfaceArray, endpointArray);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+ return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)client_data;
+
+ env->CallVoidMethod(thiz, method_usbDeviceRemoved, env->NewStringUTF(devname));
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return 0;
+}
+
+static void android_server_UsbService_monitorUsbHostBus(JNIEnv *env, jobject thiz)
+{
+ struct usb_host_context* context = usb_host_init();
+ if (!context) {
+ LOGE("usb_host_init failed");
+ return;
+ }
+ // this will never return so it is safe to pass thiz directly
+ usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
+}
+
+static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, jstring deviceName)
+{
+ const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+ struct usb_device* device = usb_device_open(deviceNameStr);
+ env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+
+ if (!device)
+ return NULL;
+
+ int fd = usb_device_get_fd(device);
+ if (fd < 0)
+ return NULL;
+ int newFD = dup(fd);
+ usb_device_close(device);
+
+ jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
+ gFileDescriptorOffsets.mConstructor);
+ if (fileDescriptor != NULL) {
+ env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD);
+ } else {
+ return NULL;
+ }
+ return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+ gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
+}
+
+static JNINativeMethod method_table[] = {
+ { "monitorUsbHostBus", "()V", (void*)android_server_UsbService_monitorUsbHostBus },
+ { "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
+ (void*)android_server_UsbService_openDevice },
+};
+
+int register_android_server_UsbService(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("com/android/server/UsbService");
+ if (clazz == NULL) {
+ LOGE("Can't find com/android/server/UsbService");
+ return -1;
+ }
+ method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V");
+ if (method_usbDeviceAdded == NULL) {
+ LOGE("Can't find usbDeviceAdded");
+ return -1;
+ }
+ method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V");
+ if (method_usbDeviceRemoved == NULL) {
+ LOGE("Can't find usbDeviceRemoved");
+ return -1;
+ }
+
+ clazz = env->FindClass("java/io/FileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+ gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "", "()V");
+ gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
+ LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
+ "Unable to find descriptor field in java.io.FileDescriptor");
+
+ clazz = env->FindClass("android/os/ParcelFileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
+ gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "", "(Ljava/io/FileDescriptor;)V");
+ LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
+ "Unable to find constructor for android.os.ParcelFileDescriptor");
+
+ return jniRegisterNativeMethods(env, "com/android/server/UsbService",
+ method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index bdd6d808ac424..37b520bf8d8d1 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -13,6 +13,7 @@ int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_UsbService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -40,6 +41,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
+ register_android_server_UsbService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);