1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.content.res.Resources; 22 import android.hardware.usb.UsbDevice; 23 import android.media.AudioManager; 24 import android.media.IAudioService; 25 import android.media.midi.MidiDeviceInfo; 26 import android.os.Bundle; 27 import android.os.FileObserver; 28 import android.os.ServiceManager; 29 import android.os.SystemClock; 30 import android.os.SystemProperties; 31 import android.provider.Settings; 32 import android.service.usb.UsbAlsaManagerProto; 33 import android.util.Slog; 34 35 import com.android.internal.alsa.AlsaCardsParser; 36 import com.android.internal.util.dump.DualDumpOutputStream; 37 import com.android.server.usb.descriptors.UsbDescriptorParser; 38 39 import libcore.io.IoUtils; 40 41 import java.io.File; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.HashMap; 45 import java.util.HashSet; 46 import java.util.List; 47 import java.util.Stack; 48 49 /** 50 * UsbAlsaManager manages USB audio and MIDI devices. 51 */ 52 public final class UsbAlsaManager { 53 private static final String TAG = UsbAlsaManager.class.getSimpleName(); 54 private static final boolean DEBUG = false; 55 56 // Flag to turn on/off multi-peripheral select mode 57 // Set to true to have multi-devices mode 58 private static final boolean IS_MULTI_MODE = SystemProperties.getBoolean( 59 "ro.audio.multi_usb_mode", false /*def*/); 60 61 private static final String ALSA_DIRECTORY = "/dev/snd/"; 62 63 private static final int ALSA_DEVICE_TYPE_UNKNOWN = 0; 64 private static final int ALSA_DEVICE_TYPE_PLAYBACK = 1; 65 private static final int ALSA_DEVICE_TYPE_CAPTURE = 2; 66 private static final int ALSA_DEVICE_TYPE_MIDI = 3; 67 68 private final Context mContext; 69 private IAudioService mAudioService; 70 private final boolean mHasMidiFeature; 71 72 private final AlsaCardsParser mCardsParser = new AlsaCardsParser(); 73 74 // this is needed to map USB devices to ALSA Audio Devices, especially to remove an 75 // ALSA device when we are notified that its associated USB device has been removed. 76 private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>(); 77 // A map from device type to attached devices. Given the audio framework only supports 78 // single device connection per device type, only the last attached device will be 79 // connected to audio framework. Once the last device is removed, previous device can 80 // be connected to audio framework. 81 private HashMap<Integer, Stack<UsbAlsaDevice>> mAttachedDevices = new HashMap<>(); 82 83 // 84 // Device Denylist 85 // 86 // This exists due to problems with Sony game controllers which present as an audio device 87 // even if no headset is connected and have no way to set the volume on the unit. 88 // Handle this by simply declining to use them as an audio device. 89 private static final int USB_VENDORID_SONY = 0x054C; 90 private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT1 = 0x05C4; 91 private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT2 = 0x09CC; 92 private static final int USB_PRODUCTID_PS5CONTROLLER = 0x0CE6; 93 94 private static final int USB_DENYLIST_OUTPUT = 0x0001; 95 private static final int USB_DENYLIST_INPUT = 0x0002; 96 97 private static class DenyListEntry { 98 final int mVendorId; 99 final int mProductId; 100 final int mFlags; 101 DenyListEntry(int vendorId, int productId, int flags)102 DenyListEntry(int vendorId, int productId, int flags) { 103 mVendorId = vendorId; 104 mProductId = productId; 105 mFlags = flags; 106 } 107 } 108 109 static final List<DenyListEntry> sDeviceDenylist = Arrays.asList( 110 new DenyListEntry(USB_VENDORID_SONY, 111 USB_PRODUCTID_PS4CONTROLLER_ZCT1, 112 USB_DENYLIST_OUTPUT), 113 new DenyListEntry(USB_VENDORID_SONY, 114 USB_PRODUCTID_PS4CONTROLLER_ZCT2, 115 USB_DENYLIST_OUTPUT), 116 new DenyListEntry(USB_VENDORID_SONY, 117 USB_PRODUCTID_PS5CONTROLLER, 118 USB_DENYLIST_OUTPUT)); 119 isDeviceDenylisted(int vendorId, int productId, int flags)120 private static boolean isDeviceDenylisted(int vendorId, int productId, int flags) { 121 for (DenyListEntry entry : sDeviceDenylist) { 122 if (entry.mVendorId == vendorId && entry.mProductId == productId) { 123 // see if the type flag is set 124 return (entry.mFlags & flags) != 0; 125 } 126 } 127 128 return false; 129 } 130 131 /** 132 * List of connected MIDI devices 133 */ 134 private final HashMap<String, UsbAlsaMidiDevice> 135 mMidiDevices = new HashMap<String, UsbAlsaMidiDevice>(); 136 137 // UsbAlsaMidiDevice for USB peripheral mode (gadget) device 138 private UsbAlsaMidiDevice mPeripheralMidiDevice = null; 139 140 private final HashSet<Integer> mAlsaCards = new HashSet<>(); 141 private final FileObserver mAlsaObserver = new FileObserver(new File(ALSA_DIRECTORY), 142 FileObserver.CREATE | FileObserver.DELETE) { 143 public void onEvent(int event, String path) { 144 switch (event) { 145 case FileObserver.CREATE: 146 alsaFileAdded(path); 147 break; 148 case FileObserver.DELETE: 149 alsaFileRemoved(path); 150 break; 151 } 152 } 153 }; 154 UsbAlsaManager(Context context)155 /* package */ UsbAlsaManager(Context context) { 156 mContext = context; 157 mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); 158 } 159 systemReady()160 public void systemReady() { 161 mAudioService = IAudioService.Stub.asInterface( 162 ServiceManager.getService(Context.AUDIO_SERVICE)); 163 mAlsaObserver.startWatching(); 164 } 165 166 /** 167 * Select the AlsaDevice to be used for AudioService. 168 * AlsaDevice.start() notifies AudioService of it's connected state. 169 * 170 * @param alsaDevice The selected UsbAlsaDevice for system USB audio. 171 */ selectAlsaDevice(UsbAlsaDevice alsaDevice)172 private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) { 173 if (DEBUG) { 174 Slog.d(TAG, "selectAlsaDevice() " + alsaDevice); 175 } 176 177 // FIXME Does not yet handle the case where the setting is changed 178 // after device connection. Ideally we should handle the settings change 179 // in SettingsObserver. Here we should log that a USB device is connected 180 // and disconnected with its address (card , device) and force the 181 // connection or disconnection when the setting changes. 182 int isDisabled = Settings.Secure.getInt(mContext.getContentResolver(), 183 Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, 0); 184 if (isDisabled != 0) { 185 return; 186 } 187 188 alsaDevice.start(); 189 190 if (DEBUG) { 191 Slog.d(TAG, "selectAlsaDevice() - done."); 192 } 193 } 194 deselectAlsaDevice(UsbAlsaDevice selectedDevice)195 private synchronized void deselectAlsaDevice(UsbAlsaDevice selectedDevice) { 196 if (DEBUG) { 197 Slog.d(TAG, "deselectAlsaDevice() selectedDevice " + selectedDevice); 198 } 199 selectedDevice.stop(); 200 } 201 getAlsaDeviceListIndexFor(String deviceAddress)202 private int getAlsaDeviceListIndexFor(String deviceAddress) { 203 for (int index = 0; index < mAlsaDevices.size(); index++) { 204 if (mAlsaDevices.get(index).getDeviceAddress().equals(deviceAddress)) { 205 return index; 206 } 207 } 208 return -1; 209 } 210 addDeviceToAttachedDevicesMap(int deviceType, UsbAlsaDevice device)211 private void addDeviceToAttachedDevicesMap(int deviceType, UsbAlsaDevice device) { 212 if (deviceType == AudioManager.DEVICE_NONE) { 213 Slog.i(TAG, "Ignore caching device as the type is NONE, device=" + device); 214 return; 215 } 216 Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); 217 if (devices == null) { 218 mAttachedDevices.put(deviceType, new Stack<>()); 219 devices = mAttachedDevices.get(deviceType); 220 } 221 devices.push(device); 222 } 223 addAlsaDevice(UsbAlsaDevice device)224 private void addAlsaDevice(UsbAlsaDevice device) { 225 mAlsaDevices.add(0, device); 226 addDeviceToAttachedDevicesMap(device.getInputDeviceType(), device); 227 addDeviceToAttachedDevicesMap(device.getOutputDeviceType(), device); 228 } 229 removeDeviceFromAttachedDevicesMap(int deviceType, UsbAlsaDevice device)230 private void removeDeviceFromAttachedDevicesMap(int deviceType, UsbAlsaDevice device) { 231 Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); 232 if (devices == null) { 233 return; 234 } 235 devices.remove(device); 236 if (devices.isEmpty()) { 237 mAttachedDevices.remove(deviceType); 238 } 239 } 240 removeAlsaDevice(String deviceAddress)241 private UsbAlsaDevice removeAlsaDevice(String deviceAddress) { 242 int index = getAlsaDeviceListIndexFor(deviceAddress); 243 if (index > -1) { 244 UsbAlsaDevice device = mAlsaDevices.remove(index); 245 removeDeviceFromAttachedDevicesMap(device.getOutputDeviceType(), device); 246 removeDeviceFromAttachedDevicesMap(device.getInputDeviceType(), device); 247 return device; 248 } else { 249 return null; 250 } 251 } 252 selectDefaultDevice(int deviceType)253 private UsbAlsaDevice selectDefaultDevice(int deviceType) { 254 if (DEBUG) { 255 Slog.d(TAG, "selectDefaultDevice():" + deviceType); 256 } 257 258 Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); 259 if (devices == null || devices.isEmpty()) { 260 return null; 261 } 262 UsbAlsaDevice alsaDevice = devices.peek(); 263 Slog.d(TAG, "select default device:" + alsaDevice); 264 if (AudioManager.isInputDevice(deviceType)) { 265 alsaDevice.startInput(); 266 } else { 267 alsaDevice.startOutput(); 268 } 269 return alsaDevice; 270 } 271 deselectCurrentDevice(int deviceType)272 private void deselectCurrentDevice(int deviceType) { 273 if (DEBUG) { 274 Slog.d(TAG, "deselectCurrentDevice():" + deviceType); 275 } 276 if (deviceType == AudioManager.DEVICE_NONE) { 277 return; 278 } 279 280 Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); 281 if (devices == null || devices.isEmpty()) { 282 return; 283 } 284 UsbAlsaDevice alsaDevice = devices.peek(); 285 Slog.d(TAG, "deselect current device:" + alsaDevice); 286 if (AudioManager.isInputDevice(deviceType)) { 287 alsaDevice.stopInput(); 288 } else { 289 alsaDevice.stopOutput(); 290 } 291 } 292 usbDeviceAdded(String deviceAddress, UsbDevice usbDevice, UsbDescriptorParser parser)293 /* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice, 294 UsbDescriptorParser parser) { 295 if (DEBUG) { 296 Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName() 297 + " nm:" + usbDevice.getProductName()); 298 } 299 300 // Scan the Alsa File Space 301 mCardsParser.scan(); 302 303 // Find the ALSA spec for this device address 304 AlsaCardsParser.AlsaCardRecord cardRec = 305 mCardsParser.findCardNumFor(deviceAddress); 306 if (cardRec == null) { 307 if (parser.hasAudioInterface()) { 308 Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress); 309 } 310 return; 311 } 312 313 waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/); 314 315 // Add it to the devices list 316 boolean hasInput = parser.hasInput() 317 && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), 318 USB_DENYLIST_INPUT); 319 boolean hasOutput = parser.hasOutput() 320 && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), 321 USB_DENYLIST_OUTPUT); 322 if (DEBUG) { 323 Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput); 324 } 325 if (hasInput || hasOutput) { 326 boolean isInputHeadset = parser.isInputHeadset(); 327 boolean isOutputHeadset = parser.isOutputHeadset(); 328 boolean isDock = parser.isDock(); 329 330 if (mAudioService == null) { 331 Slog.e(TAG, "no AudioService"); 332 return; 333 } 334 335 UsbAlsaDevice alsaDevice = 336 new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/, 337 deviceAddress, hasOutput, hasInput, 338 isInputHeadset, isOutputHeadset, isDock); 339 alsaDevice.setDeviceNameAndDescription( 340 cardRec.getCardName(), cardRec.getCardDescription()); 341 if (IS_MULTI_MODE) { 342 deselectCurrentDevice(alsaDevice.getInputDeviceType()); 343 deselectCurrentDevice(alsaDevice.getOutputDeviceType()); 344 } else { 345 // At single mode, the first device is the selected device. 346 if (!mAlsaDevices.isEmpty()) { 347 deselectAlsaDevice(mAlsaDevices.get(0)); 348 } 349 } 350 addAlsaDevice(alsaDevice); 351 selectAlsaDevice(alsaDevice); 352 } 353 354 addMidiDevice(deviceAddress, usbDevice, parser, cardRec); 355 356 logDevices("deviceAdded()"); 357 358 if (DEBUG) { 359 Slog.d(TAG, "deviceAdded() - done"); 360 } 361 } 362 addMidiDevice(String deviceAddress, UsbDevice usbDevice, UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec)363 private void addMidiDevice(String deviceAddress, UsbDevice usbDevice, 364 UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec) { 365 boolean hasMidi = parser.hasMIDIInterface(); 366 // UsbHostManager will create UsbDirectMidiDevices instead if MIDI 2 is supported. 367 boolean hasMidi2 = parser.containsUniversalMidiDeviceEndpoint(); 368 if (DEBUG) { 369 Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); 370 Slog.d(TAG, "hasMidi2: " + hasMidi2); 371 } 372 if (mHasMidiFeature && hasMidi && !hasMidi2) { 373 Bundle properties = new Bundle(); 374 String manufacturer = usbDevice.getManufacturerName(); 375 String product = usbDevice.getProductName(); 376 String version = usbDevice.getVersion(); 377 String name; 378 if (manufacturer == null || manufacturer.isEmpty()) { 379 name = product; 380 } else if (product == null || product.isEmpty()) { 381 name = manufacturer; 382 } else { 383 name = manufacturer + " " + product; 384 } 385 properties.putString(MidiDeviceInfo.PROPERTY_NAME, name); 386 properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer); 387 properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product); 388 properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version); 389 properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, 390 usbDevice.getSerialNumber()); 391 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum()); 392 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/); 393 properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); 394 395 int numLegacyMidiInputs = parser.calculateNumLegacyMidiInputs(); 396 int numLegacyMidiOutputs = parser.calculateNumLegacyMidiOutputs(); 397 if (DEBUG) { 398 Slog.d(TAG, "numLegacyMidiInputs: " + numLegacyMidiInputs); 399 Slog.d(TAG, "numLegacyMidiOutputs:" + numLegacyMidiOutputs); 400 } 401 402 UsbAlsaMidiDevice midiDevice = UsbAlsaMidiDevice.create(mContext, properties, 403 cardRec.getCardNum(), 0 /*device*/, numLegacyMidiInputs, 404 numLegacyMidiOutputs); 405 if (midiDevice != null) { 406 mMidiDevices.put(deviceAddress, midiDevice); 407 } 408 } 409 } 410 usbDeviceRemoved(String deviceAddress )411 /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { 412 if (DEBUG) { 413 Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")"); 414 } 415 416 // Audio 417 UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress); 418 Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); 419 if (alsaDevice != null) { 420 waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/); 421 deselectAlsaDevice(alsaDevice); 422 if (IS_MULTI_MODE) { 423 selectDefaultDevice(alsaDevice.getOutputDeviceType()); 424 selectDefaultDevice(alsaDevice.getInputDeviceType()); 425 } else { 426 // If there are any external devices left, select the latest attached one 427 if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) { 428 selectAlsaDevice(mAlsaDevices.get(0)); 429 } 430 } 431 } 432 433 // MIDI 434 UsbAlsaMidiDevice midiDevice = mMidiDevices.remove(deviceAddress); 435 if (midiDevice != null) { 436 Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress); 437 IoUtils.closeQuietly(midiDevice); 438 } 439 440 logDevices("usbDeviceRemoved()"); 441 } 442 setPeripheralMidiState(boolean enabled, int card, int device)443 /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) { 444 if (!mHasMidiFeature) { 445 return; 446 } 447 448 if (enabled && mPeripheralMidiDevice == null) { 449 Bundle properties = new Bundle(); 450 Resources r = mContext.getResources(); 451 properties.putString(MidiDeviceInfo.PROPERTY_NAME, r.getString( 452 com.android.internal.R.string.usb_midi_peripheral_name)); 453 properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString( 454 com.android.internal.R.string.usb_midi_peripheral_manufacturer_name)); 455 properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, r.getString( 456 com.android.internal.R.string.usb_midi_peripheral_product_name)); 457 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); 458 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); 459 mPeripheralMidiDevice = UsbAlsaMidiDevice.create(mContext, properties, card, device, 460 1 /* numInputs */, 1 /* numOutputs */); 461 } else if (!enabled && mPeripheralMidiDevice != null) { 462 IoUtils.closeQuietly(mPeripheralMidiDevice); 463 mPeripheralMidiDevice = null; 464 } 465 } 466 waitForAlsaDevice(int card, boolean isAdded)467 private boolean waitForAlsaDevice(int card, boolean isAdded) { 468 if (DEBUG) { 469 Slog.e(TAG, "waitForAlsaDevice(c:" + card + ")"); 470 } 471 472 // This value was empirically determined. 473 final int kWaitTimeMs = 2500; 474 475 synchronized (mAlsaCards) { 476 long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs; 477 while ((isAdded ^ mAlsaCards.contains(card)) 478 && timeoutMs > SystemClock.elapsedRealtime()) { 479 long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime(); 480 if (waitTimeMs > 0) { 481 try { 482 mAlsaCards.wait(waitTimeMs); 483 } catch (InterruptedException e) { 484 Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file."); 485 } 486 } 487 } 488 final boolean cardFound = mAlsaCards.contains(card); 489 if ((isAdded ^ cardFound) && timeoutMs > SystemClock.elapsedRealtime()) { 490 Slog.e(TAG, "waitForAlsaDevice(" + card + ") timeout"); 491 } else { 492 Slog.i(TAG, "waitForAlsaDevice for device card=" + card + ", isAdded=" + isAdded 493 + ", found=" + cardFound); 494 } 495 return cardFound; 496 } 497 } 498 getCardNumberFromAlsaFilePath(String path)499 private int getCardNumberFromAlsaFilePath(String path) { 500 int type = ALSA_DEVICE_TYPE_UNKNOWN; 501 if (path.startsWith("pcmC")) { 502 if (path.endsWith("p")) { 503 type = ALSA_DEVICE_TYPE_PLAYBACK; 504 } else if (path.endsWith("c")) { 505 type = ALSA_DEVICE_TYPE_CAPTURE; 506 } 507 } else if (path.startsWith("midiC")) { 508 type = ALSA_DEVICE_TYPE_MIDI; 509 } 510 511 if (type == ALSA_DEVICE_TYPE_UNKNOWN) { 512 Slog.i(TAG, "Unknown type file(" + path + ") added."); 513 return -1; 514 } 515 try { 516 int c_index = path.indexOf('C'); 517 int d_index = path.indexOf('D'); 518 return Integer.parseInt(path.substring(c_index + 1, d_index)); 519 } catch (Exception e) { 520 Slog.e(TAG, "Could not parse ALSA file name " + path, e); 521 return -1; 522 } 523 } 524 alsaFileAdded(String path)525 private void alsaFileAdded(String path) { 526 Slog.i(TAG, "alsaFileAdded(" + path + ")"); 527 final int card = getCardNumberFromAlsaFilePath(path); 528 if (card == -1) { 529 return; 530 } 531 synchronized (mAlsaCards) { 532 if (!mAlsaCards.contains(card)) { 533 Slog.d(TAG, "Adding ALSA device card=" + card); 534 mAlsaCards.add(card); 535 mAlsaCards.notifyAll(); 536 } 537 } 538 } 539 alsaFileRemoved(String path)540 private void alsaFileRemoved(String path) { 541 final int card = getCardNumberFromAlsaFilePath(path); 542 if (card == -1) { 543 return; 544 } 545 synchronized (mAlsaCards) { 546 mAlsaCards.remove(card); 547 } 548 } 549 550 // 551 // Devices List 552 // 553 /* 554 //import java.util.ArrayList; 555 public ArrayList<UsbAudioDevice> getConnectedDevices() { 556 ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size()); 557 for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) { 558 devices.add(entry.getValue()); 559 } 560 return devices; 561 } 562 */ 563 564 /** 565 * Dump the USB alsa state. 566 */ 567 // invoked with "adb shell dumpsys usb" dump(DualDumpOutputStream dump, String idName, long id)568 public void dump(DualDumpOutputStream dump, String idName, long id) { 569 long token = dump.start(idName, id); 570 571 dump.write("cards_parser", UsbAlsaManagerProto.CARDS_PARSER, mCardsParser.getScanStatus()); 572 573 for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) { 574 usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES); 575 } 576 577 for (String deviceAddr : mMidiDevices.keySet()) { 578 // A UsbAlsaMidiDevice does not have a handle to the UsbDevice anymore 579 mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "alsa_midi_devices", 580 UsbAlsaManagerProto.ALSA_MIDI_DEVICES); 581 } 582 583 dump.end(token); 584 } 585 logDevicesList(String title)586 public void logDevicesList(String title) { 587 if (DEBUG) { 588 Slog.i(TAG, title + "----------------"); 589 for (UsbAlsaDevice alsaDevice : mAlsaDevices) { 590 Slog.i(TAG, " -->"); 591 Slog.i(TAG, "" + alsaDevice); 592 Slog.i(TAG, " <--"); 593 } 594 Slog.i(TAG, "----------------"); 595 } 596 } 597 598 // This logs a more terse (and more readable) version of the devices list logDevices(String title)599 public void logDevices(String title) { 600 if (DEBUG) { 601 Slog.i(TAG, title + "----------------"); 602 for (UsbAlsaDevice alsaDevice : mAlsaDevices) { 603 Slog.i(TAG, alsaDevice.toShortString()); 604 } 605 Slog.i(TAG, "----------------"); 606 } 607 } 608 } 609