Merge "Support for simultaneous USB Audio Devices connect/disconnect"
diff --git a/core/java/android/alsa/AlsaCardsParser.java b/core/java/android/alsa/AlsaCardsParser.java
index 8b44881..8f9c56c 100644
--- a/core/java/android/alsa/AlsaCardsParser.java
+++ b/core/java/android/alsa/AlsaCardsParser.java
@@ -22,46 +22,58 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
-import java.util.Vector;
+import java.util.ArrayList;
/**
* @hide Retrieves information from an ALSA "cards" file.
*/
public class AlsaCardsParser {
private static final String TAG = "AlsaCardsParser";
+ protected static final boolean DEBUG = true;
- private static LineTokenizer tokenizer_ = new LineTokenizer(" :[]");
+ private static LineTokenizer mTokenizer = new LineTokenizer(" :[]");
+
+ private ArrayList<AlsaCardRecord> mCardRecords = new ArrayList<AlsaCardRecord>();
public class AlsaCardRecord {
+ 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;
public AlsaCardRecord() {}
public boolean parse(String line, int lineIndex) {
int tokenIndex = 0;
int delimIndex = 0;
+
if (lineIndex == 0) {
// line # (skip)
- tokenIndex = tokenizer_.nextToken(line, tokenIndex);
- delimIndex = tokenizer_.nextDelimiter(line, tokenIndex);
+ tokenIndex = mTokenizer.nextToken(line, tokenIndex);
+ delimIndex = mTokenizer.nextDelimiter(line, tokenIndex);
+
+ // mCardNum
+ mCardNum = Integer.parseInt(line.substring(tokenIndex, delimIndex));
// mField1
- tokenIndex = tokenizer_.nextToken(line, delimIndex);
- delimIndex = tokenizer_.nextDelimiter(line, tokenIndex);
+ tokenIndex = mTokenizer.nextToken(line, delimIndex);
+ delimIndex = mTokenizer.nextDelimiter(line, tokenIndex);
mField1 = line.substring(tokenIndex, delimIndex);
// mCardName
- tokenIndex = tokenizer_.nextToken(line, delimIndex);
- // delimIndex = tokenizer_.nextDelimiter(line, tokenIndex);
+ tokenIndex = mTokenizer.nextToken(line, delimIndex);
mCardName = line.substring(tokenIndex);
+
// done
} else if (lineIndex == 1) {
- tokenIndex = tokenizer_.nextToken(line, 0);
+ tokenIndex = mTokenizer.nextToken(line, 0);
if (tokenIndex != -1) {
mCardDescription = line.substring(tokenIndex);
+ mIsUsb = mCardDescription.contains(kUsbCardKeyStr);
}
}
@@ -73,44 +85,128 @@
}
}
- private Vector<AlsaCardRecord> cardRecords_ = new Vector<AlsaCardRecord>();
+ public AlsaCardsParser() {}
public void scan() {
- cardRecords_.clear();
- final String cardsFilePath = "/proc/asound/cards";
- File cardsFile = new File(cardsFilePath);
- try {
- FileReader reader = new FileReader(cardsFile);
- BufferedReader bufferedReader = new BufferedReader(reader);
- String line = "";
- while ((line = bufferedReader.readLine()) != null) {
- AlsaCardRecord cardRecord = new AlsaCardRecord();
- cardRecord.parse(line, 0);
- cardRecord.parse(line = bufferedReader.readLine(), 1);
- cardRecords_.add(cardRecord);
- }
- reader.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
+ if (DEBUG) {
+ Slog.i(TAG, "AlsaCardsParser.scan()");
+ }
+ mCardRecords = new ArrayList<AlsaCardRecord>();
- public AlsaCardRecord getCardRecordAt(int index) {
- return cardRecords_.get(index);
- }
+ final String cardsFilePath = "/proc/asound/cards";
+ File cardsFile = new File(cardsFilePath);
+ try {
+ FileReader reader = new FileReader(cardsFile);
+ BufferedReader bufferedReader = new BufferedReader(reader);
+ String line = "";
+ while ((line = bufferedReader.readLine()) != null) {
+ AlsaCardRecord cardRecord = new AlsaCardRecord();
+ if (DEBUG) {
+ Slog.i(TAG, " " + line);
+ }
+ cardRecord.parse(line, 0);
- public int getNumCardRecords() {
- return cardRecords_.size();
- }
+ line = bufferedReader.readLine();
+ if (DEBUG) {
+ Slog.i(TAG, " " + line);
+ }
+ cardRecord.parse(line, 1);
- public void Log() {
- int numCardRecs = getNumCardRecords();
- for (int index = 0; index < numCardRecs; ++index) {
- Slog.w(TAG, "usb:" + getCardRecordAt(index).textFormat());
- }
+ mCardRecords.add(cardRecord);
+ }
+ reader.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
- public AlsaCardsParser() {}
+ 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;
+ }
+ }
+
+ 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() {
+ // Choose the most-recently added EXTERNAL card
+ // or return the first added EXTERNAL card?
+ for (AlsaCardRecord rec : mCardRecords) {
+ if (rec.mIsUsb) {
+ return rec.mCardNum;
+ }
+ }
+
+ return -1;
+ }
+
+ public int getDefaultCard() {
+ // return an external card if possible
+ int card = getDefaultUsbCard();
+
+ if (card < 0 && getNumCardRecords() > 0) {
+ // otherwise return the (internal) card with the highest number
+ card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
+ }
+ 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
+ //
+ public void Log(String heading) {
+ if (DEBUG) {
+ Slog.i(TAG, heading);
+ for (AlsaCardRecord cardRec : mCardRecords) {
+ Slog.i(TAG, cardRec.textFormat());
+ }
+ }
+ }
}
diff --git a/core/java/android/alsa/AlsaDevicesParser.java b/core/java/android/alsa/AlsaDevicesParser.java
index 82cc1ae..3581cb6 100644
--- a/core/java/android/alsa/AlsaDevicesParser.java
+++ b/core/java/android/alsa/AlsaDevicesParser.java
@@ -21,7 +21,7 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
-import java.util.Vector;
+import java.util.ArrayList;
/**
* @hide
@@ -29,6 +29,7 @@
*/
public class AlsaDevicesParser {
private static final String TAG = "AlsaDevicesParser";
+ protected static final boolean DEBUG = false;
private static final int kIndex_CardDeviceField = 5;
private static final int kStartIndex_CardNum = 6;
@@ -58,8 +59,7 @@
int mDeviceType = kDeviceType_Unknown;
int mDeviceDir = kDeviceDir_Unknown;
- public AlsaDeviceRecord() {
- }
+ public AlsaDeviceRecord() {}
public boolean parse(String line) {
// "0123456789012345678901234567890"
@@ -176,38 +176,27 @@
}
}
- private Vector<AlsaDeviceRecord>
- deviceRecords_ = new Vector<AlsaDeviceRecord>();
+ private ArrayList<AlsaDeviceRecord> mDeviceRecords = new ArrayList<AlsaDeviceRecord>();
- private boolean isLineDeviceRecord(String line) {
- return line.charAt(kIndex_CardDeviceField) == '[';
+ public AlsaDevicesParser() {}
+
+ //
+ // Access
+ //
+ public int getDefaultDeviceNum(int card) {
+ // TODO - This (obviously) isn't sufficient. Revisit.
+ return 0;
}
- public AlsaDevicesParser() {
- }
-
- public int getNumDeviceRecords() {
- return deviceRecords_.size();
- }
-
- public AlsaDeviceRecord getDeviceRecordAt(int index) {
- return deviceRecords_.get(index);
- }
-
- public void Log() {
- int numDevRecs = getNumDeviceRecords();
- for (int index = 0; index < numDevRecs; ++index) {
- Slog.w(TAG, "usb:" + getDeviceRecordAt(index).textFormat());
- }
- }
-
- public boolean hasPlaybackDevices() {
+ //
+ // Predicates
+ //
+ public boolean hasPlaybackDevices() {
return mHasPlaybackDevices;
}
public boolean hasPlaybackDevices(int card) {
- for (int index = 0; index < deviceRecords_.size(); index++) {
- AlsaDeviceRecord deviceRecord = deviceRecords_.get(index);
+ for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio &&
deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) {
@@ -222,8 +211,7 @@
}
public boolean hasCaptureDevices(int card) {
- for (int index = 0; index < deviceRecords_.size(); index++) {
- AlsaDeviceRecord deviceRecord = deviceRecords_.get(index);
+ for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio &&
deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) {
@@ -238,8 +226,7 @@
}
public boolean hasMIDIDevices(int card) {
- for (int index = 0; index < deviceRecords_.size(); index++) {
- AlsaDeviceRecord deviceRecord = deviceRecords_.get(index);
+ for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) {
return true;
@@ -248,8 +235,15 @@
return false;
}
+ //
+ // Process
+ //
+ private boolean isLineDeviceRecord(String line) {
+ return line.charAt(kIndex_CardDeviceField) == '[';
+ }
+
public void scan() {
- deviceRecords_.clear();
+ mDeviceRecords.clear();
final String devicesFilePath = "/proc/asound/devices";
File devicesFile = new File(devicesFilePath);
@@ -261,7 +255,7 @@
if (isLineDeviceRecord(line)) {
AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord();
deviceRecord.parse(line);
- deviceRecords_.add(deviceRecord);
+ mDeviceRecords.add(deviceRecord);
}
}
reader.close();
@@ -271,5 +265,17 @@
e.printStackTrace();
}
}
+
+ //
+ // Loging
+ //
+ public void Log(String heading) {
+ if (DEBUG) {
+ Slog.i(TAG, heading);
+ for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
+ Slog.i(TAG, deviceRecord.textFormat());
+ }
+ }
+ }
} // class AlsaDevicesParser
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index d90e06e..1a42319 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -40,6 +40,7 @@
public class UsbDevice implements Parcelable {
private static final String TAG = "UsbDevice";
+ private static final boolean DEBUG = false;
private final String mName;
private final String mManufacturerName;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 430ead5..a7965b4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -4979,9 +4979,13 @@
boolean hasPlayback = intent.getBooleanExtra("hasPlayback", false);
boolean hasCapture = intent.getBooleanExtra("hasCapture", false);
boolean hasMIDI = intent.getBooleanExtra("hasMIDI", false);
+ int deviceClass = intent.getIntExtra("class", 0);
- String params = (alsaCard == -1 && alsaDevice == -1 ? ""
- : "card=" + alsaCard + ";device=" + alsaDevice);
+ String params = (alsaCard == -1 && alsaDevice == -1
+ ? ""
+ : "card=" + alsaCard +
+ ";device=" + alsaDevice +
+ ";class=" + Integer.toHexString(deviceClass));
// Playback Device
if (hasPlayback) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 6cc8ff5..32ca723 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -37,46 +37,33 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.ArrayList;
/**
* UsbAlsaManager manages USB audio and MIDI devices.
*/
public class UsbAlsaManager {
private static final String TAG = UsbAlsaManager.class.getSimpleName();
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final String ALSA_DIRECTORY = "/dev/snd/";
private final Context mContext;
private IMidiManager mMidiManager;
- private final class AudioDevice {
- public int mCard;
- public int mDevice;
- public boolean mHasPlayback;
- public boolean mHasCapture;
- public boolean mHasMIDI;
+ private final AlsaCardsParser mCardsParser = new AlsaCardsParser();
+ private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser();
- public AudioDevice(int card, int device,
- boolean hasPlayback, boolean hasCapture, boolean hasMidi) {
- mCard = card;
- mDevice = device;
- mHasPlayback = hasPlayback;
- mHasCapture = hasCapture;
- mHasMIDI = hasMidi;
- }
+ // 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.
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("AudioDevice: [card: " + mCard);
- sb.append(", device: " + mDevice);
- sb.append(", hasPlayback: " + mHasPlayback);
- sb.append(", hasCapture: " + mHasCapture);
- sb.append(", hasMidi: " + mHasMIDI);
- sb.append("]");
- return sb.toString();
- }
- }
+ private final HashMap<UsbDevice,UsbAudioDevice>
+ mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>();
+
+ private final HashMap<String,AlsaDevice>
+ mAlsaDevices = new HashMap<String,AlsaDevice>();
+
+ private UsbAudioDevice mSelectedAudioDevice = null;
private final class AlsaDevice {
public static final int TYPE_UNKNOWN = 0;
@@ -112,12 +99,6 @@
}
}
- private final HashMap<UsbDevice,AudioDevice> mAudioDevices
- = new HashMap<UsbDevice,AudioDevice>();
-
- private final HashMap<String,AlsaDevice> mAlsaDevices
- = new HashMap<String,AlsaDevice>();
-
private final FileObserver mAlsaObserver = new FileObserver(ALSA_DIRECTORY,
FileObserver.CREATE | FileObserver.DELETE) {
public void onEvent(int event, String path) {
@@ -134,6 +115,9 @@
/* package */ UsbAlsaManager(Context context) {
mContext = context;
+
+ // initial scan
+ mCardsParser.scan();
}
public void systemReady() {
@@ -149,9 +133,15 @@
}
// Broadcasts the arrival/departure of a USB audio interface
- // audioDevice - the AudioDevice that was added or removed
+ // audioDevice - the UsbAudioDevice that was added or removed
// enabled - if true, we're connecting a device (it's arrived), else disconnecting
- private void sendDeviceNotification(AudioDevice audioDevice, boolean enabled) {
+ private void sendDeviceNotification(UsbAudioDevice audioDevice, boolean enabled) {
+ if (DEBUG) {
+ Slog.d(TAG, "sendDeviceNotification(enabled:" + enabled +
+ " c:" + audioDevice.mCard +
+ " d:" + audioDevice.mDevice + ")");
+ }
+
// send a sticky broadcast containing current USB state
Intent intent = new Intent(AudioManager.ACTION_USB_AUDIO_DEVICE_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -162,6 +152,7 @@
intent.putExtra("hasPlayback", audioDevice.mHasPlayback);
intent.putExtra("hasCapture", audioDevice.mHasCapture);
intent.putExtra("hasMIDI", audioDevice.mHasMIDI);
+ intent.putExtra("class", audioDevice.mDeviceClass);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -241,6 +232,81 @@
}
}
+ /*
+ * Select the default device of the specified card.
+ */
+ /* package */ boolean selectCard(int card) {
+ if (DEBUG) {
+ Slog.d(TAG, "selectCard() card:" + card);
+ }
+ if (!mCardsParser.isCardUsb(card)) {
+ // Don't. AudioPolicyManager has logic for falling back to internal devices.
+ return false;
+ }
+
+ if (mSelectedAudioDevice != null) {
+ if (mSelectedAudioDevice.mCard == card) {
+ // Nothing to do here.
+ return false;
+ }
+ // "disconnect" the AudioPolicyManager from the previously selected device.
+ sendDeviceNotification(mSelectedAudioDevice, false);
+ mSelectedAudioDevice = null;
+ }
+
+ mDevicesParser.scan();
+ int device = mDevicesParser.getDefaultDeviceNum(card);
+
+ boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
+ boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
+ boolean hasMidi = mDevicesParser.hasMIDIDevices(card);
+ int deviceClass =
+ (mCardsParser.isCardUsb(card)
+ ? UsbAudioDevice.kAudioDeviceClass_External
+ : UsbAudioDevice.kAudioDeviceClass_Internal) |
+ UsbAudioDevice.kAudioDeviceMeta_Alsa;
+
+ // Playback device file needed/present?
+ if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) {
+ return false;
+ }
+
+ // Capture device file needed/present?
+ if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) {
+ return false;
+ }
+ //TODO - seems to me that we need to decouple the above tests for audio
+ // from the one below for MIDI.
+
+ // MIDI device file needed/present?
+ AlsaDevice midiDevice = null;
+ if (hasMidi) {
+ midiDevice = waitForAlsaDevice(card, device, AlsaDevice.TYPE_MIDI);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
+ }
+
+ mSelectedAudioDevice =
+ new UsbAudioDevice(card, device, hasPlayback, hasCapture, hasMidi, deviceClass);
+ mSelectedAudioDevice.mDeviceName = mCardsParser.getCardRecordFor(card).mCardName;
+ mSelectedAudioDevice.mDeviceDescription =
+ mCardsParser.getCardRecordFor(card).mCardDescription;
+
+ sendDeviceNotification(mSelectedAudioDevice, true);
+
+ return true;
+ }
+
+ /* package */ boolean selectDefaultDevice() {
+ if (DEBUG) {
+ Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
+ }
+ mCardsParser.scan();
+ return selectCard(mCardsParser.getDefaultCard());
+ }
+
/* package */ void deviceAdded(UsbDevice usbDevice) {
if (DEBUG) {
Slog.d(TAG, "deviceAdded(): " + usbDevice);
@@ -263,56 +329,28 @@
return;
}
- //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is
- // present, unlike the waitForAlsaDevice() which waits on a file in /dev/snd. It is not
- // clear why this works, or that it can be relied on going forward. Needs further
- // research.
- AlsaCardsParser cardsParser = new AlsaCardsParser();
- cardsParser.scan();
- // cardsParser.Log();
+ ArrayList<AlsaCardsParser.AlsaCardRecord> prevScanRecs = mCardsParser.getScanRecords();
+ mCardsParser.scan();
- // But we need to parse the device to determine its capabilities.
- AlsaDevicesParser devicesParser = new AlsaDevicesParser();
- devicesParser.scan();
- // devicesParser.Log();
-
- // The protocol for now will be to select the last-connected (highest-numbered)
- // Alsa Card.
- int card = cardsParser.getNumCardRecords() - 1;
- int device = 0;
-
- boolean hasPlayback = devicesParser.hasPlaybackDevices(card);
- boolean hasCapture = devicesParser.hasCaptureDevices(card);
- boolean hasMidi = devicesParser.hasMIDIDevices(card);
-
- // Playback device file needed/present?
- if (hasPlayback &&
- waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null) {
- return;
+ int addedCard = -1;
+ ArrayList<AlsaCardsParser.AlsaCardRecord>
+ newScanRecs = mCardsParser.getNewCardRecords(prevScanRecs);
+ if (newScanRecs.size() > 0) {
+ // This is where we select the just connected device
+ // NOTE - to switch to prefering the first-connected device, just always
+ // take the else clause below.
+ addedCard = newScanRecs.get(0).mCardNum;
+ } else {
+ addedCard = mCardsParser.getDefaultUsbCard();
}
- // Capture device file needed/present?
- if (hasCapture &&
- waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null) {
- return;
+ // If the default isn't a USB device, let the existing "select internal mechanism"
+ // handle the selection.
+ if (mCardsParser.isCardUsb(addedCard)) {
+ selectCard(addedCard);
+ mAudioDevices.put(usbDevice, mSelectedAudioDevice);
}
- // MIDI device file needed/present?
- if (hasMidi) {
- midiDevice = waitForAlsaDevice(card, device, AlsaDevice.TYPE_MIDI);
- }
-
- if (DEBUG) {
- Slog.d(TAG,
- "usb: hasPlayback:" + hasPlayback +
- " hasCapture:" + hasCapture +
- " hasMidi:" + hasMidi);
- }
-
- AudioDevice audioDevice = new AudioDevice(card, device, hasPlayback, hasCapture, hasMidi);
- mAudioDevices.put(usbDevice, audioDevice);
- sendDeviceNotification(audioDevice, true);
-
if (midiDevice != null && mMidiManager != null) {
try {
mMidiManager.alsaDeviceAdded(midiDevice.mCard, midiDevice.mDevice, usbDevice);
@@ -327,7 +365,7 @@
Slog.d(TAG, "deviceRemoved(): " + device);
}
- AudioDevice audioDevice = mAudioDevices.remove(device);
+ UsbAudioDevice audioDevice = mAudioDevices.remove(device);
if (audioDevice != null) {
if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) {
sendDeviceNotification(audioDevice, false);
@@ -340,12 +378,52 @@
}
}
}
+
+ mSelectedAudioDevice = null;
+
+ // if there any external devices left, select one of them
+ selectDefaultDevice();
}
+ //
+ // Devices List
+ //
+ public ArrayList<UsbAudioDevice> getConnectedDevices() {
+ ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size());
+ for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ devices.add(entry.getValue());
+ }
+ return devices;
+ }
+
+ //
+ // Logging
+ //
public void dump(FileDescriptor fd, PrintWriter pw) {
pw.println(" USB AudioDevices:");
for (UsbDevice device : mAudioDevices.keySet()) {
pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device));
}
}
+
+ public void logDevicesList(String title) {
+ if (DEBUG) {
+ for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ Slog.i(TAG, "UsbDevice-------------------");
+ Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]"));
+ Slog.i(TAG, "UsbAudioDevice--------------");
+ Slog.i(TAG, "" + entry.getValue());
+ }
+ }
+ }
+
+ // This logs a more terse (and more readable) version of the devices list
+ public void logDevices(String title) {
+ if (DEBUG) {
+ Slog.i(TAG, title);
+ for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ Slog.i(TAG, entry.getValue().toShortString());
+ }
+ }
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbAudioDevice.java b/services/usb/java/com/android/server/usb/UsbAudioDevice.java
new file mode 100644
index 0000000..b7b9563
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbAudioDevice.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+public final class UsbAudioDevice {
+ private static final String TAG = "UsbAudioDevice";
+ protected static final boolean DEBUG = false;
+
+ public int mCard;
+ public int mDevice;
+ public boolean mHasPlayback;
+ public boolean mHasCapture;
+ public boolean mHasMIDI;
+
+ // Device "class" flags
+ public static final int kAudioDeviceClassMask = 0x00FFFFFF;
+ public static final int kAudioDeviceClass_Undefined = 0x00000000;
+ public static final int kAudioDeviceClass_Internal = 0x00000001;
+ public static final int kAudioDeviceClass_External = 0x00000002;
+ // Device meta-data flags
+ public static final int kAudioDeviceMetaMask = 0xFF000000;
+ public static final int kAudioDeviceMeta_Alsa = 0x80000000;
+ // This member is a combination of the above bit-flags
+ public int mDeviceClass;
+
+ public String mDeviceName = "";
+ public String mDeviceDescription = "";
+
+ public UsbAudioDevice(int card, int device,
+ boolean hasPlayback, boolean hasCapture, boolean hasMidi, int deviceClass) {
+ mCard = card;
+ mDevice = device;
+ mHasPlayback = hasPlayback;
+ mHasCapture = hasCapture;
+ mHasMIDI = hasMidi;
+ mDeviceClass = deviceClass;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("UsbAudioDevice: [card: " + mCard);
+ sb.append(", device: " + mDevice);
+ sb.append(", name: " + mDeviceName);
+ sb.append(", description: " + mDeviceDescription);
+ sb.append(", hasPlayback: " + mHasPlayback);
+ sb.append(", hasCapture: " + mHasCapture);
+ sb.append(", hasMidi: " + mHasMIDI);
+ sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]");
+ return sb.toString();
+ }
+
+ public String toShortString() {
+ return "[card:" + mCard + " device:" + mDevice + " " + mDeviceName + "]";
+ }
+}
+