1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.car.usb.handler; 18 19 import android.hardware.usb.UsbDevice; 20 import android.hardware.usb.UsbInterface; 21 import android.util.Log; 22 23 import org.xmlpull.v1.XmlPullParser; 24 25 /** 26 * This class is used to describe a USB device. When used in HashMaps all values must be specified, 27 * but wildcards can be used for any of the fields in the package meta-data. 28 */ 29 class UsbDeviceFilter { 30 private static final String TAG = UsbDeviceFilter.class.getSimpleName(); 31 32 // USB Vendor ID (or -1 for unspecified) 33 public final int mVendorId; 34 // USB Product ID (or -1 for unspecified) 35 public final int mProductId; 36 // USB device or interface class (or -1 for unspecified) 37 public final int mClass; 38 // USB device subclass (or -1 for unspecified) 39 public final int mSubclass; 40 // USB device protocol (or -1 for unspecified) 41 public final int mProtocol; 42 // USB device manufacturer name string (or null for unspecified) 43 public final String mManufacturerName; 44 // USB device product name string (or null for unspecified) 45 public final String mProductName; 46 // USB device serial number string (or null for unspecified) 47 public final String mSerialNumber; 48 49 // USB device in AOAP mode manufacturer 50 public final String mAoapManufacturer; 51 // USB device in AOAP mode model 52 public final String mAoapModel; 53 // USB device in AOAP mode description string 54 public final String mAoapDescription; 55 // USB device in AOAP mode version 56 public final String mAoapVersion; 57 // USB device in AOAP mode URI 58 public final String mAoapUri; 59 // USB device in AOAP mode serial 60 public final String mAoapSerial; 61 // USB device in AOAP mode verification service 62 public final String mAoapService; 63 UsbDeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum, String aoapManufacturer, String aoapModel, String aoapDescription, String aoapVersion, String aoapUri, String aoapSerial, String aoapService)64 UsbDeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, 65 String manufacturer, String product, String serialnum, 66 String aoapManufacturer, String aoapModel, String aoapDescription, 67 String aoapVersion, String aoapUri, String aoapSerial, 68 String aoapService) { 69 mVendorId = vid; 70 mProductId = pid; 71 mClass = clasz; 72 mSubclass = subclass; 73 mProtocol = protocol; 74 mManufacturerName = manufacturer; 75 mProductName = product; 76 mSerialNumber = serialnum; 77 78 mAoapManufacturer = aoapManufacturer; 79 mAoapModel = aoapModel; 80 mAoapDescription = aoapDescription; 81 mAoapVersion = aoapVersion; 82 mAoapUri = aoapUri; 83 mAoapSerial = aoapSerial; 84 mAoapService = aoapService; 85 } 86 read(XmlPullParser parser, boolean aoapData)87 public static UsbDeviceFilter read(XmlPullParser parser, boolean aoapData) { 88 int vendorId = -1; 89 int productId = -1; 90 int deviceClass = -1; 91 int deviceSubclass = -1; 92 int deviceProtocol = -1; 93 String manufacturerName = null; 94 String productName = null; 95 String serialNumber = null; 96 97 String aoapManufacturer = null; 98 String aoapModel = null; 99 String aoapDescription = null; 100 String aoapVersion = null; 101 String aoapUri = null; 102 String aoapSerial = null; 103 String aoapService = null; 104 105 int count = parser.getAttributeCount(); 106 for (int i = 0; i < count; i++) { 107 String name = parser.getAttributeName(i); 108 String value = parser.getAttributeValue(i); 109 // Attribute values are ints or strings 110 if (!aoapData && "manufacturer-name".equals(name)) { 111 manufacturerName = value; 112 } else if (!aoapData && "product-name".equals(name)) { 113 productName = value; 114 } else if (!aoapData && "serial-number".equals(name)) { 115 serialNumber = value; 116 } else if (aoapData && "manufacturer".equals(name)) { 117 aoapManufacturer = value; 118 } else if (aoapData && "model".equals(name)) { 119 aoapModel = value; 120 } else if (aoapData && "description".equals(name)) { 121 aoapDescription = value; 122 } else if (aoapData && "version".equals(name)) { 123 aoapVersion = value; 124 } else if (aoapData && "uri".equals(name)) { 125 aoapUri = value; 126 } else if (aoapData && "serial".equals(name)) { 127 aoapSerial = value; 128 } else if (aoapData && "service".equals(name)) { 129 aoapService = value; 130 } else if (!aoapData) { 131 int intValue = -1; 132 int radix = 10; 133 if (value != null && value.length() > 2 && value.charAt(0) == '0' 134 && (value.charAt(1) == 'x' || value.charAt(1) == 'X')) { 135 // allow hex values starting with 0x or 0X 136 radix = 16; 137 value = value.substring(2); 138 } 139 try { 140 intValue = Integer.parseInt(value, radix); 141 } catch (NumberFormatException e) { 142 Log.e(TAG, "invalid number for field " + name, e); 143 continue; 144 } 145 if ("vendor-id".equals(name)) { 146 vendorId = intValue; 147 } else if ("product-id".equals(name)) { 148 productId = intValue; 149 } else if ("class".equals(name)) { 150 deviceClass = intValue; 151 } else if ("subclass".equals(name)) { 152 deviceSubclass = intValue; 153 } else if ("protocol".equals(name)) { 154 deviceProtocol = intValue; 155 } 156 } 157 } 158 return new UsbDeviceFilter(vendorId, productId, 159 deviceClass, deviceSubclass, deviceProtocol, 160 manufacturerName, productName, serialNumber, aoapManufacturer, 161 aoapModel, aoapDescription, aoapVersion, aoapUri, aoapSerial, 162 aoapService); 163 } 164 matches(int clasz, int subclass, int protocol)165 private boolean matches(int clasz, int subclass, int protocol) { 166 return ((mClass == -1 || clasz == mClass) 167 && (mSubclass == -1 || subclass == mSubclass) 168 && (mProtocol == -1 || protocol == mProtocol)); 169 } 170 matches(UsbDevice device)171 public boolean matches(UsbDevice device) { 172 if (mVendorId != -1 && device.getVendorId() != mVendorId) { 173 return false; 174 } 175 if (mProductId != -1 && device.getProductId() != mProductId) { 176 return false; 177 } 178 if (mManufacturerName != null && device.getManufacturerName() == null) { 179 return false; 180 } 181 if (mProductName != null && device.getProductName() == null) { 182 return false; 183 } 184 if (mSerialNumber != null && device.getSerialNumber() == null) { 185 return false; 186 } 187 if (mManufacturerName != null && device.getManufacturerName() != null 188 && !mManufacturerName.equals(device.getManufacturerName())) { 189 return false; 190 } 191 if (mProductName != null && device.getProductName() != null 192 && !mProductName.equals(device.getProductName())) { 193 return false; 194 } 195 if (mSerialNumber != null && device.getSerialNumber() != null 196 && !mSerialNumber.equals(device.getSerialNumber())) { 197 return false; 198 } 199 200 // check device class/subclass/protocol 201 if (matches(device.getDeviceClass(), device.getDeviceSubclass(), 202 device.getDeviceProtocol())) { 203 return true; 204 } 205 206 // if device doesn't match, check the interfaces 207 int count = device.getInterfaceCount(); 208 for (int i = 0; i < count; i++) { 209 UsbInterface intf = device.getInterface(i); 210 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), 211 intf.getInterfaceProtocol())) { 212 return true; 213 } 214 } 215 216 return false; 217 } 218 219 @Override equals(Object obj)220 public boolean equals(Object obj) { 221 // can't compare if we have wildcard strings 222 if (mVendorId == -1 || mProductId == -1 223 || mClass == -1 || mSubclass == -1 || mProtocol == -1) { 224 return false; 225 } 226 227 return ((obj instanceof UsbDeviceFilter) && matchesUsbDeviceFilter((UsbDeviceFilter) obj)) 228 || ((obj instanceof UsbDevice) && matchesUsbDevice((UsbDevice) obj)); 229 230 } 231 matchesUsbDevice(UsbDevice device)232 private boolean matchesUsbDevice(UsbDevice device) { 233 if (device.getVendorId() != mVendorId 234 || device.getProductId() != mProductId 235 || device.getDeviceClass() != mClass 236 || device.getDeviceSubclass() != mSubclass 237 || device.getDeviceProtocol() != mProtocol) { 238 return false; 239 } 240 241 return safeEquals(mManufacturerName, device.getManufacturerName()) 242 && safeEquals(mProductName, device.getProductName()) 243 && safeEquals(mSerialNumber, device.getSerialNumber()); 244 } 245 matchesUsbDeviceFilter(UsbDeviceFilter filter)246 private boolean matchesUsbDeviceFilter(UsbDeviceFilter filter) { 247 if (filter.mVendorId != mVendorId 248 || filter.mProductId != mProductId 249 || filter.mClass != mClass 250 || filter.mSubclass != mSubclass 251 || filter.mProtocol != mProtocol) { 252 return false; 253 } 254 255 return safeEquals(mManufacturerName, filter.mManufacturerName) 256 && safeEquals(mProductName, filter.mProductName) 257 && safeEquals(mSerialNumber, filter.mSerialNumber); 258 } 259 safeEquals(String firstString, String secondString)260 private static boolean safeEquals(String firstString, String secondString) { 261 return (firstString == secondString) 262 || ((firstString != null) && firstString.equals(secondString)); 263 } 264 265 @Override hashCode()266 public int hashCode() { 267 return (((mVendorId << 16) | mProductId) 268 ^ ((mClass << 16) | (mSubclass << 8) | mProtocol)); 269 } 270 271 @Override toString()272 public String toString() { 273 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId 274 + ",mClass=" + mClass + ",mSubclass=" + mSubclass 275 + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName 276 + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + "]"; 277 } 278 } 279