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