Loading services/usb/java/com/android/server/usb/UsbAlsaManager.java +111 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.hardware.usb.UsbDevice; 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.provider.Settings; import android.service.usb.UsbAlsaManagerProto; import android.util.Slog; Loading @@ -34,9 +36,11 @@ 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; /** Loading @@ -52,6 +56,11 @@ public final class UsbAlsaManager { 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; Loading Loading @@ -116,6 +125,21 @@ public final class UsbAlsaManager { // UsbMidiDevice for USB peripheral mode (gadget) device private UsbMidiDevice 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); Loading @@ -124,6 +148,7 @@ public final class UsbAlsaManager { public void systemReady() { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mAlsaObserver.startWatching(); } /** Loading Loading @@ -224,6 +249,8 @@ public final class UsbAlsaManager { return; } waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/); // Add it to the devices list boolean hasInput = parser.hasInput() && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), Loading Loading @@ -322,6 +349,7 @@ public final class UsbAlsaManager { UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress); Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); if (alsaDevice != null && alsaDevice == mSelectedDevice) { waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/); deselectAlsaDevice(); selectDefaultDevice(); // if there any external devices left, select one of them } Loading Loading @@ -361,6 +389,89 @@ public final class UsbAlsaManager { } } 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 // Loading Loading
services/usb/java/com/android/server/usb/UsbAlsaManager.java +111 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.hardware.usb.UsbDevice; 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.provider.Settings; import android.service.usb.UsbAlsaManagerProto; import android.util.Slog; Loading @@ -34,9 +36,11 @@ 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; /** Loading @@ -52,6 +56,11 @@ public final class UsbAlsaManager { 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; Loading Loading @@ -116,6 +125,21 @@ public final class UsbAlsaManager { // UsbMidiDevice for USB peripheral mode (gadget) device private UsbMidiDevice 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); Loading @@ -124,6 +148,7 @@ public final class UsbAlsaManager { public void systemReady() { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mAlsaObserver.startWatching(); } /** Loading Loading @@ -224,6 +249,8 @@ public final class UsbAlsaManager { return; } waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/); // Add it to the devices list boolean hasInput = parser.hasInput() && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), Loading Loading @@ -322,6 +349,7 @@ public final class UsbAlsaManager { UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress); Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); if (alsaDevice != null && alsaDevice == mSelectedDevice) { waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/); deselectAlsaDevice(); selectDefaultDevice(); // if there any external devices left, select one of them } Loading Loading @@ -361,6 +389,89 @@ public final class UsbAlsaManager { } } 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 // Loading