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:
@@ -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 + "----------------");
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user