Merge "Monitor alsa device files to notify USB devices connection."
This commit is contained in:
@@ -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
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user