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(String.format("0x%02X", mDescriptors[dataOffset++])).append(" "); 225 } 226 pw.println(sb.toString()); 227 } 228 229 // remainder 230 StringBuilder sb = new StringBuilder(); 231 while (dataOffset < length) { 232 sb.append(String.format("0x%02X", mDescriptors[dataOffset++])).append(" "); 233 } 234 pw.println(sb.toString()); 235 } else { 236 pw.println(formatTime() + " Disconnect " + mDeviceAddress); 237 } 238 } 239 } 240 241 /* 242 * UsbHostManager 243 */ UsbHostManager(Context context, UsbAlsaManager alsaManager, UsbPermissionManager permissionManager)244 public UsbHostManager(Context context, UsbAlsaManager alsaManager, 245 UsbPermissionManager permissionManager) { 246 mContext = context; 247 248 mHostDenyList = context.getResources().getStringArray( 249 com.android.internal.R.array.config_usbHostDenylist); 250 mUsbAlsaManager = alsaManager; 251 mPermissionManager = permissionManager; 252 String deviceConnectionHandler = context.getResources().getString( 253 com.android.internal.R.string.config_UsbDeviceConnectionHandling_component); 254 if (!TextUtils.isEmpty(deviceConnectionHandler)) { 255 setUsbDeviceConnectionHandler(ComponentName.unflattenFromString( 256 deviceConnectionHandler)); 257 } 258 mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); 259 } 260 setCurrentUserSettings(UsbProfileGroupSettingsManager settings)261 public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) { 262 synchronized (mSettingsLock) { 263 mCurrentSettings = settings; 264 } 265 } 266 getCurrentUserSettings()267 private UsbProfileGroupSettingsManager getCurrentUserSettings() { 268 synchronized (mSettingsLock) { 269 return mCurrentSettings; 270 } 271 } 272 setUsbDeviceConnectionHandler(@ullable ComponentName usbDeviceConnectionHandler)273 public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) { 274 synchronized (mHandlerLock) { 275 mUsbDeviceConnectionHandler = usbDeviceConnectionHandler; 276 } 277 } 278 getUsbDeviceConnectionHandler()279 private @Nullable ComponentName getUsbDeviceConnectionHandler() { 280 synchronized (mHandlerLock) { 281 return mUsbDeviceConnectionHandler; 282 } 283 } 284 isDenyListed(String deviceAddress)285 private boolean isDenyListed(String deviceAddress) { 286 int count = mHostDenyList.length; 287 for (int i = 0; i < count; i++) { 288 if (deviceAddress.startsWith(mHostDenyList[i])) { 289 return true; 290 } 291 } 292 return false; 293 } 294 295 /* returns true if the USB device should not be accessible by applications */ isDenyListed(int clazz, int subClass)296 private boolean isDenyListed(int clazz, int subClass) { 297 // deny hubs 298 if (clazz == UsbConstants.USB_CLASS_HUB) return true; 299 300 // deny HID boot devices (mouse and keyboard) 301 return clazz == UsbConstants.USB_CLASS_HID 302 && subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT; 303 304 } 305 addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors)306 private void addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors) { 307 mNumConnects++; 308 while (mConnections.size() >= MAX_CONNECT_RECORDS) { 309 mConnections.removeFirst(); 310 } 311 ConnectionRecord rec = 312 new ConnectionRecord(deviceAddress, mode, rawDescriptors); 313 mConnections.add(rec); 314 if (mode != ConnectionRecord.DISCONNECT) { 315 mLastConnect = rec; 316 } 317 if (mode == ConnectionRecord.CONNECT) { 318 mConnected.put(deviceAddress, rec); 319 } else if (mode == ConnectionRecord.DISCONNECT) { 320 mConnected.remove(deviceAddress); 321 } 322 } 323 logUsbDevice(UsbDescriptorParser descriptorParser)324 private void logUsbDevice(UsbDescriptorParser descriptorParser) { 325 int vid = 0; 326 int pid = 0; 327 String mfg = "<unknown>"; 328 String product = "<unknown>"; 329 String version = "<unknown>"; 330 String serial = "<unknown>"; 331 332 UsbDeviceDescriptor deviceDescriptor = descriptorParser.getDeviceDescriptor(); 333 if (deviceDescriptor != null) { 334 vid = deviceDescriptor.getVendorID(); 335 pid = deviceDescriptor.getProductID(); 336 mfg = deviceDescriptor.getMfgString(descriptorParser); 337 product = deviceDescriptor.getProductString(descriptorParser); 338 version = deviceDescriptor.getDeviceReleaseString(); 339 serial = deviceDescriptor.getSerialString(descriptorParser); 340 } 341 342 if (vid == LINUX_FOUNDATION_VID) { 343 return; // don't care about OS-constructed virtual USB devices. 344 } 345 boolean hasAudio = descriptorParser.hasAudioInterface(); 346 boolean hasHid = descriptorParser.hasHIDInterface(); 347 boolean hasStorage = descriptorParser.hasStorageInterface(); 348 349 String attachedString = "USB device attached: "; 350 attachedString += String.format("vidpid %04x:%04x", vid, pid); 351 attachedString += String.format(" mfg/product/ver/serial %s/%s/%s/%s", 352 mfg, product, version, serial); 353 attachedString += String.format(" hasAudio/HID/Storage: %b/%b/%b", 354 hasAudio, hasHid, hasStorage); 355 Slog.d(TAG, attachedString); 356 } 357 358 /* Called from JNI in monitorUsbHostBus() to report new USB devices 359 Returns true if successful, i.e. the USB Audio device descriptors are 360 correctly parsed and the unique device is added to the audio device list. 361 */ 362 @SuppressWarnings("unused") usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass, byte[] descriptors)363 private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass, 364 byte[] descriptors) { 365 if (DEBUG) { 366 Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start"); 367 } 368 369 if (isDenyListed(deviceAddress)) { 370 if (DEBUG) { 371 Slog.d(TAG, "device address is Deny listed"); 372 } 373 return false; 374 } 375 376 if (isDenyListed(deviceClass, deviceSubclass)) { 377 if (DEBUG) { 378 Slog.d(TAG, "device class is deny listed"); 379 } 380 return false; 381 } 382 383 if (descriptors == null) { 384 Slog.e(TAG, "Failed to add device as the descriptor is null"); 385 return false; 386 } 387 388 UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors); 389 if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE 390 && !checkUsbInterfacesDenyListed(parser)) { 391 return false; 392 } 393 394 // Potentially can block as it may read data from the USB device. 395 logUsbDevice(parser); 396 397 synchronized (mLock) { 398 if (mDevices.get(deviceAddress) != null) { 399 Slog.w(TAG, "device already on mDevices list: " + deviceAddress); 400 //TODO If this is the same peripheral as is being connected, replace 401 // it with the new connection. 402 return false; 403 } 404 405 UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder(); 406 if (newDeviceBuilder == null) { 407 Slog.e(TAG, "Couldn't create UsbDevice object."); 408 // Tracking 409 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE, 410 parser.getRawDescriptors()); 411 } else { 412 UsbSerialReader serialNumberReader = new UsbSerialReader(mContext, 413 mPermissionManager, newDeviceBuilder.serialNumber); 414 UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader); 415 serialNumberReader.setDevice(newDevice); 416 417 mDevices.put(deviceAddress, newDevice); 418 Slog.d(TAG, "Added device " + newDevice); 419 420 // It is fine to call this only for the current user as all broadcasts are 421 // sent to all profiles of the user and the dialogs should only show once. 422 ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler(); 423 if (usbDeviceConnectionHandler == null) { 424 getCurrentUserSettings().deviceAttached(newDevice); 425 } else { 426 getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice, 427 usbDeviceConnectionHandler); 428 } 429 430 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser); 431 432 if (mHasMidiFeature) { 433 // Use a 3 digit code to associate MIDI devices with one another. 434 // Each MIDI device already has mId for uniqueness. mId is generated 435 // sequentially. For clarity, this code is not generated sequentially. 436 String uniqueUsbDeviceIdentifier = generateNewUsbDeviceIdentifier(); 437 438 ArrayList<UsbDirectMidiDevice> midiDevices = 439 new ArrayList<UsbDirectMidiDevice>(); 440 if (parser.containsUniversalMidiDeviceEndpoint()) { 441 UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext, 442 newDevice, parser, true, uniqueUsbDeviceIdentifier); 443 if (midiDevice != null) { 444 midiDevices.add(midiDevice); 445 } else { 446 Slog.e(TAG, "Universal Midi Device is null."); 447 } 448 449 // Use UsbDirectMidiDevice only if this supports MIDI 2.0 as well. 450 // ALSA removes the audio sound card if MIDI interfaces are removed. 451 // This means that as long as ALSA is used for audio, MIDI 1.0 USB 452 // devices should use the ALSA path for MIDI. 453 if (parser.containsLegacyMidiDeviceEndpoint()) { 454 midiDevice = UsbDirectMidiDevice.create(mContext, 455 newDevice, parser, false, uniqueUsbDeviceIdentifier); 456 if (midiDevice != null) { 457 midiDevices.add(midiDevice); 458 } else { 459 Slog.e(TAG, "Legacy Midi Device is null."); 460 } 461 } 462 } 463 464 if (!midiDevices.isEmpty()) { 465 mMidiDevices.put(deviceAddress, midiDevices); 466 } 467 } 468 469 // Tracking 470 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT, descriptors); 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