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