Merge "Added missing USB device descriptor fields needed for intent filters"
diff --git a/api/current.txt b/api/current.txt
index 8e69592..2bd9c05 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11027,7 +11027,10 @@
     method public int getDeviceSubclass();
     method public android.hardware.usb.UsbInterface getInterface(int);
     method public int getInterfaceCount();
+    method public java.lang.String getManufacturerName();
     method public int getProductId();
+    method public java.lang.String getProductName();
+    method public java.lang.String getSerialNumber();
     method public int getVendorId();
     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/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 9bd38f9..d1e63f6 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -46,6 +46,9 @@
     private static final String TAG = "UsbDevice";
 
     private final String mName;
+    private final String mManufacturerName;
+    private final String mProductName;
+    private final String mSerialNumber;
     private final int mVendorId;
     private final int mProductId;
     private final int mClass;
@@ -58,13 +61,18 @@
      * @hide
      */
     public UsbDevice(String name, int vendorId, int productId,
-            int Class, int subClass, int protocol, Parcelable[] interfaces) {
+            int Class, int subClass, int protocol,
+            String manufacturerName, String productName, String serialNumber,
+            Parcelable[] interfaces) {
         mName = name;
         mVendorId = vendorId;
         mProductId = productId;
         mClass = Class;
         mSubclass = subClass;
         mProtocol = protocol;
+        mManufacturerName = manufacturerName;
+        mProductName = productName;
+        mSerialNumber = serialNumber;
         mInterfaces = interfaces;
     }
 
@@ -80,6 +88,33 @@
     }
 
     /**
+     * Returns the manufacturer name of the device.
+     *
+     * @return the manufacturer name
+     */
+    public String getManufacturerName() {
+        return mManufacturerName;
+    }
+
+    /**
+     * Returns the product name of the device.
+     *
+     * @return the product name
+     */
+    public String getProductName() {
+        return mProductName;
+    }
+
+    /**
+     * Returns the serial number of the device.
+     *
+     * @return the serial number name
+     */
+    public String getSerialNumber() {
+        return mSerialNumber;
+    }
+
+    /**
      * Returns a unique integer ID for the device.
      * This is a convenience for clients that want to use an integer to represent
      * the device, rather than the device name.
@@ -176,7 +211,8 @@
         return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId +
                 ",mProductId=" + mProductId + ",mClass=" + mClass +
                 ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
-                ",mInterfaces=" + mInterfaces + "]";
+                ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName +
+                ",mSerialNumber=" + mSerialNumber + ",mInterfaces=" + mInterfaces + "]";
     }
 
     public static final Parcelable.Creator<UsbDevice> CREATOR =
@@ -188,8 +224,12 @@
             int clasz = in.readInt();
             int subClass = in.readInt();
             int protocol = in.readInt();
+            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, interfaces);
+            return new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
+                                 manufacturerName, productName, serialNumber, interfaces);
         }
 
         public UsbDevice[] newArray(int size) {
@@ -208,6 +248,9 @@
         parcel.writeInt(mClass);
         parcel.writeInt(mSubclass);
         parcel.writeInt(mProtocol);
+        parcel.writeString(mManufacturerName);
+        parcel.writeString(mProductName);
+        parcel.writeString(mSerialNumber);
         parcel.writeParcelableArray(mInterfaces, 0);
    }
 
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
index 10272f2..dfaad0b 100644
--- a/services/java/com/android/server/usb/UsbHostManager.java
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -96,6 +96,7 @@
     /* Called from JNI in monitorUsbHostBus() to report new USB devices */
     private void usbDeviceAdded(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,
@@ -151,7 +152,8 @@
             }
 
             UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
-                    deviceClass, deviceSubclass, deviceProtocol, interfaces);
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, serialNumber, interfaces);
             mDevices.put(deviceName, device);
             getCurrentSettings().deviceAttached(device);
         }
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 9b5b312..ff4857b 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -108,13 +108,23 @@
         public final int mSubclass;
         // USB device protocol (or -1 for unspecified)
         public final int mProtocol;
+        // USB device manufacturer name string (or null for unspecified)
+        public final String mManufacturerName;
+        // USB device product name string (or null for unspecified)
+        public final String mProductName;
+        // USB device serial number string (or null for unspecified)
+        public final String mSerialNumber;
 
