diff --git a/api/current.txt b/api/current.txt index 5cf7e4832d48d..5ef43733edb8c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11826,6 +11826,20 @@ package android.hardware.usb { field public static final android.os.Parcelable.Creator CREATOR; } + public class UsbConfiguration implements android.os.Parcelable { + method public int describeContents(); + method public int getAttributes(); + method public int getId(); + method public android.hardware.usb.UsbInterface getInterface(int); + method public int getInterfaceCount(); + method public int getMaxPower(); + method public java.lang.String getName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ATTR_REMOTE_WAKEUP_MASK = 32; // 0x20 + field public static final int ATTR_SELF_POWERED_MASK = 64; // 0x40 + field public static final android.os.Parcelable.Creator CREATOR; + } + public final class UsbConstants { ctor public UsbConstants(); field public static final int USB_CLASS_APP_SPEC = 254; // 0xfe @@ -11865,6 +11879,8 @@ package android.hardware.usb { public class UsbDevice implements android.os.Parcelable { method public int describeContents(); + method public android.hardware.usb.UsbConfiguration getConfiguration(int); + method public int getConfigurationCount(); method public int getDeviceClass(); method public int getDeviceId(); method public static int getDeviceId(java.lang.String); @@ -11895,6 +11911,8 @@ package android.hardware.usb { method public java.lang.String getSerial(); method public boolean releaseInterface(android.hardware.usb.UsbInterface); method public android.hardware.usb.UsbRequest requestWait(); + method public boolean setConfiguration(android.hardware.usb.UsbConfiguration); + method public boolean setInterface(android.hardware.usb.UsbInterface); } public class UsbEndpoint implements android.os.Parcelable { @@ -11912,12 +11930,14 @@ package android.hardware.usb { public class UsbInterface implements android.os.Parcelable { method public int describeContents(); + method public int getAlternateSetting(); method public android.hardware.usb.UsbEndpoint getEndpoint(int); method public int getEndpointCount(); method public int getId(); method public int getInterfaceClass(); method public int getInterfaceProtocol(); method public int getInterfaceSubclass(); + method public java.lang.String getName(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } diff --git a/core/java/android/hardware/usb/UsbConfiguration.java b/core/java/android/hardware/usb/UsbConfiguration.java new file mode 100644 index 0000000000000..92d6f75964cec --- /dev/null +++ b/core/java/android/hardware/usb/UsbConfiguration.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014 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.usb; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class representing a configuration on a {@link UsbDevice}. + * A USB configuration can have one or more interfaces, each one providing a different + * piece of functionality, separate from the other interfaces. + * An interface will have one or more {@link UsbEndpoint}s, which are the + * channels by which the host transfers data with the device. + * + *
+ *

Developer Guides

+ *

For more information about communicating with USB hardware, read the + * USB developer guide.

+ *
+ */ +public class UsbConfiguration implements Parcelable { + + private final int mId; + private final String mName; + private final int mAttributes; + private final int mMaxPower; + private Parcelable[] mInterfaces; + + /** + * Mask for "self-powered" bit in the configuration's attributes. + * @see #getAttributes + */ + public static final int ATTR_SELF_POWERED_MASK = 1 << 6; + + /** + * Mask for "remote wakeup" bit in the configuration's attributes. + * @see #getAttributes + */ + public static final int ATTR_REMOTE_WAKEUP_MASK = 1 << 5; + + /** + * UsbConfiguration should only be instantiated by UsbService implementation + * @hide + */ + public UsbConfiguration(int id, String name, int attributes, int maxPower) { + mId = id; + mName = name; + mAttributes = attributes; + mMaxPower = maxPower; + } + + /** + * Returns the configuration's ID field. + * This is an integer that uniquely identifies the configuration on the device. + * + * @return the configuration's ID + */ + public int getId() { + return mId; + } + + /** + * Returns the configuration's name. + * + * @return the configuration's name + */ + public String getName() { + return mName; + } + + /** + * Returns the configuration's attributes field. + * This field contains a bit field with the following flags: + * + * Bit 7: always set to 1 + * Bit 6: self-powered + * Bit 5: remote wakeup enabled + * Bit 0-4: reserved + * @see #ATTR_SELF_POWERED_MASK + * @see #ATTR_REMOTE_WAKEUP_MASK + * @return the configuration's attributes + */ + public int getAttributes() { + return mAttributes; + } + + /** + * Returns the configuration's max power consumption, in milliamps. + * + * @return the configuration's max power + */ + public int getMaxPower() { + return mMaxPower * 2; + } + + /** + * Returns the number of {@link UsbInterface}s this configuration contains. + * + * @return the number of endpoints + */ + public int getInterfaceCount() { + return mInterfaces.length; + } + + /** + * Returns the {@link UsbInterface} at the given index. + * + * @return the interface + */ + public UsbInterface getInterface(int index) { + return (UsbInterface)mInterfaces[index]; + } + + /** + * Only used by UsbService implementation + * @hide + */ + public void setInterfaces(Parcelable[] interfaces) { + mInterfaces = interfaces; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("UsbConfiguration[mId=" + mId + + ",mName=" + mName + ",mAttributes=" + mAttributes + + ",mMaxPower=" + mMaxPower + ",mInterfaces=["); + for (int i = 0; i < mInterfaces.length; i++) { + builder.append("\n"); + builder.append(mInterfaces[i].toString()); + } + builder.append("]"); + return builder.toString(); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public UsbConfiguration createFromParcel(Parcel in) { + int id = in.readInt(); + String name = in.readString(); + int attributes = in.readInt(); + int maxPower = in.readInt(); + Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader()); + UsbConfiguration configuration = new UsbConfiguration(id, name, attributes, maxPower); + configuration.setInterfaces(interfaces); + return configuration; + } + + public UsbConfiguration[] newArray(int size) { + return new UsbConfiguration[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mId); + parcel.writeString(mName); + parcel.writeInt(mAttributes); + parcel.writeInt(mMaxPower); + parcel.writeParcelableArray(mInterfaces, 0); + } +} diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java index b0ba9c193ecff..d90e06e6c8a16 100644 --- a/core/java/android/hardware/usb/UsbDevice.java +++ b/core/java/android/hardware/usb/UsbDevice.java @@ -50,7 +50,10 @@ public class UsbDevice implements Parcelable { private final int mClass; private final int mSubclass; private final int mProtocol; - private final Parcelable[] mInterfaces; + private Parcelable[] mConfigurations; + + // list of all interfaces on the device + private UsbInterface[] mInterfaces; /** * UsbDevice should only be instantiated by UsbService implementation @@ -58,8 +61,7 @@ public class UsbDevice implements Parcelable { */ public UsbDevice(String name, int vendorId, int productId, int Class, int subClass, int protocol, - String manufacturerName, String productName, String serialNumber, - Parcelable[] interfaces) { + String manufacturerName, String productName, String serialNumber) { mName = name; mVendorId = vendorId; mProductId = productId; @@ -69,7 +71,6 @@ public class UsbDevice implements Parcelable { mManufacturerName = manufacturerName; mProductName = productName; mSerialNumber = serialNumber; - mInterfaces = interfaces; } /** @@ -168,22 +169,75 @@ public class UsbDevice implements Parcelable { return mProtocol; } + /** + * Returns the number of {@link UsbConfiguration}s this device contains. + * + * @return the number of configurations + */ + public int getConfigurationCount() { + return mConfigurations.length; + } + + /** + * Returns the {@link UsbConfiguration} at the given index. + * + * @return the configuration + */ + public UsbConfiguration getConfiguration(int index) { + return (UsbConfiguration)mConfigurations[index]; + } + + private UsbInterface[] getInterfaceList() { + if (mInterfaces == null) { + int configurationCount = mConfigurations.length; + int interfaceCount = 0; + for (int i = 0; i < configurationCount; i++) { + UsbConfiguration configuration = (UsbConfiguration)mConfigurations[i]; + interfaceCount += configuration.getInterfaceCount(); + } + + mInterfaces = new UsbInterface[interfaceCount]; + int offset = 0; + for (int i = 0; i < configurationCount; i++) { + UsbConfiguration configuration = (UsbConfiguration)mConfigurations[i]; + interfaceCount = configuration.getInterfaceCount(); + for (int j = 0; j < interfaceCount; j++) { + mInterfaces[offset++] = configuration.getInterface(j); + } + } + } + + return mInterfaces; + } + /** * Returns the number of {@link UsbInterface}s this device contains. + * For devices with multiple configurations, you will probably want to use + * {@link UsbConfiguration#getInterfaceCount} instead. * * @return the number of interfaces */ public int getInterfaceCount() { - return mInterfaces.length; + return getInterfaceList().length; } /** * Returns the {@link UsbInterface} at the given index. + * For devices with multiple configurations, you will probably want to use + * {@link UsbConfiguration#getInterface} instead. * * @return the interface */ public UsbInterface getInterface(int index) { - return (UsbInterface)mInterfaces[index]; + return getInterfaceList()[index]; + } + + /** + * Only used by UsbService implementation + * @hide + */ + public void setConfigurations(Parcelable[] configuration) { + mConfigurations = configuration; } @Override @@ -204,11 +258,17 @@ public class UsbDevice implements Parcelable { @Override public String toString() { - return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId + - ",mProductId=" + mProductId + ",mClass=" + mClass + - ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + + StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName + + ",mVendorId=" + mVendorId + ",mProductId=" + mProductId + + ",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName + - ",mSerialNumber=" + mSerialNumber + ",mInterfaces=" + mInterfaces + "]"; + ",mSerialNumber=" + mSerialNumber + ",mConfigurations=["); + for (int i = 0; i < mConfigurations.length; i++) { + builder.append("\n"); + builder.append(mConfigurations[i].toString()); + } + builder.append("]"); + return builder.toString(); } public static final Parcelable.Creator CREATOR = @@ -223,9 +283,11 @@ public class UsbDevice implements Parcelable { String manufacturerName = in.readString(); String productName = in.readString(); String serialNumber = in.readString(); - Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader()); - return new UsbDevice(name, vendorId, productId, clasz, subClass, protocol, - manufacturerName, productName, serialNumber, interfaces); + Parcelable[] configurations = in.readParcelableArray(UsbInterface.class.getClassLoader()); + UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol, + manufacturerName, productName, serialNumber); + device.setConfigurations(configurations); + return device; } public UsbDevice[] newArray(int size) { @@ -247,7 +309,7 @@ public class UsbDevice implements Parcelable { parcel.writeString(mManufacturerName); parcel.writeString(mProductName); parcel.writeString(mSerialNumber); - parcel.writeParcelableArray(mInterfaces, 0); + parcel.writeParcelableArray(mConfigurations, 0); } public static int getDeviceId(String name) { diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 389475fca6c91..628395159fc07 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -100,6 +100,25 @@ public class UsbDeviceConnection { return native_release_interface(intf.getId()); } + /** + * Sets the current {@link android.hardware.usb.UsbInterface}. + * Used to select between two interfaces with the same ID but different alternate setting. + * + * @return true if the interface was successfully released + */ + public boolean setInterface(UsbInterface intf) { + return native_set_interface(intf.getId(), intf.getAlternateSetting()); + } + + /** + * Sets the device's current {@link android.hardware.usb.UsbConfiguration}. + * + * @return true if the configuration was successfully set + */ + public boolean setConfiguration(UsbConfiguration configuration) { + return native_set_configuration(configuration.getId()); + } + /** * Performs a control transaction on endpoint zero for this device. * The direction of the transfer is determined by the request type. @@ -236,6 +255,8 @@ public class UsbDeviceConnection { private native byte[] native_get_desc(); private native boolean native_claim_interface(int interfaceID, boolean force); private native boolean native_release_interface(int interfaceID); + private native boolean native_set_interface(int interfaceID, int alternateSetting); + private native boolean native_set_configuration(int configurationID); private native int native_control_request(int requestType, int request, int value, int index, byte[] buffer, int offset, int length, int timeout); private native int native_bulk_request(int endpoint, byte[] buffer, diff --git a/core/java/android/hardware/usb/UsbInterface.java b/core/java/android/hardware/usb/UsbInterface.java index e94baa16dcf3d..de01a885d8c0b 100644 --- a/core/java/android/hardware/usb/UsbInterface.java +++ b/core/java/android/hardware/usb/UsbInterface.java @@ -35,27 +35,31 @@ import android.os.Parcelable; public class UsbInterface implements Parcelable { private final int mId; + private final int mAlternateSetting; + private final String mName; private final int mClass; private final int mSubclass; private final int mProtocol; - private final Parcelable[] mEndpoints; + private Parcelable[] mEndpoints; /** * UsbInterface should only be instantiated by UsbService implementation * @hide */ - public UsbInterface(int id, int Class, int subClass, int protocol, - Parcelable[] endpoints) { + public UsbInterface(int id, int alternateSetting, String name, + int Class, int subClass, int protocol) { mId = id; + mAlternateSetting = alternateSetting; + mName = name; mClass = Class; mSubclass = subClass; mProtocol = protocol; - mEndpoints = endpoints; } /** - * Returns the interface's ID field. - * This is an integer that uniquely identifies the interface on the device. + * Returns the interface's bInterfaceNumber field. + * This is an integer that along with the alternate setting uniquely identifies + * the interface on the device. * * @return the interface's ID */ @@ -63,6 +67,28 @@ public class UsbInterface implements Parcelable { return mId; } + /** + * Returns the interface's bAlternateSetting field. + * This is an integer that along with the ID uniquely identifies + * the interface on the device. + * {@link UsbDeviceConnection#setInterface} can be used to switch between + * two interfaces with the same ID but different alternate setting. + * + * @return the interface's alternate setting + */ + public int getAlternateSetting() { + return mAlternateSetting; + } + + /** + * Returns the interface's name. + * + * @return the interface's name + */ + public String getName() { + return mName; + } + /** * Returns the interface's class field. * Some useful constants for USB classes can be found in {@link UsbConstants} @@ -109,22 +135,42 @@ public class UsbInterface implements Parcelable { return (UsbEndpoint)mEndpoints[index]; } + /** + * Only used by UsbService implementation + * @hide + */ + public void setEndpoints(Parcelable[] endpoints) { + mEndpoints = endpoints; + } + @Override public String toString() { - return "UsbInterface[mId=" + mId + ",mClass=" + mClass + + StringBuilder builder = new StringBuilder("UsbInterface[mId=" + mId + + ",mAlternateSetting=" + mAlternateSetting + + ",mName=" + mName + ",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + - ",mEndpoints=" + mEndpoints + "]"; + ",mEndpoints=["); + for (int i = 0; i < mEndpoints.length; i++) { + builder.append("\n"); + builder.append(mEndpoints[i].toString()); + } + builder.append("]"); + return builder.toString(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public UsbInterface createFromParcel(Parcel in) { int id = in.readInt(); + int alternateSetting = in.readInt(); + String name = in.readString(); int Class = in.readInt(); int subClass = in.readInt(); int protocol = in.readInt(); Parcelable[] endpoints = in.readParcelableArray(UsbEndpoint.class.getClassLoader()); - return new UsbInterface(id, Class, subClass, protocol, endpoints); + UsbInterface intf = new UsbInterface(id, alternateSetting, name, Class, subClass, protocol); + intf.setEndpoints(endpoints); + return intf; } public UsbInterface[] newArray(int size) { @@ -138,6 +184,8 @@ public class UsbInterface implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mId); + parcel.writeInt(mAlternateSetting); + parcel.writeString(mName); parcel.writeInt(mClass); parcel.writeInt(mSubclass); parcel.writeInt(mProtocol); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index bf62745f254fb..05c57e8997cfa 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -244,9 +244,11 @@ public class ZygoteInit { } static void preload() { + Log.d(TAG, "begin preload"); preloadClasses(); preloadResources(); preloadOpenGL(); + Log.d(TAG, "end preload"); } private static void preloadOpenGL() { diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp index c10b963f578f9..467a9a124fc4d 100644 --- a/core/jni/android_hardware_UsbDeviceConnection.cpp +++ b/core/jni/android_hardware_UsbDeviceConnection.cpp @@ -123,20 +123,45 @@ android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz, return (ret == 0) ? JNI_TRUE : JNI_FALSE; } -static jint +static jboolean android_hardware_UsbDeviceConnection_release_interface(JNIEnv *env, jobject thiz, jint interfaceID) { struct usb_device* device = get_device_from_object(env, thiz); if (!device) { ALOGE("device is closed in native_release_interface"); - return -1; + return JNI_FALSE; } 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; + return (ret == 0) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean +android_hardware_UsbDeviceConnection_set_interface(JNIEnv *env, jobject thiz, jint interfaceID, + jint alternateSetting) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + ALOGE("device is closed in native_set_interface"); + return JNI_FALSE; + } + int ret = usb_device_set_interface(device, interfaceID, alternateSetting); + return (ret == 0) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean +android_hardware_UsbDeviceConnection_set_configuration(JNIEnv *env, jobject thiz, jint configurationID) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + ALOGE("device is closed in native_set_configuration"); + return JNI_FALSE; + } + int ret = usb_device_set_configuration(device, configurationID); + return (ret == 0) ? JNI_TRUE : JNI_FALSE; } static jint @@ -229,6 +254,8 @@ static JNINativeMethod method_table[] = { {"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc}, {"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface}, {"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface}, + {"native_set_interface","(II)Z", (void *)android_hardware_UsbDeviceConnection_set_interface}, + {"native_set_configuration","(I)Z", (void *)android_hardware_UsbDeviceConnection_set_configuration}, {"native_control_request", "(IIII[BIII)I", (void *)android_hardware_UsbDeviceConnection_control_request}, {"native_bulk_request", "(I[BIII)I", diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp index fc6de60dbf5ec..bc866d3ee13b0 100644 --- a/services/core/jni/com_android_server_UsbHostManager.cpp +++ b/services/core/jni/com_android_server_UsbHostManager.cpp @@ -41,7 +41,11 @@ static struct parcel_file_descriptor_offsets_t jmethodID mConstructor; } gParcelFileDescriptorOffsets; -static jmethodID method_usbDeviceAdded; +static jmethodID method_beginUsbDeviceAdded; +static jmethodID method_addUsbConfiguration; +static jmethodID method_addUsbInterface; +static jmethodID method_addUsbEndpoint; +static jmethodID method_endUsbDeviceAdded; static jmethodID method_usbDeviceRemoved; static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { @@ -68,64 +72,69 @@ static int usb_device_added(const char *devname, void* client_data) { 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; char *manufacturer = usb_device_get_manufacturer_name(device); char *product = usb_device_get_product_name(device); char *serial = usb_device_get_serial(device); - 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()); - jstring deviceName = env->NewStringUTF(devname); jstring manufacturerName = env->NewStringUTF(manufacturer); jstring productName = env->NewStringUTF(product); jstring serialNumber = env->NewStringUTF(serial); - env->CallVoidMethod(thiz, method_usbDeviceAdded, - deviceName, vendorId, productId, deviceClass, - deviceSubClass, protocol, manufacturerName, - productName, serialNumber, interfaceArray, endpointArray); - env->DeleteLocalRef(interfaceArray); - env->DeleteLocalRef(endpointArray); + jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded, + deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device), + deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol, + manufacturerName, productName, serialNumber); + env->DeleteLocalRef(serialNumber); env->DeleteLocalRef(productName); env->DeleteLocalRef(manufacturerName); env->DeleteLocalRef(deviceName); + free(manufacturer); + free(product); + free(serial); + + if (!result) goto fail; + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_CONFIG) { + struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc; + char *name = usb_device_get_string(device, config->iConfiguration); + jstring configName = env->NewStringUTF(name); + + env->CallVoidMethod(thiz, method_addUsbConfiguration, + config->bConfigurationValue, configName, config->bmAttributes, + config->bMaxPower); + + env->DeleteLocalRef(configName); + free(name); + } else if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + char *name = usb_device_get_string(device, interface->iInterface); + jstring interfaceName = env->NewStringUTF(name); + + env->CallVoidMethod(thiz, method_addUsbInterface, + interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting, + interface->bInterfaceClass, interface->bInterfaceSubClass, + interface->bInterfaceProtocol); + + env->DeleteLocalRef(interfaceName); + free(name); + } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { + struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc; + + env->CallVoidMethod(thiz, method_addUsbEndpoint, + endpoint->bEndpointAddress, endpoint->bmAttributes, + __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval); + } + } + + env->CallVoidMethod(thiz, method_endUsbDeviceAdded); + +fail: + usb_device_close(device); checkAndClearExceptionFromCallback(env, __FUNCTION__); return 0; @@ -191,12 +200,36 @@ int register_android_server_UsbHostManager(JNIEnv *env) ALOGE("Can't find com/android/server/usb/UsbHostManager"); return -1; } - method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I)V"); - if (method_usbDeviceAdded == NULL) { - ALOGE("Can't find usbDeviceAdded"); + method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded", + "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); + if (method_beginUsbDeviceAdded == NULL) { + ALOGE("Can't find beginUsbDeviceAdded"); return -1; } - method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V"); + method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration", + "(ILjava/lang/String;II)V"); + if (method_addUsbConfiguration == NULL) { + ALOGE("Can't find addUsbConfiguration"); + return -1; + } + method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface", + "(ILjava/lang/String;IIII)V"); + if (method_addUsbInterface == NULL) { + ALOGE("Can't find addUsbInterface"); + return -1; + } + method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V"); + if (method_addUsbEndpoint == NULL) { + ALOGE("Can't find addUsbEndpoint"); + return -1; + } + method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V"); + if (method_endUsbDeviceAdded == NULL) { + ALOGE("Can't find endUsbDeviceAdded"); + return -1; + } + method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", + "(Ljava/lang/String;)V"); if (method_usbDeviceRemoved == NULL) { ALOGE("Can't find usbDeviceRemoved"); return -1; @@ -205,7 +238,8 @@ int register_android_server_UsbHostManager(JNIEnv *env) 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"); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "", + "(Ljava/io/FileDescriptor;)V"); LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, "Unable to find constructor for android.os.ParcelFileDescriptor"); diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index dfaad0bcece93..7ae54606cb000 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -17,6 +17,7 @@ package com.android.server.usb; import android.content.Context; +import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbEndpoint; @@ -30,6 +31,7 @@ import com.android.internal.annotations.GuardedBy; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; /** @@ -48,6 +50,13 @@ public class UsbHostManager { private final Context mContext; private final Object mLock = new Object(); + private UsbDevice mNewDevice; + private UsbConfiguration mNewConfiguration; + private UsbInterface mNewInterface; + private ArrayList mNewConfigurations; + private ArrayList mNewInterfaces; + private ArrayList mNewEndpoints; + @GuardedBy("mLock") private UsbSettingsManager mCurrentSettings; @@ -93,69 +102,103 @@ public class UsbHostManager { return false; } - /* Called from JNI in monitorUsbHostBus() to report new USB devices */ - private void usbDeviceAdded(String deviceName, int vendorID, int productID, + /* Called from JNI in monitorUsbHostBus() to report new USB devices + Returns true if successful, in which case the JNI code will continue adding configurations, + interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors + have been processed + */ + private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID, int deviceClass, int deviceSubclass, int deviceProtocol, - String manufacturerName, String productName, String serialNumber, - /* 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) { + String manufacturerName, String productName, String serialNumber) { if (isBlackListed(deviceName) || isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) { - return; + return false; } synchronized (mLock) { if (mDevices.get(deviceName) != null) { Slog.w(TAG, "device already on mDevices list: " + deviceName); - return; + return false; } - 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; - 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); - } - - // don't allow if any interfaces are blacklisted - if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) { - return; - } - interfaces[intf] = new UsbInterface(interfaceId, interfaceClass, - interfaceSubclass, interfaceProtocol, endpoints); - } - } catch (Exception e) { - // beware of index out of bound exceptions, which might happen if - // a device does not set bNumEndpoints correctly - Slog.e(TAG, "error parsing USB descriptors", e); - return; + if (mNewDevice != null) { + Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded"); + return false; } - UsbDevice device = new UsbDevice(deviceName, vendorID, productID, + mNewDevice = new UsbDevice(deviceName, vendorID, productID, deviceClass, deviceSubclass, deviceProtocol, - manufacturerName, productName, serialNumber, interfaces); - mDevices.put(deviceName, device); - getCurrentSettings().deviceAttached(device); + manufacturerName, productName, serialNumber); + + mNewConfigurations = new ArrayList(); + mNewInterfaces = new ArrayList(); + mNewEndpoints = new ArrayList(); + } + return true; + } + + /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device + currently being added. Returns true if successful, false in case of error. + */ + private void addUsbConfiguration(int id, String name, int attributes, int maxPower) { + if (mNewConfiguration != null) { + mNewConfiguration.setInterfaces( + mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()])); + mNewInterfaces.clear(); + } + + mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower); + mNewConfigurations.add(mNewConfiguration); + } + + /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device + currently being added. Returns true if successful, false in case of error. + */ + private void addUsbInterface(int id, String name, int altSetting, + int Class, int subClass, int protocol) { + if (mNewInterface != null) { + mNewInterface.setEndpoints( + mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()])); + mNewEndpoints.clear(); + } + + mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol); + mNewInterfaces.add(mNewInterface); + } + + /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device + currently being added. Returns true if successful, false in case of error. + */ + private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) { + mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval)); + } + + /* Called from JNI in monitorUsbHostBus() to finish adding a new device */ + private void endUsbDeviceAdded() { + if (mNewInterface != null) { + mNewInterface.setEndpoints( + mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()])); + } + if (mNewConfiguration != null) { + mNewConfiguration.setInterfaces( + mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()])); + } + + synchronized (mLock) { + if (mNewDevice != null) { + mNewDevice.setConfigurations( + mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()])); + mDevices.put(mNewDevice.getDeviceName(), mNewDevice); + Slog.d(TAG, "Added device " + mNewDevice); + getCurrentSettings().deviceAttached(mNewDevice); + } else { + Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded"); + } + mNewDevice = null; + mNewConfigurations = null; + mNewInterfaces = null; + mNewEndpoints = null; } }