| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.usb; |
| |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.hardware.usb.UsbDevice; |
| import android.media.AudioManager; |
| import android.media.IAudioService; |
| import android.media.midi.MidiDeviceInfo; |
| import android.os.Bundle; |
| import android.os.FileObserver; |
| import android.os.ServiceManager; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.provider.Settings; |
| import android.service.usb.UsbAlsaManagerProto; |
| import android.util.Slog; |
| |
| import com.android.internal.alsa.AlsaCardsParser; |
| import com.android.internal.util.dump.DualDumpOutputStream; |
| import com.android.server.usb.descriptors.UsbDescriptorParser; |
| |
| import libcore.io.IoUtils; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Stack; |
| |
| /** |
| * UsbAlsaManager manages USB audio and MIDI devices. |
| */ |
| public final class UsbAlsaManager { |
| private static final String TAG = UsbAlsaManager.class.getSimpleName(); |
| private static final boolean DEBUG = false; |
| |
| // Flag to turn on/off multi-peripheral select mode |
| // Set to true to have multi-devices mode |
| private static final boolean IS_MULTI_MODE = SystemProperties.getBoolean( |
| "ro.audio.multi_usb_mode", false /*def*/); |
| |
| private static final String ALSA_DIRECTORY = "/dev/snd/"; |
| |
| private static final int ALSA_DEVICE_TYPE_UNKNOWN = 0; |
| private static final int ALSA_DEVICE_TYPE_PLAYBACK = 1; |
| private static final int ALSA_DEVICE_TYPE_CAPTURE = 2; |
| private static final int ALSA_DEVICE_TYPE_MIDI = 3; |
| |
| private final Context mContext; |
| private IAudioService mAudioService; |
| private final boolean mHasMidiFeature; |
| |
| private final AlsaCardsParser mCardsParser = new AlsaCardsParser(); |
| |
| // this is needed to map USB devices to ALSA Audio Devices, especially to remove an |
| // ALSA device when we are notified that its associated USB device has been removed. |
| private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>(); |
| // A map from device type to attached devices. Given the audio framework only supports |
| // single device connection per device type, only the last attached device will be |
| // connected to audio framework. Once the last device is removed, previous device can |
| // be connected to audio framework. |
| private HashMap<Integer, Stack<UsbAlsaDevice>> mAttachedDevices = new HashMap<>(); |
| |
| // |
| // Device Denylist |
| // |
| // This exists due to problems with Sony game controllers which present as an audio device |
| // even if no headset is connected and have no way to set the volume on the unit. |
| // Handle this by simply declining to use them as an audio device. |
| private static final int USB_VENDORID_SONY = 0x054C; |
| private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT1 = 0x05C4; |
| private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT2 = 0x09CC; |
| private static final int USB_PRODUCTID_PS5CONTROLLER = 0x0CE6; |
| |
| private static final int USB_DENYLIST_OUTPUT = 0x0001; |
| private static final int USB_DENYLIST_INPUT = 0x0002; |
| |
| private static class DenyListEntry { |
| final int mVendorId; |
| final int mProductId; |
| final int mFlags; |
| |
| DenyListEntry(int vendorId, int productId, int flags) { |
| mVendorId = vendorId; |
| mProductId = productId; |
| mFlags = flags; |
| } |
| } |
| |
| static final List<DenyListEntry> sDeviceDenylist = Arrays.asList( |
| new DenyListEntry(USB_VENDORID_SONY, |
| USB_PRODUCTID_PS4CONTROLLER_ZCT1, |
| USB_DENYLIST_OUTPUT), |
| new DenyListEntry(USB_VENDORID_SONY, |
| USB_PRODUCTID_PS4CONTROLLER_ZCT2, |
| USB_DENYLIST_OUTPUT), |
| new DenyListEntry(USB_VENDORID_SONY, |
| USB_PRODUCTID_PS5CONTROLLER, |
| USB_DENYLIST_OUTPUT)); |
| |
| private static boolean isDeviceDenylisted(int vendorId, int productId, int flags) { |
| for (DenyListEntry entry : sDeviceDenylist) { |
| if (entry.mVendorId == vendorId && entry.mProductId == productId) { |
| // see if the type flag is set |
| return (entry.mFlags & flags) != 0; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * List of connected MIDI devices |
| */ |
| private final HashMap<String, UsbAlsaMidiDevice> |
| mMidiDevices = new HashMap<String, UsbAlsaMidiDevice>(); |
| |
| // UsbAlsaMidiDevice for USB peripheral mode (gadget) device |
| private UsbAlsaMidiDevice mPeripheralMidiDevice = null; |
| |
| private final HashSet<Integer> mAlsaCards = new HashSet<>(); |
| private final FileObserver mAlsaObserver = new FileObserver(new File(ALSA_DIRECTORY), |
| FileObserver.CREATE | FileObserver.DELETE) { |
| public void onEvent(int event, String path) { |
| switch (event) { |
| case FileObserver.CREATE: |
| alsaFileAdded(path); |
| break; |
| case FileObserver.DELETE: |
| alsaFileRemoved(path); |
| break; |
| } |
| } |
| }; |
| |
| /* package */ UsbAlsaManager(Context context) { |
| mContext = context; |
| mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); |
| } |
| |
| public void systemReady() { |
| mAudioService = IAudioService.Stub.asInterface( |
| ServiceManager.getService(Context.AUDIO_SERVICE)); |
| mAlsaObserver.startWatching(); |
| } |
| |
| /** |
| * Select the AlsaDevice to be used for AudioService. |
| * AlsaDevice.start() notifies AudioService of it's connected state. |
| * |
| * @param alsaDevice The selected UsbAlsaDevice for system USB audio. |
| */ |
| private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) { |
| if (DEBUG) { |
| Slog.d(TAG, "selectAlsaDevice() " + alsaDevice); |
| } |
| |
| // FIXME Does not yet handle the case where the setting is changed |
| // after device connection. Ideally we should handle the settings change |
| // in SettingsObserver. Here we should log that a USB device is connected |
| // and disconnected with its address (card , device) and force the |
| // connection or disconnection when the setting changes. |
| int isDisabled = Settings.Secure.getInt(mContext.getContentResolver(), |
| Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, 0); |
| if (isDisabled != 0) { |
| return; |
| } |
| |
| alsaDevice.start(); |
| |
| if (DEBUG) { |
| Slog.d(TAG, "selectAlsaDevice() - done."); |
| } |
| } |
| |
| private synchronized void deselectAlsaDevice(UsbAlsaDevice selectedDevice) { |
| if (DEBUG) { |
| Slog.d(TAG, "deselectAlsaDevice() selectedDevice " + selectedDevice); |
| } |
| selectedDevice.stop(); |
| } |
| |
| private int getAlsaDeviceListIndexFor(String deviceAddress) { |
| for (int index = 0; index < mAlsaDevices.size(); index++) { |
| if (mAlsaDevices.get(index).getDeviceAddress().equals(deviceAddress)) { |
| return index; |
| } |
| } |
| return -1; |
| } |
| |
| private void addDeviceToAttachedDevicesMap(int deviceType, UsbAlsaDevice device) { |
| if (deviceType == AudioManager.DEVICE_NONE) { |
| Slog.i(TAG, "Ignore caching device as the type is NONE, device=" + device); |
| return; |
| } |
| Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); |
| if (devices == null) { |
| mAttachedDevices.put(deviceType, new Stack<>()); |
| devices = mAttachedDevices.get(deviceType); |
| } |
| devices.push(device); |
| } |
| |
| private void addAlsaDevice(UsbAlsaDevice device) { |
| mAlsaDevices.add(0, device); |
| addDeviceToAttachedDevicesMap(device.getInputDeviceType(), device); |
| addDeviceToAttachedDevicesMap(device.getOutputDeviceType(), device); |
| } |
| |
| private void removeDeviceFromAttachedDevicesMap(int deviceType, UsbAlsaDevice device) { |
| Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); |
| if (devices == null) { |
| return; |
| } |
| devices.remove(device); |
| if (devices.isEmpty()) { |
| mAttachedDevices.remove(deviceType); |
| } |
| } |
| |
| private UsbAlsaDevice removeAlsaDevice(String deviceAddress) { |
| int index = getAlsaDeviceListIndexFor(deviceAddress); |
| if (index > -1) { |
| UsbAlsaDevice device = mAlsaDevices.remove(index); |
| removeDeviceFromAttachedDevicesMap(device.getOutputDeviceType(), device); |
| removeDeviceFromAttachedDevicesMap(device.getInputDeviceType(), device); |
| return device; |
| } else { |
| return null; |
| } |
| } |
| |
| private UsbAlsaDevice selectDefaultDevice(int deviceType) { |
| if (DEBUG) { |
| Slog.d(TAG, "selectDefaultDevice():" + deviceType); |
| } |
| |
| Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); |
| if (devices == null || devices.isEmpty()) { |
| return null; |
| } |
| UsbAlsaDevice alsaDevice = devices.peek(); |
| Slog.d(TAG, "select default device:" + alsaDevice); |
| if (AudioManager.isInputDevice(deviceType)) { |
| alsaDevice.startInput(); |
| } else { |
| alsaDevice.startOutput(); |
| } |
| return alsaDevice; |
| } |
| |
| private void deselectCurrentDevice(int deviceType) { |
| if (DEBUG) { |
| Slog.d(TAG, "deselectCurrentDevice():" + deviceType); |
| } |
| if (deviceType == AudioManager.DEVICE_NONE) { |
| return; |
| } |
| |
| Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType); |
| if (devices == null || devices.isEmpty()) { |
| return; |
| } |
| UsbAlsaDevice alsaDevice = devices.peek(); |
| Slog.d(TAG, "deselect current device:" + alsaDevice); |
| if (AudioManager.isInputDevice(deviceType)) { |
| alsaDevice.stopInput(); |
| } else { |
| alsaDevice.stopOutput(); |
| } |
| } |
| |
| /* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice, |
| UsbDescriptorParser parser) { |
| if (DEBUG) { |
| Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName() |
| + " nm:" + usbDevice.getProductName()); |
| } |
| |
| // Scan the Alsa File Space |
| mCardsParser.scan(); |
| |
| // Find the ALSA spec for this device address |
| AlsaCardsParser.AlsaCardRecord cardRec = |
| mCardsParser.findCardNumFor(deviceAddress); |
| if (cardRec == null) { |
| Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress); |
| return; |
| } |
| |
| waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/); |
| |
| // Add it to the devices list |
| boolean hasInput = parser.hasInput() |
| && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), |
| USB_DENYLIST_INPUT); |
| boolean hasOutput = parser.hasOutput() |
| && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), |
| USB_DENYLIST_OUTPUT); |
| if (DEBUG) { |
| Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput); |
| } |
| if (hasInput || hasOutput) { |
| boolean isInputHeadset = parser.isInputHeadset(); |
| boolean isOutputHeadset = parser.isOutputHeadset(); |
| boolean isDock = parser.isDock(); |
| |
| if (mAudioService == null) { |
| Slog.e(TAG, "no AudioService"); |
| return; |
| } |
| |
| UsbAlsaDevice alsaDevice = |
| new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/, |
| deviceAddress, hasOutput, hasInput, |
| isInputHeadset, isOutputHeadset, isDock); |
| alsaDevice.setDeviceNameAndDescription( |
| cardRec.getCardName(), cardRec.getCardDescription()); |
| if (IS_MULTI_MODE) { |
| deselectCurrentDevice(alsaDevice.getInputDeviceType()); |
| deselectCurrentDevice(alsaDevice.getOutputDeviceType()); |
| } else { |
| // At single mode, the first device is the selected device. |
| if (!mAlsaDevices.isEmpty()) { |
| deselectAlsaDevice(mAlsaDevices.get(0)); |
| } |
| } |
| addAlsaDevice(alsaDevice); |
| selectAlsaDevice(alsaDevice); |
| } |
| |
| addMidiDevice(deviceAddress, usbDevice, parser, cardRec); |
| |
| logDevices("deviceAdded()"); |
| |
| if (DEBUG) { |
| Slog.d(TAG, "deviceAdded() - done"); |
| } |
| } |
| |
| private void addMidiDevice(String deviceAddress, UsbDevice usbDevice, |
| UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec) { |
| boolean hasMidi = parser.hasMIDIInterface(); |
| // UsbHostManager will create UsbDirectMidiDevices instead if MIDI 2 is supported. |
| boolean hasMidi2 = parser.containsUniversalMidiDeviceEndpoint(); |
| if (DEBUG) { |
| Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); |
| Slog.d(TAG, "hasMidi2: " + hasMidi2); |
| } |
| if (mHasMidiFeature && hasMidi && !hasMidi2) { |
| Bundle properties = new Bundle(); |
| String manufacturer = usbDevice.getManufacturerName(); |
| String product = usbDevice.getProductName(); |
| String version = usbDevice.getVersion(); |
| String name; |
| if (manufacturer == null || manufacturer.isEmpty()) { |
| name = product; |
| } else if (product == null || product.isEmpty()) { |
| name = manufacturer; |
| } else { |
| name = manufacturer + " " + product; |
| } |
| properties.putString(MidiDeviceInfo.PROPERTY_NAME, name); |
| properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer); |
| properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product); |
| properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version); |
| properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, |
| usbDevice.getSerialNumber()); |
| properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum()); |
| properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/); |
| properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); |
| |
| int numLegacyMidiInputs = parser.calculateNumLegacyMidiInputs(); |
| int numLegacyMidiOutputs = parser.calculateNumLegacyMidiOutputs(); |
| if (DEBUG) { |
| Slog.d(TAG, "numLegacyMidiInputs: " + numLegacyMidiInputs); |
| Slog.d(TAG, "numLegacyMidiOutputs:" + numLegacyMidiOutputs); |
| } |
| |
| UsbAlsaMidiDevice midiDevice = UsbAlsaMidiDevice.create(mContext, properties, |
| cardRec.getCardNum(), 0 /*device*/, numLegacyMidiInputs, |
| numLegacyMidiOutputs); |
| if (midiDevice != null) { |
| mMidiDevices.put(deviceAddress, midiDevice); |
| } |
| } |
| } |
| |
| /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { |
| if (DEBUG) { |
| Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")"); |
| } |
| |
| // Audio |
| UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress); |
| Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); |
| if (alsaDevice != null) { |
| waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/); |
| deselectAlsaDevice(alsaDevice); |
| if (IS_MULTI_MODE) { |
| selectDefaultDevice(alsaDevice.getOutputDeviceType()); |
| selectDefaultDevice(alsaDevice.getInputDeviceType()); |
| } else { |
| // If there are any external devices left, select the latest attached one |
| if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) { |
| selectAlsaDevice(mAlsaDevices.get(0)); |
| } |
| } |
| } |
| |
| // MIDI |
| UsbAlsaMidiDevice midiDevice = mMidiDevices.remove(deviceAddress); |
| if (midiDevice != null) { |
| Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress); |
| IoUtils.closeQuietly(midiDevice); |
| } |
| |
| logDevices("usbDeviceRemoved()"); |
| } |
| |
| /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) { |
| if (!mHasMidiFeature) { |
| return; |
| } |
| |
| if (enabled && mPeripheralMidiDevice == null) { |
| Bundle properties = new Bundle(); |
| Resources r = mContext.getResources(); |
| properties.putString(MidiDeviceInfo.PROPERTY_NAME, r.getString( |
| com.android.internal.R.string.usb_midi_peripheral_name)); |
| properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString( |
| com.android.internal.R.string.usb_midi_peripheral_manufacturer_name)); |
| properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, r.getString( |
| com.android.internal.R.string.usb_midi_peripheral_product_name)); |
| properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); |
| properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); |
| mPeripheralMidiDevice = UsbAlsaMidiDevice.create(mContext, properties, card, device, |
| 1 /* numInputs */, 1 /* numOutputs */); |
| } else if (!enabled && mPeripheralMidiDevice != null) { |
| IoUtils.closeQuietly(mPeripheralMidiDevice); |
| mPeripheralMidiDevice = null; |
| } |
| } |
| |
| private boolean waitForAlsaDevice(int card, boolean isAdded) { |
| if (DEBUG) { |
| Slog.e(TAG, "waitForAlsaDevice(c:" + card + ")"); |
| } |
| |
| // This value was empirically determined. |
| final int kWaitTimeMs = 2500; |
| |
| synchronized (mAlsaCards) { |
| long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs; |
| while ((isAdded ^ mAlsaCards.contains(card)) |
| && timeoutMs > SystemClock.elapsedRealtime()) { |
| long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime(); |
| if (waitTimeMs > 0) { |
| try { |
| mAlsaCards.wait(waitTimeMs); |
| } catch (InterruptedException e) { |
| Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file."); |
| } |
| } |
| } |
| final boolean cardFound = mAlsaCards.contains(card); |
| if ((isAdded ^ cardFound) && timeoutMs > SystemClock.elapsedRealtime()) { |
| Slog.e(TAG, "waitForAlsaDevice(" + card + ") timeout"); |
| } else { |
| Slog.i(TAG, "waitForAlsaDevice for device card=" + card + ", isAdded=" + isAdded |
| + ", found=" + cardFound); |
| } |
| return cardFound; |
| } |
| } |
| |
| private int getCardNumberFromAlsaFilePath(String path) { |
| int type = ALSA_DEVICE_TYPE_UNKNOWN; |
| if (path.startsWith("pcmC")) { |
| if (path.endsWith("p")) { |
| type = ALSA_DEVICE_TYPE_PLAYBACK; |
| } else if (path.endsWith("c")) { |
| type = ALSA_DEVICE_TYPE_CAPTURE; |
| } |
| } else if (path.startsWith("midiC")) { |
| type = ALSA_DEVICE_TYPE_MIDI; |
| } |
| |
| if (type == ALSA_DEVICE_TYPE_UNKNOWN) { |
| Slog.i(TAG, "Unknown type file(" + path + ") added."); |
| return -1; |
| } |
| try { |
| int c_index = path.indexOf('C'); |
| int d_index = path.indexOf('D'); |
| return Integer.parseInt(path.substring(c_index + 1, d_index)); |
| } catch (Exception e) { |
| Slog.e(TAG, "Could not parse ALSA file name " + path, e); |
| return -1; |
| } |
| } |
| |
| private void alsaFileAdded(String path) { |
| Slog.i(TAG, "alsaFileAdded(" + path + ")"); |
| final int card = getCardNumberFromAlsaFilePath(path); |
| if (card == -1) { |
| return; |
| } |
| synchronized (mAlsaCards) { |
| if (!mAlsaCards.contains(card)) { |
| Slog.d(TAG, "Adding ALSA device card=" + card); |
| mAlsaCards.add(card); |
| mAlsaCards.notifyAll(); |
| } |
| } |
| } |
| |
| private void alsaFileRemoved(String path) { |
| final int card = getCardNumberFromAlsaFilePath(path); |
| if (card == -1) { |
| return; |
| } |
| synchronized (mAlsaCards) { |
| mAlsaCards.remove(card); |
| } |
| } |
| |
| // |
| // Devices List |
| // |
| /* |
| //import java.util.ArrayList; |
| public ArrayList<UsbAudioDevice> getConnectedDevices() { |
| ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size()); |
| for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) { |
| devices.add(entry.getValue()); |
| } |
| return devices; |
| } |
| */ |
| |
| /** |
| * Dump the USB alsa state. |
| */ |
| // invoked with "adb shell dumpsys usb" |
| public void dump(DualDumpOutputStream dump, String idName, long id) { |
| long token = dump.start(idName, id); |
| |
| dump.write("cards_parser", UsbAlsaManagerProto.CARDS_PARSER, mCardsParser.getScanStatus()); |
| |
| for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) { |
| usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES); |
| } |
| |
| for (String deviceAddr : mMidiDevices.keySet()) { |
| // A UsbAlsaMidiDevice does not have a handle to the UsbDevice anymore |
| mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "alsa_midi_devices", |
| UsbAlsaManagerProto.ALSA_MIDI_DEVICES); |
| } |
| |
| dump.end(token); |
| } |
| |
| public void logDevicesList(String title) { |
| if (DEBUG) { |
| Slog.i(TAG, title + "----------------"); |
| for (UsbAlsaDevice alsaDevice : mAlsaDevices) { |
| Slog.i(TAG, " -->"); |
| Slog.i(TAG, "" + alsaDevice); |
| Slog.i(TAG, " <--"); |
| } |
| Slog.i(TAG, "----------------"); |
| } |
| } |
| |
| // This logs a more terse (and more readable) version of the devices list |
| public void logDevices(String title) { |
| if (DEBUG) { |
| Slog.i(TAG, title + "----------------"); |
| for (UsbAlsaDevice alsaDevice : mAlsaDevices) { |
| Slog.i(TAG, alsaDevice.toShortString()); |
| } |
| Slog.i(TAG, "----------------"); |
| } |
| } |
| } |