-        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
+        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+                            String manufacturer, String product, String serialnum) {
             mVendorId = vid;
             mProductId = pid;
             mClass = clasz;
             mSubclass = subclass;
             mProtocol = protocol;
+            mManufacturerName = manufacturer;
+            mProductName = product;
+            mSerialNumber = serialnum;
         }
 
         public DeviceFilter(UsbDevice device) {
@@ -123,6 +133,9 @@
             mClass = device.getDeviceClass();
             mSubclass = device.getDeviceSubclass();
             mProtocol = device.getDeviceProtocol();
+            mManufacturerName = device.getManufacturerName();
+            mProductName = device.getProductName();
+            mSerialNumber = device.getSerialNumber();
         }
 
         public static DeviceFilter read(XmlPullParser parser)
@@ -132,27 +145,52 @@
             int deviceClass = -1;
             int deviceSubclass = -1;
             int deviceProtocol = -1;
+            String manufacturerName = null;
+            String productName = null;
+            String serialNumber = null;
 
             int count = parser.getAttributeCount();
             for (int i = 0; i < count; i++) {
                 String name = parser.getAttributeName(i);
-                // All attribute values are ints
-                int value = Integer.parseInt(parser.getAttributeValue(i));
-
-                if ("vendor-id".equals(name)) {
-                    vendorId = value;
-                } else if ("product-id".equals(name)) {
-                    productId = value;
-                } else if ("class".equals(name)) {
-                    deviceClass = value;
-                } else if ("subclass".equals(name)) {
-                    deviceSubclass = value;
-                } else if ("protocol".equals(name)) {
-                    deviceProtocol = value;
+                String value = parser.getAttributeValue(i);
+                // Attribute values are ints or strings
+                if ("manufacturer-name".equals(name)) {
+                    manufacturerName = value;
+                } else if ("product-name".equals(name)) {
+                    productName = value;
+                } else if ("serial-number".equals(name)) {
+                    serialNumber = value;
+                } else {
+                    int intValue = -1;
+                    int radix = 10;
+                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+                        // allow hex values starting with 0x or 0X
+                        radix = 16;
+                        value = value.substring(2);
+                    }
+                    try {
+                        intValue = Integer.parseInt(value, radix);
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG, "invalid number for field " + name, e);
+                        continue;
+                    }
+                    if ("vendor-id".equals(name)) {
+                        vendorId = intValue;
+                    } else if ("product-id".equals(name)) {
+                        productId = intValue;
+                    } else if ("class".equals(name)) {
+                        deviceClass = intValue;
+                    } else if ("subclass".equals(name)) {
+                        deviceSubclass = intValue;
+                    } else if ("protocol".equals(name)) {
+                        deviceProtocol = intValue;
+                    }
                 }
             }
             return new DeviceFilter(vendorId, productId,
-                    deviceClass, deviceSubclass, deviceProtocol);
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, serialNumber);
         }
 
         public void write(XmlSerializer serializer) throws IOException {
@@ -172,6 +210,15 @@
             if (mProtocol != -1) {
                 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
             }
+            if (mManufacturerName != null) {
+                serializer.attribute(null, "manufacturer-name", mManufacturerName);
+            }
+            if (mProductName != null) {
+                serializer.attribute(null, "product-name", mProductName);
+            }
+            if (mSerialNumber != null) {
+                serializer.attribute(null, "serial-number", mSerialNumber);
+            }
             serializer.endTag(null, "usb-device");
         }
 
