Merge "Monitor alsa device files to notify USB devices connection."

This commit is contained in:
Jiabin Huang
2023-02-01 19:35:53 +00:00
committed by Android (Google) Code Review

View File

@@ -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;
@@ -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;
/**
@@ -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;
@@ -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);
@@ -124,6 +148,7 @@ public final class UsbAlsaManager {
public void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
mAlsaObserver.startWatching();
}
/**
@@ -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(),
@@ -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
}
@@ -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
//