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 import android.util.StatsLog; 38 39 import com.android.internal.annotations.GuardedBy; 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[] mHostBlacklist; 66 67 private final UsbAlsaManager mUsbAlsaManager; 68 private final UsbSettingsManager mSettingsManager; 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, UsbSettingsManager settingsManager)234 public UsbHostManager(Context context, UsbAlsaManager alsaManager, 235 UsbSettingsManager settingsManager) { 236 mContext = context; 237 238 mHostBlacklist = context.getResources().getStringArray( 239 com.android.internal.R.array.config_usbHostBlacklist); 240 mUsbAlsaManager = alsaManager; 241 mSettingsManager = settingsManager; 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 isBlackListed(String deviceAddress)274 private boolean isBlackListed(String deviceAddress) { 275 int count = mHostBlacklist.length; 276 for (int i = 0; i < count; i++) { 277 if (deviceAddress.startsWith(mHostBlacklist[i])) { 278 return true; 279 } 280 } 281 return false; 282 } 283 284 /* returns true if the USB device should not be accessible by applications */ isBlackListed(int clazz, int subClass)285 private boolean isBlackListed(int clazz, int subClass) { 286 // blacklist hubs 287 if (clazz == UsbConstants.USB_CLASS_HUB) return true; 288 289 // blacklist 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 (isBlackListed(deviceAddress)) { 359 if (DEBUG) { 360 Slog.d(TAG, "device address is black listed"); 361 } 362 return false; 363 } 364 365 if (isBlackListed(deviceClass, deviceSubclass)) { 366 if (DEBUG) { 367 Slog.d(TAG, "device class is black listed"); 368 } 369 return false; 370 } 371 372 UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors); 373 if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE 374 && !checkUsbInterfacesBlackListed(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.toAndroidUsbDevice(); 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, mSettingsManager, 397 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 StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(), 422 newDevice.getProductId(), parser.hasAudioInterface(), 423 parser.hasHIDInterface(), parser.hasStorageInterface(), 424 StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0); 425 } 426 } 427 428 if (DEBUG) { 429 Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end"); 430 } 431 432 return true; 433 } 434 435 /* Called from JNI in monitorUsbHostBus to report USB device removal */ 436 @SuppressWarnings("unused") usbDeviceRemoved(String deviceAddress)437 private void usbDeviceRemoved(String deviceAddress) { 438 if (DEBUG) { 439 Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end"); 440 } 441 442 synchronized (mLock) { 443 UsbDevice device = mDevices.remove(deviceAddress); 444 if (device != null) { 445 Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName()); 446 mUsbAlsaManager.usbDeviceRemoved(deviceAddress); 447 mSettingsManager.usbDeviceRemoved(device); 448 getCurrentUserSettings().usbDeviceRemoved(device); 449 ConnectionRecord current = mConnected.get(deviceAddress); 450 // Tracking 451 addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null); 452 453 if (current != null) { 454 UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, 455 current.mDescriptors); 456 // Stats collection 457 StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(), 458 device.getProductId(), parser.hasAudioInterface(), 459 parser.hasHIDInterface(), parser.hasStorageInterface(), 460 StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED, 461 System.currentTimeMillis() - current.mTimestamp); 462 } 463 } else { 464 Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone"); 465 } 466 } 467 } 468 systemReady()469 public void systemReady() { 470 synchronized (mLock) { 471 // Create a thread to call into native code to wait for USB host events. 472 // This thread will call us back on usbDeviceAdded and usbDeviceRemoved. 473 Runnable runnable = this::monitorUsbHostBus; 474 new Thread(null, runnable, "UsbService host thread").start(); 475 } 476 } 477 478 /* Returns a list of all currently attached USB devices */ getDeviceList(Bundle devices)479 public void getDeviceList(Bundle devices) { 480 synchronized (mLock) { 481 for (String name : mDevices.keySet()) { 482 devices.putParcelable(name, mDevices.get(name)); 483 } 484 } 485 } 486 487 /* Opens the specified USB device */ openDevice(String deviceAddress, UsbUserSettingsManager settings, String packageName, int pid, int uid)488 public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings, 489 String packageName, int pid, int uid) { 490 synchronized (mLock) { 491 if (isBlackListed(deviceAddress)) { 492 throw new SecurityException("USB device is on a restricted bus"); 493 } 494 UsbDevice device = mDevices.get(deviceAddress); 495 if (device == null) { 496 // if it is not in mDevices, it either does not exist or is blacklisted 497 throw new IllegalArgumentException( 498 "device " + deviceAddress + " does not exist or is restricted"); 499 } 500 501 settings.checkPermission(device, packageName, pid, uid); 502 return nativeOpenDevice(deviceAddress); 503 } 504 } 505 506 /** 507 * Dump out various information about the state of USB device connections. 508 */ dump(DualDumpOutputStream dump, String idName, long id)509 public void dump(DualDumpOutputStream dump, String idName, long id) { 510 long token = dump.start(idName, id); 511 512 synchronized (mHandlerLock) { 513 if (mUsbDeviceConnectionHandler != null) { 514 writeComponentName(dump, "default_usb_host_connection_handler", 515 UsbHostManagerProto.DEFAULT_USB_HOST_CONNECTION_HANDLER, 516 mUsbDeviceConnectionHandler); 517 } 518 } 519 synchronized (mLock) { 520 for (String name : mDevices.keySet()) { 521 writeDevice(dump, "devices", UsbHostManagerProto.DEVICES, mDevices.get(name)); 522 } 523 524 dump.write("num_connects", UsbHostManagerProto.NUM_CONNECTS, mNumConnects); 525 526 for (ConnectionRecord rec : mConnections) { 527 rec.dump(dump, "connections", UsbHostManagerProto.CONNECTIONS); 528 } 529 } 530 531 dump.end(token); 532 } 533 534 /** 535 * Dump various descriptor data. 536 */ dumpDescriptors(IndentingPrintWriter pw, String[] args)537 public void dumpDescriptors(IndentingPrintWriter pw, String[] args) { 538 if (mLastConnect != null) { 539 pw.println("Last Connected USB Device:"); 540 if (args.length <= 1 || args[1].equals("-dump-short")) { 541 mLastConnect.dumpShort(pw); 542 } else if (args[1].equals("-dump-tree")) { 543 mLastConnect.dumpTree(pw); 544 } else if (args[1].equals("-dump-list")) { 545 mLastConnect.dumpList(pw); 546 } else if (args[1].equals("-dump-raw")) { 547 mLastConnect.dumpRaw(pw); 548 } 549 } else { 550 pw.println("No USB Devices have been connected."); 551 } 552 } 553 checkUsbInterfacesBlackListed(UsbDescriptorParser parser)554 private boolean checkUsbInterfacesBlackListed(UsbDescriptorParser parser) { 555 // Device class needs to be obtained through the device interface. Ignore device only 556 // if ALL interfaces are black-listed. 557 boolean shouldIgnoreDevice = false; 558 for (UsbDescriptor descriptor: parser.getDescriptors()) { 559 if (!(descriptor instanceof UsbInterfaceDescriptor)) { 560 continue; 561 } 562 UsbInterfaceDescriptor iface = (UsbInterfaceDescriptor) descriptor; 563 shouldIgnoreDevice = isBlackListed(iface.getUsbClass(), iface.getUsbSubclass()); 564 if (!shouldIgnoreDevice) { 565 break; 566 } 567 } 568 if (shouldIgnoreDevice) { 569 if (DEBUG) { 570 Slog.d(TAG, "usb interface class is black listed"); 571 } 572 return false; 573 } 574 return true; 575 } 576 monitorUsbHostBus()577 private native void monitorUsbHostBus(); nativeOpenDevice(String deviceAddress)578 private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress); 579 } 580