@@ -184,6 +231,15 @@
         public boolean matches(UsbDevice device) {
             if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
             if (mProductId != -1 && device.getProductId() != mProductId) return false;
+            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+            if (mProductName != null && device.getProductName() == null) return false;
+            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+            if (mManufacturerName != null && device.getManufacturerName() != null &&
+                !mManufacturerName.equals(device.getManufacturerName())) return false;
+            if (mProductName != null && device.getProductName() != null &&
+                !mProductName.equals(device.getProductName())) return false;
+            if (mSerialNumber != null && device.getSerialNumber() != null &&
+                !mSerialNumber.equals(device.getSerialNumber())) return false;
 
             // check device class/subclass/protocol
             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
@@ -203,6 +259,15 @@
         public boolean matches(DeviceFilter f) {
             if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
             if (mProductId != -1 && f.mProductId != mProductId) return false;
+            if (f.mManufacturerName != null && mManufacturerName == null) return false;
+            if (f.mProductName != null && mProductName == null) return false;
+            if (f.mSerialNumber != null && mSerialNumber == null) return false;
+            if (mManufacturerName != null && f.mManufacturerName != null &&
+                !mManufacturerName.equals(f.mManufacturerName)) return false;
+            if (mProductName != null && f.mProductName != null &&
+                !mProductName.equals(f.mProductName)) return false;
+            if (mSerialNumber != null && f.mSerialNumber != null &&
+                !mSerialNumber.equals(f.mSerialNumber)) return false;
 
             // check device class/subclass/protocol
             return matches(f.mClass, f.mSubclass, f.mProtocol);
@@ -217,19 +282,67 @@
             }
             if (obj instanceof DeviceFilter) {
                 DeviceFilter filter = (DeviceFilter)obj;
-                return (filter.mVendorId == mVendorId &&
-                        filter.mProductId == mProductId &&
-                        filter.mClass == mClass &&
-                        filter.mSubclass == mSubclass &&
-                        filter.mProtocol == mProtocol);
+
+                if (filter.mVendorId != mVendorId ||
+                        filter.mProductId != mProductId ||
+                        filter.mClass != mClass ||
+                        filter.mSubclass != mSubclass ||
+                        filter.mProtocol != mProtocol) {
+                    return(false);
+                }
+                if ((filter.mManufacturerName != null &&
+                        mManufacturerName == null) ||
+                    (filter.mManufacturerName == null &&
+                        mManufacturerName != null) ||
+                    (filter.mProductName != null &&
+                        mProductName == null)  ||
+                    (filter.mProductName == null &&
+                        mProductName != null) ||
+                    (filter.mSerialNumber != null &&
+                        mSerialNumber == null)  ||
+                    (filter.mSerialNumber == null &&
+                        mSerialNumber != null)) {
+                    return(false);
+                }
+                if  ((filter.mManufacturerName != null &&
+                        mManufacturerName != null &&
+                        !mManufacturerName.equals(filter.mManufacturerName)) ||
+                     (filter.mProductName != null &&
+                        mProductName != null &&
+                        !mProductName.equals(filter.mProductName)) ||
+                     (filter.mSerialNumber != null &&
+                        mSerialNumber != null &&
+                        !mSerialNumber.equals(filter.mSerialNumber))) {
+                    return(false);
+                }
+                return(true);
             }
             if (obj instanceof UsbDevice) {
                 UsbDevice device = (UsbDevice)obj;
-                return (device.getVendorId() == mVendorId &&
-                        device.getProductId() == mProductId &&
-                        device.getDeviceClass() == mClass &&
-                        device.getDeviceSubclass() == mSubclass &&
-                        device.getDeviceProtocol() == mProtocol);
+                if (device.getVendorId() != mVendorId ||
+                        device.getProductId() != mProductId ||
+                        device.getDeviceClass() != mClass ||
+                        device.getDeviceSubclass() != mSubclass ||
+                        device.getDeviceProtocol() != mProtocol) {
+                    return(false);
+                }
+                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+                        (mManufacturerName == null && device.getManufacturerName() != null) ||
+                        (mProductName != null && device.getProductName() == null) ||
+                        (mProductName == null && device.getProductName() != null) ||
+                        (mSerialNumber != null && device.getSerialNumber() == null) ||
+                        (mSerialNumber == null && device.getSerialNumber() != null)) {
+                    return(false);
+                }
+                if ((device.getManufacturerName() != null &&
+                        !mManufacturerName.equals(device.getManufacturerName())) ||
+                        (device.getProductName() != null &&
+                            !mProductName.equals(device.getProductName())) ||
+                        (device.getSerialNumber() != null &&
+                            !mSerialNumber.equals(device.getSerialNumber()))) {
+                    return(false);
+                }
+                return true;
             }
             return false;
         }
@@ -244,7 +357,9 @@
         public String toString() {
             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
                     ",mClass=" + mClass + ",mSubclass=" + mSubclass +
-                    ",mProtocol=" + mProtocol + "]";
+                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+                    "]";
         }
     }
 
diff --git a/services/jni/com_android_server_UsbHostManager.cpp b/services/jni/com_android_server_UsbHostManager.cpp
index 639790b..f1fa6cf 100644
--- a/services/jni/com_android_server_UsbHostManager.cpp
+++ b/services/jni/com_android_server_UsbHostManager.cpp
@@ -73,6 +73,9 @@
     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);
 
@@ -109,12 +112,19 @@
     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, interfaceArray, endpointArray);
+            deviceSubClass, protocol, manufacturerName,
+            productName, serialNumber, interfaceArray, endpointArray);
 
     env->DeleteLocalRef(interfaceArray);
     env->DeleteLocalRef(endpointArray);
+    env->DeleteLocalRef(serialNumber);
+    env->DeleteLocalRef(productName);
+    env->DeleteLocalRef(manufacturerName);
     env->DeleteLocalRef(deviceName);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 
@@ -179,7 +189,7 @@
         ALOGE("Can't find com/android/server/usb/UsbHostManager");
         return -1;
     }
-    method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V");
+    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");
         return -1;