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