1 /* 2 * Copyright (C) 2011 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 an 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import static com.android.internal.usb.DumpUtils.writeDevice; 20 import static com.android.internal.util.dump.DumpUtils.writeComponentName; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.hardware.usb.UsbConstants; 27 import android.hardware.usb.UsbDevice; 28 import android.os.Bundle; 29 import android.os.ParcelFileDescriptor; 30 import android.service.ServiceProtoEnums; 31 import android.service.usb.UsbConnectionRecordProto; 32 import android.service.usb.UsbHostManagerProto; 33 import android.service.usb.UsbIsHeadsetProto; 34 import android.text.TextUtils; 35 import android.util.ArrayMap; 36 import android.util.Slog; 37 38 import com.android.internal.annotations.GuardedBy; 39 import com.android.internal.util.FrameworkStatsLog; 40 import com.android.internal.util.IndentingPrintWriter; 41 import com.android.internal.util.dump.DualDumpOutputStream; 42 import com.android.server.usb.descriptors.UsbDescriptor; 43 import com.android.server.usb.descriptors.UsbDescriptorParser; 44 import com.android.server.usb.descriptors.UsbDeviceDescriptor; 45 import com.android.server.usb.descriptors.UsbInterfaceDescriptor; 46 import com.android.server.usb.descriptors.report.TextReportCanvas; 47 import com.android.server.usb.descriptors.tree.UsbDescriptorsTree; 48 49 import java.text.SimpleDateFormat; 50 import java.util.Date; 51 import java.util.HashMap; 52 import java.util.LinkedList; 53 54 /** 55 * UsbHostManager manages USB state in host mode. 56 */ 57 public class UsbHostManager { 58 private static final String TAG = UsbHostManager.class.getSimpleName(); 59 private static final boolean DEBUG = false; 60 private static final int LINUX_FOUNDATION_VID = 0x1d6b; 61 62 private final Context mContext; 63 64 // USB busses to exclude from USB host support 65 private final String[] mHostDenyList; 66 67 private final UsbAlsaManager mUsbAlsaManager; 68 private final UsbPermissionManager mPermissionManager; 69 70 private final Object mLock = new Object(); 71 @GuardedBy("mLock") 72 // contains all connected USB devices 73 private final HashMap<String, UsbDevice> mDevices = new HashMap<>(); 74 75 private Object mSettingsLock = new Object(); 76 @GuardedBy("mSettingsLock") 77 private UsbProfileGroupSettingsManager mCurrentSettings; 78 79 private Object mHandlerLock = new Object(); 80 @GuardedBy("mHandlerLock") 81 private ComponentName mUsbDeviceConnectionHandler; 82 83 /* 84 * Member used for tracking connections & disconnections 85 */ 86 static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS"); 87 private static final int MAX_CONNECT_RECORDS = 32; 88 private int mNumConnects; // TOTAL # of connect/disconnect 89 private final LinkedList<ConnectionRecord> mConnections = new LinkedList<ConnectionRecord>(); 90 private ConnectionRecord mLastConnect; 91 private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>(); 92 93 /* 94 * ConnectionRecord 95 * Stores connection/disconnection data. 96 */ 97 class ConnectionRecord { 98 long mTimestamp; // Same time-base as system log. 99 String mDeviceAddress; 100 101 static final int CONNECT = ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT; // 0 102 static final int CONNECT_BADPARSE = 103 ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT_BADPARSE; // 1 104 static final int CONNECT_BADDEVICE = 105 ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT_BADDEVICE; // 2 106 static final int DISCONNECT = 107 ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_DISCONNECT; // -1 108 109 final int mMode; 110 final byte[] mDescriptors; 111 ConnectionRecord(String deviceAddress, int mode, byte[] descriptors)112 ConnectionRecord(String deviceAddress, int mode, byte[] descriptors) { 113 mTimestamp = System.currentTimeMillis(); 114 mDeviceAddress = deviceAddress; 115 mMode = mode; 116 mDescriptors = descriptors; 117 } 118 formatTime()119 private String formatTime() { 120 return (new StringBuilder(sFormat.format(new Date(mTimestamp)))).toString(); 121 } 122 dump(@onNull DualDumpOutputStream dump, String idName, long id)123 void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { 124 long token = dump.start(idName, id); 125 126 dump.write("device_address", UsbConnectionRecordProto.DEVICE_ADDRESS, mDeviceAddress); 127 dump.write("mode", UsbConnectionRecordProto.MODE, mMode); 128 dump.write("timestamp", UsbConnectionRecordProto.TIMESTAMP, mTimestamp); 129 130 if (mMode != DISCONNECT) { 131 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors); 132 133 UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor(); 134 135 dump.write("manufacturer", UsbConnectionRecordProto.MANUFACTURER, 136 deviceDescriptor.getVendorID()); 137 dump.write("product", UsbConnectionRecordProto.PRODUCT, 138 deviceDescriptor.getProductID()); 139 long isHeadSetToken = dump.start("is_headset", UsbConnectionRecordProto.IS_HEADSET); 140 dump.write("in", UsbIsHeadsetProto.IN, parser.isInputHeadset()); 141 dump.write("out", UsbIsHeadsetProto.OUT, parser.isOutputHeadset()); 142 dump.end(isHeadSetToken); 143 } 144 145 dump.end(token); 146 } 147 dumpShort(IndentingPrintWriter pw)148 void dumpShort(IndentingPrintWriter pw) { 149 if (mMode != DISCONNECT) { 150 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode); 151 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors); 152 153 UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor(); 154 155 pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID()) 156 + " product:" + Integer.toHexString(deviceDescriptor.getProductID())); 157 pw.println("isHeadset[in: " + parser.isInputHeadset() 158 + " , out: " + parser.isOutputHeadset() + "]"); 159 } else { 160 pw.println(formatTime() + " Disconnect " + mDeviceAddress); 161 } 162 } 163 dumpTree(IndentingPrintWriter pw)164 void dumpTree(IndentingPrintWriter pw) { 165 if (mMode != DISCONNECT) { 166 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode); 167 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors); 168 StringBuilder stringBuilder = new StringBuilder(); 169 UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree(); 170 descriptorTree.parse(parser); 171 descriptorTree.report(new TextReportCanvas(parser, stringBuilder)); 172 173 stringBuilder.append("isHeadset[in: " + parser.isInputHeadset() 174 + " , out: " + parser.isOutputHeadset() + "]"); 175 pw.println(stringBuilder.toString()); 176 } else { 177 pw.println(formatTime() + " Disconnect " + mDeviceAddress); 178 } 179 } 180 dumpList(IndentingPrintWriter pw)181 void dumpList(IndentingPrintWriter pw) { 182 if (mMode != DISCONNECT) { 183 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode); 184 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors); 185 StringBuilder stringBuilder = new StringBuilder(); 186 TextReportCanvas canvas = new TextReportCanvas(parser, stringBuilder); 187 for (UsbDescriptor descriptor : parser.getDescriptors()) { 188 descriptor.report(canvas); 189 } 190 pw.println(stringBuilder.toString()); 191 192 pw.println("isHeadset[in: " + parser.isInputHeadset() 193 + " , out: " + parser.isOutputHeadset() + "]"); 194 } else { 195 pw.println(formatTime() + " Disconnect " + mDeviceAddress); 196 } 197 } 198 199 private static final int kDumpBytesPerLine = 16; 200 dumpRaw(IndentingPrintWriter pw)201 void dumpRaw(IndentingPrintWriter pw) { 202 if (mMode != DISCONNECT) { 203 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode); 204 int length = mDescriptors.length; 205 pw.println("Raw Descriptors " + length + " bytes"); 206 int dataOffset = 0; 207 for (int line = 0; line < length / kDumpBytesPerLine; line++) { 208 StringBuilder sb = new StringBuilder(); 209 for (int offset = 0; offset < kDumpBytesPerLine; offset++) { 210 sb.append("0x") 211 .append(String.format("0x%02X", mDescriptors[dataOffset++])) 212 .append(" "); 213 } 214 pw.println(sb.toString()); 215 } 216 217 // remainder 218 StringBuilder sb = new StringBuilder(); 219 while (dataOffset < length) { 220 sb.append("0x") 221 .append(String.format("0x%02X", mDescriptors[dataOffset++])) 222 .append(" "); 223 } 224 pw.println(sb.toString()); 225 } else { 226 pw.println(formatTime() + " Disconnect " + mDeviceAddress); 227 } 228 } 229 } 230 231 /* 232 * UsbHostManager 233 */ UsbHostManager(Context context, UsbAlsaManager alsaManager, UsbPermissionManager permissionManager)234 public UsbHostManager(Context context, UsbAlsaManager alsaManager, 235 UsbPermissionManager permissionManager) { 236 mContext = context; 237 238 mHostDenyList = context.getResources().getStringArray( 239 com.android.internal.R.array.config_usbHostDenylist); 240 mUsbAlsaManager = alsaManager; 241 mPermissionManager = permissionManager; 242 String deviceConnectionHandler = context.getResources().getString( 243 com.android.internal.R.string.config_UsbDeviceConnectionHandling_component); 244 if (!TextUtils.isEmpty(deviceConnectionHandler)) { 245 setUsbDeviceConnectionHandler(ComponentName.unflattenFromString( 246 deviceConnectionHandler)); 247 } 248 } 249 setCurrentUserSettings(UsbProfileGroupSettingsManager settings)250 public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) { 251 synchronized (mSettingsLock) { 252 mCurrentSettings = settings; 253 } 254 } 255 getCurrentUserSettings()256 private UsbProfileGroupSettingsManager getCurrentUserSettings() { 257 synchronized (mSettingsLock) { 258 return mCurrentSettings; 259 } 260 } 261 setUsbDeviceConnectionHandler(@ullable ComponentName usbDeviceConnectionHandler)262 public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) { 263 synchronized (mHandlerLock) { 264 mUsbDeviceConnectionHandler = usbDeviceConnectionHandler; 265 } 266 } 267 getUsbDeviceConnectionHandler()268 private @Nullable ComponentName getUsbDeviceConnectionHandler() { 269 synchronized (mHandlerLock) { 270 return mUsbDeviceConnectionHandler; 271 } 272 } 273 isDenyListed(String deviceAddress)274 private boolean isDenyListed(String deviceAddress) { 275 int count = mHostDenyList.length; 276 for (int i = 0; i < count; i++) { 277 if (deviceAddress.startsWith(mHostDenyList[i])) { 278 return true; 279 } 280 } 281 return false; 282 } 283 284 /* returns true if the USB device should not be accessible by applications */ isDenyListed(int clazz, int subClass)285 private boolean isDenyListed(int clazz, int subClass) { 286 // deny hubs 287 if (clazz == UsbConstants.USB_CLASS_HUB) return true; 288 289 // deny HID boot devices (mouse and keyboard) 290 return clazz == UsbConstants.USB_CLASS_HID 291 && subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT; 292 293 } 294 addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors)295 private void addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors) { 296 mNumConnects++; 297 while (mConnections.size() >= MAX_CONNECT_RECORDS) { 298 mConnections.removeFirst(); 299 } 300 ConnectionRecord rec = 301 new ConnectionRecord(deviceAddress, mode, rawDescriptors); 302 mConnections.add(rec); 303 if (mode != ConnectionRecord.DISCONNECT) { 304 mLastConnect = rec; 305 } 306 if (mode == ConnectionRecord.CONNECT) { 307 mConnected.put(deviceAddress, rec); 308 } else if (mode == ConnectionRecord.DISCONNECT) { 309 mConnected.remove(deviceAddress); 310 } 311 } 312 logUsbDevice(UsbDescriptorParser descriptorParser)313 private void logUsbDevice(UsbDescriptorParser descriptorParser) { 314 int vid = 0; 315 int pid = 0; 316 String mfg = "<unknown>"; 317 String product = "<unknown>"; 318 String version = "<unknown>"; 319 String serial = "<unknown>"; 320 321 UsbDeviceDescriptor deviceDescriptor = descriptorParser.getDeviceDescriptor(); 322 if (deviceDescriptor != null) { 323 vid = deviceDescriptor.getVendorID(); 324 pid = deviceDescriptor.getProductID(); 325 mfg = deviceDescriptor.getMfgString(descriptorParser); 326 product = deviceDescriptor.getProductString(descriptorParser); 327 version = deviceDescriptor.getDeviceReleaseString(); 328 serial = deviceDescriptor.getSerialString(descriptorParser); 329 } 330 331 if (vid == LINUX_FOUNDATION_VID) { 332 return; // don't care about OS-constructed virtual USB devices. 333 } 334 boolean hasAudio = descriptorParser.hasAudioInterface(); 335 boolean hasHid = descriptorParser.hasHIDInterface(); 336 boolean hasStorage = descriptorParser.hasStorageInterface(); 337 338 String attachedString = "USB device attached: "; 339 attachedString += String.format("vidpid %04x:%04x", vid, pid); 340 attachedString += String.format(" mfg/product/ver/serial %s/%s/%s/%s", 341 mfg, product, version, serial); 342 attachedString += String.format(" hasAudio/HID/Storage: %b/%b/%b", 343 hasAudio, hasHid, hasStorage); 344 Slog.d(TAG, attachedString); 345 } 346 347 /* Called from JNI in monitorUsbHostBus() to report new USB devices 348 Returns true if successful, i.e. the USB Audio device descriptors are 349 correctly parsed and the unique device is added to the audio device list. 350 */ 351 @SuppressWarnings("unused") usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass, byte[] descriptors)352 private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass, 353 byte[] descriptors) { 354 if (DEBUG) { 355 Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start"); 356 } 357 358 if (isDenyListed(deviceAddress)) { 359 if (DEBUG) { 360 Slog.d(TAG, "device address is Deny listed"); 361 } 362 return false; 363 } 364 365 if (isDenyListed(deviceClass, deviceSubclass)) { 366 if (DEBUG) { 367 Slog.d(TAG, "device class is deny listed"); 368 } 369 return false; 370 } 371 372 UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors); 373 if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE 374 && !checkUsbInterfacesDenyListed(parser)) { 375 return false; 376 } 377 378 // Potentially can block as it may read data from the USB device. 379 logUsbDevice(parser); 380 381 synchronized (mLock) { 382 if (mDevices.get(deviceAddress) != null) { 383 Slog.w(TAG, "device already on mDevices list: " + deviceAddress); 384 //TODO If this is the same peripheral as is being connected, replace 385 // it with the new connection. 386 return false; 387 } 388 389 UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder(); 390 if (newDeviceBuilder == null) { 391 Slog.e(TAG, "Couldn't create UsbDevice object."); 392 // Tracking 393 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE, 394 parser.getRawDescriptors()); 395 } else { 396 UsbSerialReader serialNumberReader = new UsbSerialReader(mContext, 397 mPermissionManager, newDeviceBuilder.serialNumber); 398 UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader); 399 serialNumberReader.setDevice(newDevice); 400 401 mDevices.put(deviceAddress, newDevice); 402 Slog.d(TAG, "Added device " + newDevice); 403 404 // It is fine to call this only for the current user as all broadcasts are 405 // sent to all profiles of the user and the dialogs should only show once. 406 ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler(); 407 if (usbDeviceConnectionHandler == null) { 408 getCurrentUserSettings().deviceAttached(newDevice); 409 } else { 410 getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice, 411 usbDeviceConnectionHandler); 412 } 413 414 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser); 415 416 // Tracking 417 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT, 418 parser.getRawDescriptors()); 419 420 // Stats collection 421 FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED, 422 newDevice.getVendorId(), newDevice.getProductId(), 423 parser.hasAudioInterface(), parser.hasHIDInterface(), 424 parser.hasStorageInterface(), 425 FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0); 426 } 427 } 428 429 if (DEBUG) { 430 Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end"); 431 } 432 433 return true; 434 } 435 436 /* Called from JNI in monitorUsbHostBus to report USB device removal */ 437 @SuppressWarnings("unused") usbDeviceRemoved(String deviceAddress)438 private void usbDeviceRemoved(String deviceAddress) { 439 if (DEBUG) { 440 Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end"); 441 } 442 443 synchronized (mLock) { 444 UsbDevice device = mDevices.remove(deviceAddress); 445 if (device != null) { 446 Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName()); 447 mUsbAlsaManager.usbDeviceRemoved(deviceAddress); 448 mPermissionManager.usbDeviceRemoved(device); 449 getCurrentUserSettings().usbDeviceRemoved(device); 450 ConnectionRecord current = mConnected.get(deviceAddress); 451 // Tracking 452 addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null); 453 454 if (current != null) { 455 UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, 456 current.mDescriptors); 457 // Stats collection 458 FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED, 459 device.getVendorId(), device.getProductId(), parser.hasAudioInterface(), 460 parser.hasHIDInterface(), parser.hasStorageInterface(), 461 FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED, 462 System.currentTimeMillis() - current.mTimestamp); 463 } 464 } else { 465 Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone"); 466 } 467 } 468 } 469 systemReady()470 public void systemReady() { 471 synchronized (mLock) { 472 // Create a thread to call into native code to wait for USB host events. 473 // This thread will call us back on usbDeviceAdded and usbDeviceRemoved. 474 Runnable runnable = this::monitorUsbHostBus; 475 new Thread(null, runnable, "UsbService host thread").start(); 476 } 477 } 478 479 /* Returns a list of all currently attached USB devices */ getDeviceList(Bundle devices)480 public void getDeviceList(Bundle devices) { 481 synchronized (mLock) { 482 for (String name : mDevices.keySet()) { 483 devices.putParcelable(name, mDevices.get(name)); 484 } 485 } 486 } 487 488 /** 489 * Opens the specified USB device 490 */ openDevice(String deviceAddress, UsbUserPermissionManager permissions, String packageName, int pid, int uid)491 public ParcelFileDescriptor openDevice(String deviceAddress, 492 UsbUserPermissionManager permissions, String packageName, int pid, int uid) { 493 synchronized (mLock) { 494 if (isDenyListed(deviceAddress)) { 495 throw new SecurityException("USB device is on a restricted bus"); 496 } 497 UsbDevice device = mDevices.get(deviceAddress); 498 if (device == null) { 499 // if it is not in mDevices, it either does not exist or is denylisted 500 throw new IllegalArgumentException( 501 "device " + deviceAddress + " does not exist or is restricted"); 502 } 503 504 permissions.checkPermission(device, packageName, pid, uid); 505 return nativeOpenDevice(deviceAddress); 506 } 507 } 508 509 /** 510 * Dump out various information about the state of USB device connections. 511 */ dump(DualDumpOutputStream dump, String idName, long id)512 public void dump(DualDumpOutputStream dump, String idName, long id) { 513 long token = dump.start(idName, id); 514 515 synchronized (mHandlerLock) { 516 if (mUsbDeviceConnectionHandler != null) { 517 writeComponentName(dump, "default_usb_host_connection_handler", 518 UsbHostManagerProto.DEFAULT_USB_HOST_CONNECTION_HANDLER, 519 mUsbDeviceConnectionHandler); 520 } 521 } 522 synchronized (mLock) { 523 for (String name : mDevices.keySet()) { 524 writeDevice(dump, "devices", UsbHostManagerProto.DEVICES, mDevices.get(name)); 525 } 526 527 dump.write("num_connects", UsbHostManagerProto.NUM_CONNECTS, mNumConnects); 528 529 for (ConnectionRecord rec : mConnections) { 530 rec.dump(dump, "connections", UsbHostManagerProto.CONNECTIONS); 531 } 532 } 533 534 dump.end(token); 535 } 536 537 /** 538 * Dump various descriptor data. 539 */ dumpDescriptors(IndentingPrintWriter pw, String[] args)540 public void dumpDescriptors(IndentingPrintWriter pw, String[] args) { 541 if (mLastConnect != null) { 542 pw.println("Last Connected USB Device:"); 543 if (args.length <= 1 || args[1].equals("-dump-short")) { 544 mLastConnect.dumpShort(pw); 545 } else if (args[1].equals("-dump-tree")) { 546 mLastConnect.dumpTree(pw); 547 } else if (args[1].equals("-dump-list")) { 548 mLastConnect.dumpList(pw); 549 } else if (args[1].equals("-dump-raw")) { 550 mLastConnect.dumpRaw(pw); 551 } 552 } else { 553 pw.println("No USB Devices have been connected."); 554 } 555 } 556 checkUsbInterfacesDenyListed(UsbDescriptorParser parser)557 private boolean checkUsbInterfacesDenyListed(UsbDescriptorParser parser) { 558 // Device class needs to be obtained through the device interface. Ignore device only 559 // if ALL interfaces are deny-listed. 560 boolean shouldIgnoreDevice = false; 561 for (UsbDescriptor descriptor: parser.getDescriptors()) { 562 if (!(descriptor instanceof UsbInterfaceDescriptor)) { 563 continue; 564 } 565 UsbInterfaceDescriptor iface = (UsbInterfaceDescriptor) descriptor; 566 shouldIgnoreDevice = isDenyListed(iface.getUsbClass(), iface.getUsbSubclass()); 567 if (!shouldIgnoreDevice) { 568 break; 569 } 570 } 571 if (shouldIgnoreDevice) { 572 if (DEBUG) { 573 Slog.d(TAG, "usb interface class is deny listed"); 574 } 575 return false; 576 } 577 return true; 578 } 579 monitorUsbHostBus()580 private native void monitorUsbHostBus(); nativeOpenDevice(String deviceAddress)581 private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress); 582 } 583