1 /* 2 * Copyright 2017 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.hardware.usb; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.hardware.usb.flags.Flags; 22 import android.service.usb.UsbDeviceFilterProto; 23 import android.util.Slog; 24 25 import com.android.internal.util.dump.DualDumpOutputStream; 26 27 import org.xmlpull.v1.XmlPullParser; 28 import org.xmlpull.v1.XmlPullParserException; 29 import org.xmlpull.v1.XmlSerializer; 30 31 import java.io.IOException; 32 import java.util.Objects; 33 34 /** 35 * This class is used to describe a USB device. 36 * When used in HashMaps all values must be specified, 37 * but wildcards can be used for any of the fields in 38 * the package meta-data. 39 * 40 * @hide 41 */ 42 public class DeviceFilter { 43 private static final String TAG = DeviceFilter.class.getSimpleName(); 44 45 // USB Vendor ID (or -1 for unspecified) 46 public final int mVendorId; 47 // USB Product ID (or -1 for unspecified) 48 public final int mProductId; 49 // USB device or interface class (or -1 for unspecified) 50 public final int mClass; 51 // USB device subclass (or -1 for unspecified) 52 public final int mSubclass; 53 // USB device protocol (or -1 for unspecified) 54 public final int mProtocol; 55 // USB device manufacturer name string (or null for unspecified) 56 public final String mManufacturerName; 57 // USB device product name string (or null for unspecified) 58 public final String mProductName; 59 // USB device serial number string (or null for unspecified) 60 public final String mSerialNumber; 61 // USB interface name (or null for unspecified). This will be used when matching devices using 62 // the available interfaces. 63 public final String mInterfaceName; 64 DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum, String interfaceName)65 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, 66 String manufacturer, String product, String serialnum, String interfaceName) { 67 mVendorId = vid; 68 mProductId = pid; 69 mClass = clasz; 70 mSubclass = subclass; 71 mProtocol = protocol; 72 mManufacturerName = manufacturer; 73 mProductName = product; 74 mSerialNumber = serialnum; 75 mInterfaceName = interfaceName; 76 } 77 DeviceFilter(UsbDevice device)78 public DeviceFilter(UsbDevice device) { 79 mVendorId = device.getVendorId(); 80 mProductId = device.getProductId(); 81 mClass = device.getDeviceClass(); 82 mSubclass = device.getDeviceSubclass(); 83 mProtocol = device.getDeviceProtocol(); 84 mManufacturerName = device.getManufacturerName(); 85 mProductName = device.getProductName(); 86 mSerialNumber = device.getSerialNumber(); 87 mInterfaceName = null; 88 } 89 DeviceFilter(@onNull DeviceFilter filter)90 public DeviceFilter(@NonNull DeviceFilter filter) { 91 mVendorId = filter.mVendorId; 92 mProductId = filter.mProductId; 93 mClass = filter.mClass; 94 mSubclass = filter.mSubclass; 95 mProtocol = filter.mProtocol; 96 mManufacturerName = filter.mManufacturerName; 97 mProductName = filter.mProductName; 98 mSerialNumber = filter.mSerialNumber; 99 mInterfaceName = filter.mInterfaceName; 100 } 101 read(XmlPullParser parser)102 public static DeviceFilter read(XmlPullParser parser) 103 throws XmlPullParserException, IOException { 104 int vendorId = -1; 105 int productId = -1; 106 int deviceClass = -1; 107 int deviceSubclass = -1; 108 int deviceProtocol = -1; 109 String manufacturerName = null; 110 String productName = null; 111 String serialNumber = null; 112 String interfaceName = null; 113 int count = parser.getAttributeCount(); 114 for (int i = 0; i < count; i++) { 115 String name = parser.getAttributeName(i); 116 String value = parser.getAttributeValue(i); 117 // Attribute values are ints or strings 118 if ("manufacturer-name".equals(name)) { 119 manufacturerName = value; 120 } else if ("product-name".equals(name)) { 121 productName = value; 122 } else if ("serial-number".equals(name)) { 123 serialNumber = value; 124 } else if ("interface-name".equals(name)) { 125 interfaceName = value; 126 } else { 127 int intValue; 128 int radix = 10; 129 if (value != null && value.length() > 2 && value.charAt(0) == '0' && 130 (value.charAt(1) == 'x' || value.charAt(1) == 'X')) { 131 // allow hex values starting with 0x or 0X 132 radix = 16; 133 value = value.substring(2); 134 } 135 try { 136 intValue = Integer.parseInt(value, radix); 137 } catch (NumberFormatException e) { 138 Slog.e(TAG, "invalid number for field " + name, e); 139 continue; 140 } 141 if ("vendor-id".equals(name)) { 142 vendorId = intValue; 143 } else if ("product-id".equals(name)) { 144 productId = intValue; 145 } else if ("class".equals(name)) { 146 deviceClass = intValue; 147 } else if ("subclass".equals(name)) { 148 deviceSubclass = intValue; 149 } else if ("protocol".equals(name)) { 150 deviceProtocol = intValue; 151 } 152 } 153 } 154 return new DeviceFilter(vendorId, productId, 155 deviceClass, deviceSubclass, deviceProtocol, 156 manufacturerName, productName, serialNumber, interfaceName); 157 } 158 write(XmlSerializer serializer)159 public void write(XmlSerializer serializer) throws IOException { 160 serializer.startTag(null, "usb-device"); 161 if (mVendorId != -1) { 162 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); 163 } 164 if (mProductId != -1) { 165 serializer.attribute(null, "product-id", Integer.toString(mProductId)); 166 } 167 if (mClass != -1) { 168 serializer.attribute(null, "class", Integer.toString(mClass)); 169 } 170 if (mSubclass != -1) { 171 serializer.attribute(null, "subclass", Integer.toString(mSubclass)); 172 } 173 if (mProtocol != -1) { 174 serializer.attribute(null, "protocol", Integer.toString(mProtocol)); 175 } 176 if (mManufacturerName != null) { 177 serializer.attribute(null, "manufacturer-name", mManufacturerName); 178 } 179 if (mProductName != null) { 180 serializer.attribute(null, "product-name", mProductName); 181 } 182 if (mSerialNumber != null) { 183 serializer.attribute(null, "serial-number", mSerialNumber); 184 } 185 if (mInterfaceName != null) { 186 serializer.attribute(null, "interface-name", mInterfaceName); 187 } 188 serializer.endTag(null, "usb-device"); 189 } 190 matches(int usbClass, int subclass, int protocol)191 private boolean matches(int usbClass, int subclass, int protocol) { 192 return ((mClass == -1 || usbClass == mClass) 193 && (mSubclass == -1 || subclass == mSubclass) 194 && (mProtocol == -1 || protocol == mProtocol)); 195 } 196 matches(int usbClass, int subclass, int protocol, String interfaceName)197 private boolean matches(int usbClass, int subclass, int protocol, String interfaceName) { 198 if (Flags.enableInterfaceNameDeviceFilter()) { 199 return matches(usbClass, subclass, protocol) 200 && (mInterfaceName == null || mInterfaceName.equals(interfaceName)); 201 } else { 202 return matches(usbClass, subclass, protocol); 203 } 204 } 205 matches(UsbDevice device)206 public boolean matches(UsbDevice device) { 207 if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; 208 if (mProductId != -1 && device.getProductId() != mProductId) return false; 209 if (mManufacturerName != null && device.getManufacturerName() == null) return false; 210 if (mProductName != null && device.getProductName() == null) return false; 211 if (mSerialNumber != null && device.getSerialNumber() == null) return false; 212 if (mManufacturerName != null && device.getManufacturerName() != null && 213 !mManufacturerName.equals(device.getManufacturerName())) return false; 214 if (mProductName != null && device.getProductName() != null && 215 !mProductName.equals(device.getProductName())) return false; 216 if (mSerialNumber != null && device.getSerialNumber() != null && 217 !mSerialNumber.equals(device.getSerialNumber())) return false; 218 219 // check device class/subclass/protocol 220 if (matches(device.getDeviceClass(), device.getDeviceSubclass(), 221 device.getDeviceProtocol())) return true; 222 223 // if device doesn't match, check the interfaces 224 int count = device.getInterfaceCount(); 225 for (int i = 0; i < count; i++) { 226 UsbInterface intf = device.getInterface(i); 227 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), 228 intf.getInterfaceProtocol(), intf.getName())) return true; 229 } 230 231 return false; 232 } 233 234 /** 235 * If the device described by {@code device} covered by this filter? 236 * 237 * @param device The device 238 * 239 * @return {@code true} iff this filter covers the {@code device} 240 */ contains(DeviceFilter device)241 public boolean contains(DeviceFilter device) { 242 // -1 and null means "match anything" 243 244 if (mVendorId != -1 && device.mVendorId != mVendorId) return false; 245 if (mProductId != -1 && device.mProductId != mProductId) return false; 246 if (mManufacturerName != null && !Objects.equals(mManufacturerName, 247 device.mManufacturerName)) { 248 return false; 249 } 250 if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) { 251 return false; 252 } 253 if (mSerialNumber != null 254 && !Objects.equals(mSerialNumber, device.mSerialNumber)) { 255 return false; 256 } 257 258 // check device class/subclass/protocol 259 return matches(device.mClass, device.mSubclass, device.mProtocol); 260 } 261 262 @Override equals(@ullable Object obj)263 public boolean equals(@Nullable Object obj) { 264 // can't compare if we have wildcard strings 265 if (mVendorId == -1 || mProductId == -1 || 266 mClass == -1 || mSubclass == -1 || mProtocol == -1) { 267 return false; 268 } 269 if (obj instanceof DeviceFilter) { 270 DeviceFilter filter = (DeviceFilter)obj; 271 272 if (filter.mVendorId != mVendorId || 273 filter.mProductId != mProductId || 274 filter.mClass != mClass || 275 filter.mSubclass != mSubclass || 276 filter.mProtocol != mProtocol) { 277 return(false); 278 } 279 if ((filter.mManufacturerName != null && 280 mManufacturerName == null) || 281 (filter.mManufacturerName == null && 282 mManufacturerName != null) || 283 (filter.mProductName != null && 284 mProductName == null) || 285 (filter.mProductName == null && 286 mProductName != null) || 287 (filter.mSerialNumber != null && 288 mSerialNumber == null) || 289 (filter.mSerialNumber == null && 290 mSerialNumber != null)) { 291 return(false); 292 } 293 if ((filter.mManufacturerName != null && 294 mManufacturerName != null && 295 !mManufacturerName.equals(filter.mManufacturerName)) || 296 (filter.mProductName != null && 297 mProductName != null && 298 !mProductName.equals(filter.mProductName)) || 299 (filter.mSerialNumber != null && 300 mSerialNumber != null && 301 !mSerialNumber.equals(filter.mSerialNumber))) { 302 return false; 303 } 304 return true; 305 } 306 if (obj instanceof UsbDevice) { 307 UsbDevice device = (UsbDevice)obj; 308 if (device.getVendorId() != mVendorId || 309 device.getProductId() != mProductId || 310 device.getDeviceClass() != mClass || 311 device.getDeviceSubclass() != mSubclass || 312 device.getDeviceProtocol() != mProtocol) { 313 return(false); 314 } 315 if ((mManufacturerName != null && device.getManufacturerName() == null) || 316 (mManufacturerName == null && device.getManufacturerName() != null) || 317 (mProductName != null && device.getProductName() == null) || 318 (mProductName == null && device.getProductName() != null) || 319 (mSerialNumber != null && device.getSerialNumber() == null) || 320 (mSerialNumber == null && device.getSerialNumber() != null)) { 321 return(false); 322 } 323 if ((device.getManufacturerName() != null && 324 !mManufacturerName.equals(device.getManufacturerName())) || 325 (device.getProductName() != null && 326 !mProductName.equals(device.getProductName())) || 327 (device.getSerialNumber() != null && 328 !mSerialNumber.equals(device.getSerialNumber()))) { 329 return false; 330 } 331 return true; 332 } 333 return false; 334 } 335 336 @Override hashCode()337 public int hashCode() { 338 return (((mVendorId << 16) | mProductId) ^ 339 ((mClass << 16) | (mSubclass << 8) | mProtocol)); 340 } 341 342 @Override toString()343 public String toString() { 344 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId 345 + ",mClass=" + mClass + ",mSubclass=" + mSubclass 346 + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName 347 + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber 348 + ",mInterfaceName=" + mInterfaceName 349 + "]"; 350 } 351 352 /** 353 * Write a description of the filter to a dump stream. 354 */ dump(@onNull DualDumpOutputStream dump, String idName, long id)355 public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { 356 long token = dump.start(idName, id); 357 358 dump.write("vendor_id", UsbDeviceFilterProto.VENDOR_ID, mVendorId); 359 dump.write("product_id", UsbDeviceFilterProto.PRODUCT_ID, mProductId); 360 dump.write("class", UsbDeviceFilterProto.CLASS, mClass); 361 dump.write("subclass", UsbDeviceFilterProto.SUBCLASS, mSubclass); 362 dump.write("protocol", UsbDeviceFilterProto.PROTOCOL, mProtocol); 363 dump.write("manufacturer_name", UsbDeviceFilterProto.MANUFACTURER_NAME, mManufacturerName); 364 dump.write("product_name", UsbDeviceFilterProto.PRODUCT_NAME, mProductName); 365 dump.write("serial_number", UsbDeviceFilterProto.SERIAL_NUMBER, mSerialNumber); 366 367 dump.end(token); 368 } 369 } 370