Optimize/streamline USB audio (dis)connect

Map from usb device "address" to ALSA card num.
Remove Alsa filespace monitor.

Bug: 72435641

Test: Connect various devices and check for correct input/out determination.
Prototype Headset, Presonus AudioBox 22VSL...

Change-Id: I9e25b3e9c1c3280c26171873c0a48c58cbb99fb5
This commit is contained in:
Paul McLean
2018-01-02 08:42:30 -07:00
parent 50c00924d8
commit 16d7913d98
7 changed files with 448 additions and 615 deletions

View File

@@ -31,7 +31,9 @@ public class AlsaCardsParser {
private static final String TAG = "AlsaCardsParser";
protected static final boolean DEBUG = false;
private static final String kCardsFilePath = "/proc/asound/cards";
private static final String kAlsaFolderPath = "/proc/asound";
private static final String kCardsFilePath = kAlsaFolderPath + "/cards";
private static final String kDeviceAddressPrefix = "/dev/bus/usb/";
private static LineTokenizer mTokenizer = new LineTokenizer(" :[]");
@@ -47,14 +49,31 @@ public class AlsaCardsParser {
private static final String TAG = "AlsaCardRecord";
private static final String kUsbCardKeyStr = "at usb-";
public int mCardNum = -1;
public String mField1 = "";
public String mCardName = "";
public String mCardDescription = "";
public boolean mIsUsb = false;
int mCardNum = -1;
String mField1 = "";
String mCardName = "";
String mCardDescription = "";
private String mUsbDeviceAddress = null;
public AlsaCardRecord() {}
public int getCardNum() {
return mCardNum;
}
public String getCardName() {
return mCardName;
}
public String getCardDescription() {
return mCardDescription;
}
public void setDeviceAddress(String usbDeviceAddress) {
mUsbDeviceAddress = usbDeviceAddress;
}
private boolean parse(String line, int lineIndex) {
int tokenIndex = 0;
int delimIndex = 0;
@@ -87,8 +106,8 @@ public class AlsaCardsParser {
tokenIndex = mTokenizer.nextToken(line, 0);
if (tokenIndex != -1) {
int keyIndex = line.indexOf(kUsbCardKeyStr);
mIsUsb = keyIndex != -1;
if (mIsUsb) {
boolean isUsb = keyIndex != -1;
if (isUsb) {
mCardDescription = line.substring(tokenIndex, keyIndex - 1);
}
}
@@ -97,14 +116,18 @@ public class AlsaCardsParser {
return true;
}
boolean isUsb() {
return mUsbDeviceAddress != null;
}
public String textFormat() {
return mCardName + " : " + mCardDescription;
return mCardName + " : " + mCardDescription + " [addr:" + mUsbDeviceAddress + "]";
}
public void log(int listIndex) {
Slog.d(TAG, "" + listIndex +
" [" + mCardNum + " " + mCardName + " : " + mCardDescription +
" usb:" + mIsUsb);
" usb:" + isUsb());
}
}
@@ -112,7 +135,7 @@ public class AlsaCardsParser {
public int scan() {
if (DEBUG) {
Slog.i(TAG, "AlsaCardsParser.scan()....");
Slog.d(TAG, "AlsaCardsParser.scan()....");
}
mCardRecords = new ArrayList<AlsaCardRecord>();
@@ -125,7 +148,7 @@ public class AlsaCardsParser {
while ((line = bufferedReader.readLine()) != null) {
AlsaCardRecord cardRecord = new AlsaCardRecord();
if (DEBUG) {
Slog.i(TAG, " " + line);
Slog.d(TAG, " " + line);
}
cardRecord.parse(line, 0);
@@ -134,10 +157,23 @@ public class AlsaCardsParser {
break;
}
if (DEBUG) {
Slog.i(TAG, " " + line);
Slog.d(TAG, " " + line);
}
cardRecord.parse(line, 1);
// scan "usbbus" file
int cardNum = cardRecord.mCardNum;
String cardFolderPath = kAlsaFolderPath + "/card" + cardNum;
File usbbusFile = new File(cardFolderPath + "/usbbus");
if (usbbusFile.exists()) {
// read it in
FileReader usbbusReader = new FileReader(usbbusFile);
String deviceAddress = (new BufferedReader(usbbusReader)).readLine();
if (deviceAddress != null) {
cardRecord.setDeviceAddress(kDeviceAddressPrefix + deviceAddress);
}
usbbusReader.close();
}
mCardRecords.add(cardRecord);
}
reader.close();
@@ -147,14 +183,12 @@ public class AlsaCardsParser {
mScanStatus = SCANSTATUS_EMPTY;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
mScanStatus = SCANSTATUS_FAIL;
} catch (IOException e) {
e.printStackTrace();
mScanStatus = SCANSTATUS_FAIL;
}
if (DEBUG) {
Slog.i(TAG, " status:" + mScanStatus);
Slog.d(TAG, " status:" + mScanStatus);
}
return mScanStatus;
}
@@ -163,142 +197,33 @@ public class AlsaCardsParser {
return mScanStatus;
}
public ArrayList<AlsaCardRecord> getScanRecords() {
return mCardRecords;
}
public AlsaCardRecord getCardRecordAt(int index) {
return mCardRecords.get(index);
}
public AlsaCardRecord getCardRecordFor(int cardNum) {
for (AlsaCardRecord rec : mCardRecords) {
if (rec.mCardNum == cardNum) {
return rec;
public AlsaCardRecord findCardNumFor(String deviceAddress) {
for(AlsaCardRecord cardRec : mCardRecords) {
if (cardRec.isUsb() && cardRec.mUsbDeviceAddress.equals(deviceAddress)) {
return cardRec;
}
}
return null;
}
public int getNumCardRecords() {
return mCardRecords.size();
}
public boolean isCardUsb(int cardNum) {
for (AlsaCardRecord rec : mCardRecords) {
if (rec.mCardNum == cardNum) {
return rec.mIsUsb;
}
}
return false;
}
// return -1 if none found
public int getDefaultUsbCard() {
// save the current list of devices
ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords;
if (DEBUG) {
LogDevices("Previous Devices:", prevRecs);
}
// get the new list of devices
if (scan() != SCANSTATUS_SUCCESS) {
Slog.e(TAG, "Error scanning Alsa cards file.");
return -1;
}
if (DEBUG) {
LogDevices("Current Devices:", mCardRecords);
}
// Calculate the difference between the old and new device list
ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs);
if (DEBUG) {
LogDevices("New Devices:", newRecs);
}
// Choose the most-recently added EXTERNAL card
// Check recently added devices
for (AlsaCardRecord rec : newRecs) {
if (DEBUG) {
Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
}
if (rec.mIsUsb) {
// Found it
return rec.mCardNum;
}
}
// or return the first added EXTERNAL card?
for (AlsaCardRecord rec : prevRecs) {
if (DEBUG) {
Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
}
if (rec.mIsUsb) {
return rec.mCardNum;
}
}
return -1;
}
public int getDefaultCard() {
// return an external card if possible
int card = getDefaultUsbCard();
if (DEBUG) {
Slog.d(TAG, "getDefaultCard() default usb card:" + card);
}
if (card < 0 && getNumCardRecords() > 0) {
// otherwise return the (internal) card with the highest number
card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
}
if (DEBUG) {
Slog.d(TAG, " returns card:" + card);
}
return card;
}
static public boolean hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum) {
for (AlsaCardRecord cardRec : recs) {
if (cardRec.mCardNum == cardNum) {
return true;
}
}
return false;
}
public ArrayList<AlsaCardRecord> getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs) {
ArrayList<AlsaCardRecord> newRecs = new ArrayList<AlsaCardRecord>();
for (AlsaCardRecord rec : mCardRecords) {
// now scan to see if this card number is in the previous scan list
if (!hasCardNumber(prevScanRecs, rec.mCardNum)) {
newRecs.add(rec);
}
}
return newRecs;
}
//
// Logging
//
private void Log(String heading) {
if (DEBUG) {
Slog.i(TAG, heading);
Slog.d(TAG, heading);
for (AlsaCardRecord cardRec : mCardRecords) {
Slog.i(TAG, cardRec.textFormat());
Slog.d(TAG, cardRec.textFormat());
}
}
}
static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
Slog.d(TAG, caption + " ----------------");
int listIndex = 0;
for (AlsaCardRecord device : deviceList) {
device.log(listIndex++);
}
Slog.d(TAG, "----------------");
}
// static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
// Slog.d(TAG, caption + " ----------------");
// int listIndex = 0;
// for (AlsaCardRecord device : deviceList) {
// device.log(listIndex++);
// }
// Slog.d(TAG, caption + "----------------");
// }
}

View File

@@ -27,6 +27,9 @@ import java.util.ArrayList;
* @hide
* Retrieves information from an ALSA "devices" file.
*/
/*
* NOTE: This class is currently not being used, but may be needed in the future.
*/
public class AlsaDevicesParser {
private static final String TAG = "AlsaDevicesParser";
protected static final boolean DEBUG = false;
@@ -207,12 +210,6 @@ public class AlsaDevicesParser {
//
// Predicates
//
/*
public boolean hasPlaybackDevices() {
return mHasPlaybackDevices;
}
*/
public boolean hasPlaybackDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -224,12 +221,6 @@ public class AlsaDevicesParser {
return false;
}
/*
public boolean hasCaptureDevices() {
return mHasCaptureDevices;
}
*/
public boolean hasCaptureDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -241,12 +232,6 @@ public class AlsaDevicesParser {
return false;
}
/*
public boolean hasMIDIDevices() {
return mHasMIDIDevices;
}
*/
public boolean hasMIDIDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -280,6 +265,7 @@ public class AlsaDevicesParser {
if (isLineDeviceRecord(line)) {
AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord();
deviceRecord.parse(line);
Slog.i(TAG, deviceRecord.textFormat());
mDeviceRecords.add(deviceRecord);
}
}

View File

@@ -17,58 +17,101 @@
package com.android.server.usb;
/**
* Represents an instance of an Alsa-supported USB peripheral.
* Represents the ALSA specification, and attributes of an ALSA device.
*/
public final class UsbAlsaDevice {
private static final String TAG = "UsbAlsaDevice";
protected static final boolean DEBUG = false;
public final int mCard;
public final int mDevice;
public final boolean mHasPlayback;
public final boolean mHasCapture;
private final int mCardNum;
private final int mDeviceNum;
private final boolean mHasPlayback;
private final boolean mHasCapture;
// Device "class" flags
static final int AUDIO_DEVICE_CLASS_MASK = 0x00FFFFFF;
static final int AUDIO_DEVICE_CLASS_UNDEFINED = 0x00000000;
static final int AUDIO_DEVICE_CLASS_INTERNAL = 0x00000001;
static final int AUDIO_DEVICE_CLASS_EXTERNAL = 0x00000002;
// Device meta-data flags
static final int AUDIO_DEVICE_META_MASK = 0xFF000000;
static final int AUDIO_DEVICE_META_ALSA = 0x80000000;
// This member is a combination of the above bit-flags
public final int mDeviceClass;
private final boolean mIsInputHeadset;
private final boolean mIsOutputHeadset;
private final String mDeviceAddress;
private String mDeviceName = "";
private String mDeviceDescription = "";
public UsbAlsaDevice(int card, int device,
boolean hasPlayback, boolean hasCapture, int deviceClass) {
mCard = card;
mDevice = device;
public UsbAlsaDevice(int card, int device, String deviceAddress,
boolean hasPlayback, boolean hasCapture,
boolean isInputHeadset, boolean isOutputHeadset) {
mCardNum = card;
mDeviceNum = device;
mDeviceAddress = deviceAddress;
mHasPlayback = hasPlayback;
mHasCapture = hasCapture;
mDeviceClass = deviceClass;
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
}
/**
* @returns the ALSA card number associated with this peripheral.
*/
public int getCardNum() {
return mCardNum;
}
/**
* @returns the ALSA device number associated with this peripheral.
*/
public int getDeviceNum() {
return mDeviceNum;
}
/**
* @returns the USB device device address associated with this peripheral.
*/
public String getDeviceAddress() {
return mDeviceAddress;
}
/**
* @returns true if the device supports playback.
*/
public boolean hasPlayback() {
return mHasPlayback;
}
/**
* @returns true if the device supports capture (recording).
*/
public boolean hasCapture() {
return mHasCapture;
}
/**
* @returns true if the device is a headset for purposes of capture.
*/
public boolean isInputHeadset() {
return mIsInputHeadset;
}
/**
* @returns true if the device is a headset for purposes of playback.
*/
public boolean isOutputHeadset() {
return mIsOutputHeadset;
}
/**
* @Override
* @return a String representation of the object.
* @returns a string representation of the object.
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("UsbAlsaDevice: [card: " + mCard);
sb.append(", device: " + mDevice);
sb.append(", name: " + mDeviceName);
sb.append(", hasPlayback: " + mHasPlayback);
sb.append(", hasCapture: " + mHasCapture);
sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]");
return sb.toString();
return "UsbAlsaDevice: [card: " + mCardNum
+ ", device: " + mDeviceNum
+ ", name: " + mDeviceName
+ ", hasPlayback: " + mHasPlayback
+ ", hasCapture: " + mHasCapture + "]";
}
// called by logDevices
String toShortString() {
return "[card:" + mCard + " device:" + mDevice + " " + mDeviceName + "]";
return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]";
}
String getDeviceName() {
@@ -80,5 +123,38 @@ public final class UsbAlsaDevice {
mDeviceDescription = deviceDescription;
}
/**
* @Override
* @returns true if the objects are equivalent.
*/
public boolean equals(Object obj) {
if (!(obj instanceof UsbAlsaDevice)) {
return false;
}
UsbAlsaDevice other = (UsbAlsaDevice) obj;
return (mCardNum == other.mCardNum
&& mDeviceNum == other.mDeviceNum
&& mHasPlayback == other.mHasPlayback
&& mHasCapture == other.mHasCapture
&& mIsInputHeadset == other.mIsInputHeadset
&& mIsOutputHeadset == other.mIsOutputHeadset);
}
/**
* @Override
* @returns a hash code generated from the object contents.
*/
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mCardNum;
result = prime * result + mDeviceNum;
result = prime * result + (mHasPlayback ? 0 : 1);
result = prime * result + (mHasCapture ? 0 : 1);
result = prime * result + (mIsInputHeadset ? 0 : 1);
result = prime * result + (mIsOutputHeadset ? 0 : 1);
return result;
}
}

View File

@@ -10,7 +10,7 @@
* 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 an
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@@ -19,28 +19,24 @@ package com.android.server.usb;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.midi.MidiDeviceInfo;
import android.os.Bundle;
import android.os.FileObserver;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Slog;
import com.android.internal.alsa.AlsaCardsParser;
import com.android.internal.alsa.AlsaDevicesParser;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.audio.AudioService;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import libcore.io.IoUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -57,107 +53,36 @@ public final class UsbAlsaManager {
private final boolean mHasMidiFeature;
private final AlsaCardsParser mCardsParser = new AlsaCardsParser();
private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser();
// 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>();
private final HashMap<UsbDevice, UsbAlsaDevice>
mAudioDevices = new HashMap<UsbDevice, UsbAlsaDevice>();
private boolean mIsInputHeadset; // as reported by UsbDescriptorParser
private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser
private final HashMap<UsbDevice,UsbMidiDevice>
mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
private final HashMap<String,AlsaDevice>
mAlsaDevices = new HashMap<String,AlsaDevice>();
private UsbAlsaDevice mAccessoryAudioDevice = null;
/**
* List of connected MIDI devices
*/
private final HashMap<String, UsbMidiDevice>
mMidiDevices = new HashMap<String, UsbMidiDevice>();
// UsbMidiDevice for USB peripheral mode (gadget) device
private UsbMidiDevice mPeripheralMidiDevice = null;
private final class AlsaDevice {
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_PLAYBACK = 1;
public static final int TYPE_CAPTURE = 2;
public static final int TYPE_MIDI = 3;
public int mCard;
public int mDevice;
public int mType;
public AlsaDevice(int type, int card, int device) {
mType = type;
mCard = card;
mDevice = device;
}
public boolean equals(Object obj) {
if (! (obj instanceof AlsaDevice)) {
return false;
}
AlsaDevice other = (AlsaDevice)obj;
return (mType == other.mType && mCard == other.mCard && mDevice == other.mDevice);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AlsaDevice: [card: " + mCard);
sb.append(", device: " + mDevice);
sb.append(", type: " + mType);
sb.append("]");
return sb.toString();
}
}
private final FileObserver mAlsaObserver = new FileObserver(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);
// initial scan
if (mCardsParser.scan() != AlsaCardsParser.SCANSTATUS_SUCCESS) {
Slog.e(TAG, "Error scanning ASLA cards file.");
}
}
public void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
mAlsaObserver.startWatching();
// add existing alsa devices
File[] files = new File(ALSA_DIRECTORY).listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
alsaFileAdded(files[i].getName());
}
}
}
// Notifies AudioService when a device is added or removed
// audioDevice - the AudioDevice that was added or removed
// enabled - if true, we're connecting a device (it's arrived), else disconnecting
private void notifyDeviceState(UsbAlsaDevice audioDevice, boolean enabled) {
private void notifyDeviceState(UsbAlsaDevice alsaDevice, boolean enabled) {
if (DEBUG) {
Slog.d(TAG, "notifyDeviceState " + enabled + " " + audioDevice);
Slog.d(TAG, "notifyDeviceState " + enabled + " " + alsaDevice);
}
if (mAudioService == null) {
@@ -177,276 +102,151 @@ public final class UsbAlsaManager {
}
int state = (enabled ? 1 : 0);
int alsaCard = audioDevice.mCard;
int alsaDevice = audioDevice.mDevice;
if (alsaCard < 0 || alsaDevice < 0) {
Slog.e(TAG, "Invalid alsa card or device alsaCard: " + alsaCard +
" alsaDevice: " + alsaDevice);
int cardNum = alsaDevice.getCardNum();
int deviceNum = alsaDevice.getDeviceNum();
if (cardNum < 0 || deviceNum < 0) {
Slog.e(TAG, "Invalid alsa card or device alsaCard: " + cardNum
+ " alsaDevice: " + deviceNum);
return;
}
String address = AudioService.makeAlsaAddressString(alsaCard, alsaDevice);
String address = AudioService.makeAlsaAddressString(cardNum, deviceNum);
try {
// Playback Device
if (audioDevice.mHasPlayback) {
int device;
if (mIsOutputHeadset) {
device = AudioSystem.DEVICE_OUT_USB_HEADSET;
} else {
device = (audioDevice == mAccessoryAudioDevice
? AudioSystem.DEVICE_OUT_USB_ACCESSORY
: AudioSystem.DEVICE_OUT_USB_DEVICE);
}
if (alsaDevice.hasPlayback()) {
int device = alsaDevice.isOutputHeadset()
? AudioSystem.DEVICE_OUT_USB_HEADSET
: AudioSystem.DEVICE_OUT_USB_DEVICE;
if (DEBUG) {
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
" addr:" + address + " name:" + audioDevice.getDeviceName());
" addr:" + address + " name:" + alsaDevice.getDeviceName());
}
mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG);
device, state, address, alsaDevice.getDeviceName(), TAG);
}
// Capture Device
if (audioDevice.mHasCapture) {
int device;
if (mIsInputHeadset) {
device = AudioSystem.DEVICE_IN_USB_HEADSET;
} else {
device = (audioDevice == mAccessoryAudioDevice
? AudioSystem.DEVICE_IN_USB_ACCESSORY
: AudioSystem.DEVICE_IN_USB_DEVICE);
}
if (alsaDevice.hasCapture()) {
int device = alsaDevice.isInputHeadset()
? AudioSystem.DEVICE_IN_USB_HEADSET
: AudioSystem.DEVICE_IN_USB_DEVICE;
mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG);
device, state, address, alsaDevice.getDeviceName(), TAG);
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
}
}
private AlsaDevice waitForAlsaDevice(int card, int device, int type) {
if (DEBUG) {
Slog.e(TAG, "waitForAlsaDevice(c:" + card + " d:" + device + ")");
private int getAlsaDeviceListIndexFor(String deviceAddress) {
for (int index = 0; index < mAlsaDevices.size(); index++) {
if (mAlsaDevices.get(index).getDeviceAddress().equals(deviceAddress)) {
return index;
}
}
AlsaDevice testDevice = new AlsaDevice(type, card, device);
// This value was empirically determined.
final int kWaitTimeMs = 2500;
synchronized(mAlsaDevices) {
long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs;
do {
if (mAlsaDevices.values().contains(testDevice)) {
return testDevice;
}
long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime();
if (waitTimeMs > 0) {
try {
mAlsaDevices.wait(waitTimeMs);
} catch (InterruptedException e) {
Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
}
}
} while (timeoutMs > SystemClock.elapsedRealtime());
}
Slog.e(TAG, "waitForAlsaDevice failed for " + testDevice);
return null;
return -1;
}
private void alsaFileAdded(String name) {
Slog.i(TAG, "alsaFileAdded(" + name + ")");
int type = AlsaDevice.TYPE_UNKNOWN;
int card = -1, device = -1;
if (name.startsWith("pcmC")) {
if (name.endsWith("p")) {
type = AlsaDevice.TYPE_PLAYBACK;
} else if (name.endsWith("c")) {
type = AlsaDevice.TYPE_CAPTURE;
}
} else if (name.startsWith("midiC")) {
type = AlsaDevice.TYPE_MIDI;
}
if (type != AlsaDevice.TYPE_UNKNOWN) {
try {
int c_index = name.indexOf('C');
int d_index = name.indexOf('D');
int end = name.length();
if (type == AlsaDevice.TYPE_PLAYBACK || type == AlsaDevice.TYPE_CAPTURE) {
// skip trailing 'p' or 'c'
end--;
}
card = Integer.parseInt(name.substring(c_index + 1, d_index));
device = Integer.parseInt(name.substring(d_index + 1, end));
} catch (Exception e) {
Slog.e(TAG, "Could not parse ALSA file name " + name, e);
return;
}
synchronized(mAlsaDevices) {
if (mAlsaDevices.get(name) == null) {
AlsaDevice alsaDevice = new AlsaDevice(type, card, device);
Slog.d(TAG, "Adding ALSA device " + alsaDevice);
mAlsaDevices.put(name, alsaDevice);
mAlsaDevices.notifyAll();
}
}
}
}
private void alsaFileRemoved(String path) {
synchronized(mAlsaDevices) {
AlsaDevice device = mAlsaDevices.remove(path);
if (device != null) {
Slog.d(TAG, "ALSA device removed: " + device);
}
}
}
/*
* Select the default device of the specified card.
*/
/* package */ UsbAlsaDevice selectAudioCard(int card) {
if (DEBUG) {
Slog.d(TAG, "selectAudioCard() card:" + card
+ " isCardUsb(): " + mCardsParser.isCardUsb(card));
}
if (!mCardsParser.isCardUsb(card)) {
// Don't. AudioPolicyManager has logic for falling back to internal devices.
private UsbAlsaDevice removeAlsaDeviceFromList(String deviceAddress) {
int index = getAlsaDeviceListIndexFor(deviceAddress);
if (index > -1) {
return mAlsaDevices.remove(index);
} else {
return null;
}
if (mDevicesParser.scan() != AlsaDevicesParser.SCANSTATUS_SUCCESS) {
Slog.e(TAG, "Error parsing ALSA devices file.");
return null;
}
int device = mDevicesParser.getDefaultDeviceNum(card);
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
if (DEBUG) {
Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
}
int deviceClass =
(mCardsParser.isCardUsb(card)
? UsbAlsaDevice.AUDIO_DEVICE_CLASS_EXTERNAL
: UsbAlsaDevice.AUDIO_DEVICE_CLASS_INTERNAL)
| UsbAlsaDevice.AUDIO_DEVICE_META_ALSA;
// Playback device file needed/present?
if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) {
return null;
}
// Capture device file needed/present?
if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) {
return null;
}
UsbAlsaDevice audioDevice =
new UsbAlsaDevice(card, device, hasPlayback, hasCapture, deviceClass);
AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
audioDevice.setDeviceNameAndDescription(cardRecord.mCardName, cardRecord.mCardDescription);
notifyDeviceState(audioDevice, true /*enabled*/);
return audioDevice;
}
/* package */ UsbAlsaDevice selectDefaultDevice() {
if (DEBUG) {
Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
}
return selectAudioCard(mCardsParser.getDefaultCard());
if (mAlsaDevices.size() > 0) {
UsbAlsaDevice alsaDevice = mAlsaDevices.get(0);
if (DEBUG) {
Slog.d(TAG, " alsaDevice:" + alsaDevice);
}
if (alsaDevice != null) {
notifyDeviceState(alsaDevice, true /*enabled*/);
}
return alsaDevice;
} else {
return null;
}
}
/* package */ void usbDeviceAdded(UsbDevice usbDevice,
boolean isInputHeadset, boolean isOutputHeadset) {
/* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
UsbDescriptorParser parser) {
if (DEBUG) {
Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName()
+ " nm:" + usbDevice.getProductName());
}
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
// Scan the Alsa File Space
mCardsParser.scan();
// Is there an audio interface in there?
boolean isAudioDevice = false;
// FIXME - handle multiple configurations?
int interfaceCount = usbDevice.getInterfaceCount();
for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < interfaceCount;
ntrfaceIndex++) {
UsbInterface ntrface = usbDevice.getInterface(ntrfaceIndex);
if (ntrface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
isAudioDevice = true;
}
}
if (DEBUG) {
Slog.d(TAG, " isAudioDevice: " + isAudioDevice);
}
if (!isAudioDevice) {
// Find the ALSA spec for this device address
AlsaCardsParser.AlsaCardRecord cardRec =
mCardsParser.findCardNumFor(deviceAddress);
if (cardRec == null) {
return;
}
int addedCard = mCardsParser.getDefaultUsbCard();
// If the default isn't a USB device, let the existing "select internal mechanism"
// handle the selection.
// Add it to the devices list
boolean hasInput = parser.hasInput();
boolean hasOutput = parser.hasOutput();
if (DEBUG) {
Slog.d(TAG, " mCardsParser.isCardUsb(" + addedCard + ") = "
+ mCardsParser.isCardUsb(addedCard));
Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput);
}
if (mCardsParser.isCardUsb(addedCard)) {
UsbAlsaDevice audioDevice = selectAudioCard(addedCard);
if (audioDevice != null) {
mAudioDevices.put(usbDevice, audioDevice);
Slog.i(TAG, "USB Audio Device Added: " + audioDevice);
if (hasInput || hasOutput) {
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
UsbAlsaDevice alsaDevice =
new UsbAlsaDevice(cardRec.getCardNum(), 0 /*device*/, deviceAddress,
hasOutput, hasInput, isInputHeadset, isOutputHeadset);
alsaDevice.setDeviceNameAndDescription(
cardRec.getCardName(), cardRec.getCardDescription());
mAlsaDevices.add(0, alsaDevice);
// Select it
if (alsaDevice != null) {
notifyDeviceState(alsaDevice, true /*enabled*/);
}
}
// look for MIDI devices
// look for MIDI devices
boolean hasMidi = parser.hasMIDIInterface();
if (DEBUG) {
Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
}
if (hasMidi && mHasMidiFeature) {
int device = 0;
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);
// Don't need to call mDevicesParser.scan() because selectAudioCard() does this above.
// Uncomment this next line if that behavior changes in the fugure.
// mDevicesParser.scan()
boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard);
if (hasMidi && mHasMidiFeature) {
int device = mDevicesParser.getDefaultDeviceNum(addedCard);
AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
if (alsaDevice != null) {
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, alsaDevice.mCard);
properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice);
properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
alsaDevice.mCard, alsaDevice.mDevice);
if (usbMidiDevice != null) {
mMidiDevices.put(usbDevice, usbMidiDevice);
}
}
UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
cardRec.getCardNum(), 0 /*device*/);
if (usbMidiDevice != null) {
mMidiDevices.put(deviceAddress, usbMidiDevice);
}
}
@@ -455,39 +255,28 @@ public final class UsbAlsaManager {
}
}
/* package */ void usbDeviceRemoved(UsbDevice usbDevice) {
/* package */ void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
if (DEBUG) {
Slog.d(TAG, "deviceRemoved(): " + usbDevice.getManufacturerName() +
" " + usbDevice.getProductName());
Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")");
}
UsbAlsaDevice audioDevice = mAudioDevices.remove(usbDevice);
Slog.i(TAG, "USB Audio Device Removed: " + audioDevice);
if (audioDevice != null) {
if (audioDevice.mHasPlayback || audioDevice.mHasCapture) {
notifyDeviceState(audioDevice, false /*enabled*/);
// Audio
UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress);
Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
if (alsaDevice != null) {
if (alsaDevice.hasPlayback() || alsaDevice.hasCapture()) {
notifyDeviceState(alsaDevice, false /*enabled*/);
// if there any external devices left, select one of them
selectDefaultDevice();
}
}
UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice);
if (usbMidiDevice != null) {
IoUtils.closeQuietly(usbMidiDevice);
}
}
/* package */ void setAccessoryAudioState(boolean enabled, int card, int device) {
if (DEBUG) {
Slog.d(TAG, "setAccessoryAudioState " + enabled + " " + card + " " + device);
}
if (enabled) {
mAccessoryAudioDevice = new UsbAlsaDevice(card, device, true, false,
UsbAlsaDevice.AUDIO_DEVICE_CLASS_EXTERNAL);
notifyDeviceState(mAccessoryAudioDevice, true /*enabled*/);
} else if (mAccessoryAudioDevice != null) {
notifyDeviceState(mAccessoryAudioDevice, false /*enabled*/);
mAccessoryAudioDevice = null;
// MIDI
UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
if (usbMidiDevice != null) {
Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
IoUtils.closeQuietly(usbMidiDevice);
}
}
@@ -514,20 +303,6 @@ public final class UsbAlsaManager {
}
}
//
// Devices List
//
/*
//import java.util.ArrayList;
public ArrayList<UsbAlsaDevice> getConnectedDevices() {
ArrayList<UsbAlsaDevice> devices = new ArrayList<UsbAlsaDevice>(mAudioDevices.size());
for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
devices.add(entry.getValue());
}
return devices;
}
*/
//
// Logging
//
@@ -535,14 +310,15 @@ public final class UsbAlsaManager {
public void dump(IndentingPrintWriter pw) {
pw.println("Parsers Scan Status:");
pw.println(" Cards Parser: " + mCardsParser.getScanStatus());
pw.println(" Devices Parser: " + mDevicesParser.getScanStatus());
// pw.println(" Devices Parser: " + mDevicesParser.getScanStatus());
pw.println("USB Audio Devices:");
for (UsbDevice device : mAudioDevices.keySet()) {
pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device));
for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) {
pw.println(" " + usbAlsaDevice.getDeviceAddress() + ": " + usbAlsaDevice);
}
pw.println("USB MIDI Devices:");
for (UsbDevice device : mMidiDevices.keySet()) {
pw.println(" " + device.getDeviceName() + ": " + mMidiDevices.get(device));
for (String deviceAddr : mMidiDevices.keySet()) {
UsbMidiDevice midiDevice = mMidiDevices.get(deviceAddr);
pw.println(" " + deviceAddr + ": " + midiDevice);
}
}

