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 Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress); 308 return; 309 } 310 311 waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/); 312 313 // Add it to the devices list 314 boolean hasInput = parser.hasInput() 315 && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), 316 USB_DENYLIST_INPUT); 317 boolean hasOutput = parser.hasOutput() 318 && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), 319 USB_DENYLIST_OUTPUT); 320 if (DEBUG) { 321 Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput); 322 } 323 if (hasInput || hasOutput) { 324 boolean isInputHeadset = parser.isInputHeadset(); 325 boolean isOutputHeadset = parser.isOutputHeadset(); 326 boolean isDock = parser.isDock(); 327 328 if (mAudioService == null) { 329 Slog.e(TAG, "no AudioService"); 330 return; 331 } 332 333 UsbAlsaDevice alsaDevice = 334 new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/, 335 deviceAddress, hasOutput, hasInput, 336 isInputHeadset, isOutputHeadset, isDock); 337 alsaDevice.setDeviceNameAndDescription( 338 cardRec.getCardName(), cardRec.getCardDescription()); 339 if (IS_MULTI_MODE) { 340 deselectCurrentDevice(alsaDevice.getInputDeviceType()); 341 deselectCurrentDevice(alsaDevice.getOutputDeviceType()); 342 } else { 343 // At single mode, the first device is the selected device. 344 if (!mAlsaDevices.isEmpty()) { 345 deselectAlsaDevice(mAlsaDevices.get(0)); 346 } 347 } 348 addAlsaDevice(alsaDevice); 349 selectAlsaDevice(alsaDevice); 350 } 351 352 addMidiDevice(deviceAddress, usbDevice, parser, cardRec); 353 354 logDevices("deviceAdded()"); 355 356 if (DEBUG) { 357 Slog.d(TAG, "deviceAdded() - done"); 358 } 359 } 360 addMidiDevice(String deviceAddress, UsbDevice usbDevice, UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec)361 private void addMidiDevice(String deviceAddress, UsbDevice usbDevice, 362 UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec) { 363 boolean hasMidi = parser.hasMIDIInterface(); 364 // UsbHostManager will create UsbDirectMidiDevices instead if MIDI 2 is supported. 365 boolean hasMidi2 = parser.containsUniversalMidiDeviceEndpoint(); 366 if (DEBUG) { 367 Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); 368 Slog.d(TAG, "hasMidi2: " + hasMidi2); 369 } 370 if (mHasMidiFeature && hasMidi && !hasMidi2) { 371 Bundle properties = new Bundle(); 372 String manufacturer = usbDevice.getManufacturerName(); 373 String product = usbDevice.getProductName(); 374 String version = usbDevice.getVersion(); 375 String name; 376 if (manufacturer == null || manufacturer.isEmpty()) { 377 name = product; 378 } else if (product == null || product.isEmpty()) { 379 name = manufacturer; 380 } else { 381 name = manufacturer + " " + product; 382 } 383 properties.putString(MidiDeviceInfo.PROPERTY_NAME, name); 384 properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer); 385 properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product); 386 properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version); 387 properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, 388 usbDevice.getSerialNumber()); 389 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum()); 390 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/); 391 properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); 392 393 int numLegacyMidiInputs = parser.calculateNumLegacyMidiInputs(); 394 int numLegacyMidiOutputs = parser.calculateNumLegacyMidiOutputs(); 395 if (DEBUG) { 396 Slog.d(TAG, "numLegacyMidiInputs: " + numLegacyMidiInputs); 397 Slog.d(TAG, "numLegacyMidiOutputs:" + numLegacyMidiOutputs); 398 } 399 400 UsbAlsaMidiDevice midiDevice = UsbAlsaMidiDevice.create(mContext, properties, 401 cardRec.getCardNum(), 0 /*device*/, numLegacyMidiInputs, 402 numLegacyMidiOutputs); 403 if (midiDevice != null) { 404 mMidiDevices.put(deviceAddress, midiDevice); 405 } 406 } 407 } 408 usbDeviceRemoved(String deviceAddress )409 /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { 410 if (DEBUG) { 411 Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")"); 412 } 413 414 // Audio 415 UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress); 416 Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); 417 if (alsaDevice != null) { 418 waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/); 419 deselectAlsaDevice(alsaDevice); 420 if (IS_MULTI_MODE) { 421 selectDefaultDevice(alsaDevice.getOutputDeviceType()); 422 selectDefaultDevice(alsaDevice.getInputDeviceType()); 423 } else { 424 // If there are any external devices left, select the latest attached one 425 if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) { 426 selectAlsaDevice(mAlsaDevices.get(0)); 427 } 428 } 429 } 430 431 // MIDI 432 UsbAlsaMidiDevice midiDevice = mMidiDevices.remove(deviceAddress); 433 if (midiDevice != null) { 434 Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress); 435 IoUtils.closeQuietly(midiDevice); 436 } 437 438 logDevices("usbDeviceRemoved()"); 439 } 440 setPeripheralMidiState(boolean enabled, int card, int device)441 /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) { 442 if (!mHasMidiFeature) { 443 return; 444 } 445 446 if (enabled && mPeripheralMidiDevice == null) { 447 Bundle properties = new Bundle(); 448 Resources r = mContext.getResources(); 449 properties.putString(MidiDeviceInfo.PROPERTY_NAME, r.getString( 450 com.android.internal.R.string.usb_midi_peripheral_name)); 451 properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString( 452 com.android.internal.R.string.usb_midi_peripheral_manufacturer_name)); 453 properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, r.getString( 454 com.android.internal.R.string.usb_midi_peripheral_product_name)); 455 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); 456 properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); 457 mPeripheralMidiDevice = UsbAlsaMidiDevice.create(mContext, properties, card, device, 458 1 /* numInputs */, 1 /* numOutputs */); 459 } else if (!enabled && mPeripheralMidiDevice != null) { 460 IoUtils.closeQuietly(mPeripheralMidiDevice); 461 mPeripheralMidiDevice = null; 462 } 463 } 464 waitForAlsaDevice(int card, boolean isAdded)465 private boolean waitForAlsaDevice(int card, boolean isAdded) { 466 if (DEBUG) { 467 Slog.e(TAG, "waitForAlsaDevice(c:" + card + ")"); 468 } 469 470 // This value was empirically determined. 471 final int kWaitTimeMs = 2500; 472 473 synchronized (mAlsaCards) { 474 long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs; 475 while ((isAdded ^ mAlsaCards.contains(card)) 476 && timeoutMs > SystemClock.elapsedRealtime()) { 477 long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime(); 478 if (waitTimeMs > 0) { 479 try { 480 mAlsaCards.wait(waitTimeMs); 481 } catch (InterruptedException e) { 482 Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file."); 483 } 484 } 485 } 486 final boolean cardFound = mAlsaCards.contains(card); 487 if ((isAdded ^ cardFound) && timeoutMs > SystemClock.elapsedRealtime()) { 488 Slog.e(TAG, "waitForAlsaDevice(" + card + ") timeout"); 489 } else { 490 Slog.i(TAG, "waitForAlsaDevice for device card=" + card + ", isAdded=" + isAdded 491 + ", found=" + cardFound); 492 } 493 return cardFound; 494 } 495 } 496 getCardNumberFromAlsaFilePath(String path)497 private int getCardNumberFromAlsaFilePath(String path) { 498 int type = ALSA_DEVICE_TYPE_UNKNOWN; 499 if (path.startsWith("pcmC")) { 500 if (path.endsWith("p")) { 501 type = ALSA_DEVICE_TYPE_PLAYBACK; 502 } else if (path.endsWith("c")) { 503 type = ALSA_DEVICE_TYPE_CAPTURE; 504 } 505 } else if (path.startsWith("midiC")) { 506 type = ALSA_DEVICE_TYPE_MIDI; 507 } 508 509 if (type == ALSA_DEVICE_TYPE_UNKNOWN) { 510 Slog.i(TAG, "Unknown type file(" + path + ") added."); 511 return -1; 512 } 513 try { 514 int c_index = path.indexOf('C'); 515 int d_index = path.indexOf('D'); 516 return Integer.parseInt(path.substring(c_index + 1, d_index)); 517 } catch (Exception e) { 518 Slog.e(TAG, "Could not parse ALSA file name " + path, e); 519 return -1; 520 } 521 } 522 alsaFileAdded(String path)523 private void alsaFileAdded(String path) { 524 Slog.i(TAG, "alsaFileAdded(" + path + ")"); 525 final int card = getCardNumberFromAlsaFilePath(path); 526 if (card == -1) { 527 return; 528 } 529 synchronized (mAlsaCards) { 530 if (!mAlsaCards.contains(card)) { 531 Slog.d(TAG, "Adding ALSA device card=" + card); 532 mAlsaCards.add(card); 533 mAlsaCards.notifyAll(); 534 } 535 } 536 } 537 alsaFileRemoved(String path)538 private void alsaFileRemoved(String path) { 539 final int card = getCardNumberFromAlsaFilePath(path); 540 if (card == -1) { 541 return; 542 } 543 synchronized (mAlsaCards) { 544 mAlsaCards.remove(card); 545 } 546 } 547 548 // 549 // Devices List 550 // 551 /* 552 //import java.util.ArrayList; 553 public ArrayList<UsbAudioDevice> getConnectedDevices() { 554 ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size()); 555 for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) { 556 devices.add(entry.getValue()); 557 } 558 return devices; 559 } 560 */ 561 562 /** 563 * Dump the USB alsa state. 564 */ 565 // invoked with "adb shell dumpsys usb" dump(DualDumpOutputStream dump, String idName, long id)566 public void dump(DualDumpOutputStream dump, String idName, long id) { 567 long token = dump.start(idName, id); 568 569 dump.write("cards_parser", UsbAlsaManagerProto.CARDS_PARSER, mCardsParser.getScanStatus()); 570 571 for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) { 572 usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES); 573 } 574 575 for (String deviceAddr : mMidiDevices.keySet()) { 576 // A UsbAlsaMidiDevice does not have a handle to the UsbDevice anymore 577 mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "alsa_midi_devices", 578 UsbAlsaManagerProto.ALSA_MIDI_DEVICES); 579 } 580 581 dump.end(token); 582 } 583 logDevicesList(String title)584 public void logDevicesList(String title) { 585 if (DEBUG) { 586 Slog.i(TAG, title + "----------------"); 587 for (UsbAlsaDevice alsaDevice : mAlsaDevices) { 588 Slog.i(TAG, " -->"); 589 Slog.i(TAG, "" + alsaDevice); 590 Slog.i(TAG, " <--"); 591 } 592 Slog.i(TAG, "----------------"); 593 } 594 } 595 596 // This logs a more terse (and more readable) version of the devices list logDevices(String title)597 public void logDevices(String title) { 598 if (DEBUG) { 599 Slog.i(TAG, title + "----------------"); 600 for (UsbAlsaDevice alsaDevice : mAlsaDevices) { 601 Slog.i(TAG, alsaDevice.toShortString()); 602 } 603 Slog.i(TAG, "----------------"); 604 } 605 } 606 } 607