View File

@@ -726,35 +726,9 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
private void updateUsbFunctions() {
updateAudioSourceFunction();
updateMidiFunction();
}
private void updateAudioSourceFunction() {
boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_AUDIO_SOURCE) != 0;
if (enabled != mAudioSourceEnabled) {
int card = -1;
int device = -1;
if (enabled) {
Scanner scanner = null;
try {
scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH));
card = scanner.nextInt();
device = scanner.nextInt();
} catch (FileNotFoundException e) {
Slog.e(TAG, "could not open audio source PCM file", e);
} finally {
if (scanner != null) {
scanner.close();
}
}
}
mUsbAlsaManager.setAccessoryAudioState(enabled, card, device);
mAudioSourceEnabled = enabled;
}
}
private void updateMidiFunction() {
boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
if (enabled != mMidiEnabled) {

View File

@@ -44,7 +44,7 @@ import java.util.LinkedList;
*/
public class UsbHostManager {
private static final String TAG = UsbHostManager.class.getSimpleName();
private static final boolean DEBUG = true;
private static final boolean DEBUG = false;
private final Context mContext;
@@ -230,6 +230,7 @@ public class UsbHostManager {
}
private boolean isBlackListed(String deviceAddress) {
Slog.i(TAG, "isBlackListed(" + deviceAddress + ")");
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
if (deviceAddress.startsWith(mHostBlacklist[i])) {
@@ -241,6 +242,7 @@ public class UsbHostManager {
/* returns true if the USB device should not be accessible by applications */
private boolean isBlackListed(int clazz, int subClass) {
Slog.i(TAG, "isBlackListed(" + clazz + ", " + subClass + ")");
// blacklist hubs
if (clazz == UsbConstants.USB_CLASS_HUB) return true;
@@ -312,13 +314,7 @@ public class UsbHostManager {
usbDeviceConnectionHandler);
}
// Headset?
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+ " , out: " + isOutputHeadset + "]");
mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
@@ -343,10 +339,13 @@ public class UsbHostManager {
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
if (DEBUG) {
Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") - start");
}
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
mUsbAlsaManager.usbDeviceRemoved(device);
mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);

View File

@@ -26,7 +26,7 @@ import java.util.ArrayList;
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
private static final boolean DEBUG = true;
private static final boolean DEBUG = false;
private final String mDeviceAddr;
@@ -358,6 +358,139 @@ public final class UsbDescriptorParser {
return list;
}
/*
* Attribute predicates
*/
/**
* @hide
*/
public boolean hasInput() {
if (DEBUG) {
Log.d(TAG, "---- hasInput()");
}
ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
boolean hasInput = false;
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal inDescr = (UsbACTerminal) descriptor;
// Check for input and bi-directional terminal types
int type = inDescr.getTerminalType();
if (DEBUG) {
Log.d(TAG, " type:0x" + Integer.toHexString(type));
}
if ((type >= UsbTerminalTypes.TERMINAL_IN_UNDEFINED
&& type <= UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY)
|| (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
&& type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)
|| (type == UsbTerminalTypes.TERMINAL_USB_STREAMING)) {
hasInput = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
if (DEBUG) {
Log.d(TAG, "hasInput() = " + hasInput);
}
return hasInput;
}
/**
* @hide
*/
public boolean hasOutput() {
if (DEBUG) {
Log.d(TAG, "---- hasOutput()");
}
ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
boolean hasOutput = false;
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
// Check for output and bi-directional terminal types
int type = outDescr.getTerminalType();
if (DEBUG) {
Log.d(TAG, " type:0x" + Integer.toHexString(type));
}
if ((type >= UsbTerminalTypes.TERMINAL_OUT_UNDEFINED
&& type <= UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER)
|| (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
&& type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)) {
hasOutput = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
if (DEBUG) {
Log.d(TAG, "hasOutput() = " + hasOutput);
}
return hasOutput;
}
/**
* @hide
*/
public boolean hasMic() {
boolean hasMic = false;
ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal inDescr = (UsbACTerminal) descriptor;
if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
hasMic = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
return hasMic;
}
/**
* @hide
*/
public boolean hasSpeaker() {
boolean hasSpeaker = false;
ArrayList<UsbDescriptor> acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
|| outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
|| outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
hasSpeaker = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
return hasSpeaker;
}
/**
* @hide
*/
@@ -397,48 +530,12 @@ public final class UsbDescriptorParser {
}
float probability = 0.0f;
ArrayList<UsbDescriptor> acDescriptors;
// Look for a microphone
boolean hasMic = false;
acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal inDescr = (UsbACTerminal) descriptor;
if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
hasMic = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
boolean hasMic = hasMic();
// Look for a "speaker"
boolean hasSpeaker = false;
acDescriptors =
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
|| outDescr.getTerminalType()
== UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
|| outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
hasSpeaker = true;
break;
}
} else {
Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
boolean hasSpeaker = hasSpeaker();
if (hasMic && hasSpeaker) {
probability += 0.75f;