Loading core/java/android/alsa/AlsaDevicesParser.java +35 −0 Original line number Diff line number Diff line Loading @@ -205,14 +205,49 @@ public class AlsaDevicesParser { return mHasPlaybackDevices; } public boolean hasPlaybackDevices(int card) { for (int index = 0; index < deviceRecords_.size(); index++) { AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) { return true; } } return false; } public boolean hasCaptureDevices() { return mHasCaptureDevices; } public boolean hasCaptureDevices(int card) { for (int index = 0; index < deviceRecords_.size(); index++) { AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) { return true; } } return false; } public boolean hasMIDIDevices() { return mHasMIDIDevices; } public boolean hasMIDIDevices(int card) { for (int index = 0; index < deviceRecords_.size(); index++) { AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) { return true; } } return false; } public void scan() { deviceRecords_.clear(); Loading media/java/android/media/AudioService.java +22 −14 Original line number Diff line number Diff line Loading @@ -4083,8 +4083,19 @@ public class AudioService extends IAudioService.Stub { } } } } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) || action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG)) { state = intent.getIntExtra("state", 0); int alsaCard = intent.getIntExtra("card", -1); int alsaDevice = intent.getIntExtra("device", -1); String params = (alsaCard == -1 && alsaDevice == -1 ? "" : "card=" + alsaCard + ";device=" + alsaDevice); // Playback Device device = AudioSystem.DEVICE_OUT_USB_ACCESSORY; setWiredDeviceConnectionState(device, state, params); } else if (action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { state = intent.getIntExtra("state", 0); int alsaCard = intent.getIntExtra("card", -1); Loading @@ -4096,21 +4107,18 @@ public class AudioService extends IAudioService.Stub { String params = (alsaCard == -1 && alsaDevice == -1 ? "" : "card=" + alsaCard + ";device=" + alsaDevice); //TODO(pmclean) - Ignore for now the hasPlayback & hasCapture flags since we // still need to call setWiredDeviceConnectionState() on disconnect (when we // don't have card/device files to parse for this info). We will need to store // that info here when we get smarter about multiple USB card/devices. // Playback Device device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ? AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE; if (hasPlayback) { device = AudioSystem.DEVICE_OUT_USB_DEVICE; setWiredDeviceConnectionState(device, state, params); } // Capture Device device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ? AudioSystem.DEVICE_IN_USB_ACCESSORY : AudioSystem.DEVICE_IN_USB_DEVICE; if (hasCapture) { device = AudioSystem.DEVICE_IN_USB_DEVICE; setWiredDeviceConnectionState(device, state, params); } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { boolean broadcast = false; int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; synchronized (mScoClients) { Loading services/usb/java/com/android/server/usb/UsbHostManager.java +128 −69 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.os.Parcelable; import android.os.UserHandle; import android.util.Slog; import com.android.alsascan.AlsaCardsParser; import com.android.alsascan.AlsaDevicesParser; import com.android.internal.annotations.GuardedBy; import java.io.File; Loading @@ -37,7 +39,6 @@ import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner; /** * UsbHostManager manages USB state in host mode. Loading @@ -62,6 +63,15 @@ public class UsbHostManager { private ArrayList<UsbInterface> mNewInterfaces; private ArrayList<UsbEndpoint> mNewEndpoints; // Attributes of any connected USB audio device. //TODO(pmclean) When we extend to multiple, USB Audio devices, we will need to get // more clever about this. private int mConnectedUsbCard = -1; private int mConnectedUsbDeviceNum = -1; private boolean mConnectedHasPlayback = false; private boolean mConnectedHasCapture = false; private boolean mConnectedHasMIDI = false; @GuardedBy("mLock") private UsbSettingsManager mCurrentSettings; Loading Loading @@ -126,9 +136,27 @@ public class UsbHostManager { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } static boolean isBuiltInUsbDevice(String deviceName) { // This may be too broad an assumption return deviceName.equals("/dev/bus/usb/001/001"); private boolean waitForAlsaFile(int card, int device, boolean capture) { // These values were empirically determined. final int kNumRetries = 5; final int kSleepTime = 500; // ms String alsaDevPath = "/dev/snd/pcmC" + card + "D" + device + (capture ? "c" : "p"); File alsaDevFile = new File(alsaDevPath); boolean exists = false; for (int retry = 0; !exists && retry < kNumRetries; retry++) { exists = alsaDevFile.exists(); if (!exists) { try { Thread.sleep(kSleepTime); } catch (IllegalThreadStateException ex) { Slog.d(TAG, "usb: IllegalThreadStateException while waiting for ALSA file."); } catch (java.lang.InterruptedException ex) { Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file."); } } } return exists; } /* Called from JNI in monitorUsbHostBus() to report new USB devices Loading @@ -151,47 +179,13 @@ public class UsbHostManager { // midi streaming: 0x03 // some useful debugging info Slog.d(TAG, "usb:UsbHostManager.usbDeviceAdded()"); Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:" + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol); } if (!isBuiltInUsbDevice(deviceName)) { //TODO(pmclean) we will need this when we need to support USB interfaces // beyond card1, device0 but turn them off for now //com.android.alsascan.AlsaCardsParser cardsParser = // new com.android.alsascan.AlsaCardsParser(); //cardsParser.scan(); //cardsParser.Log(); // But we need to parse the device to determine its capabilities. com.android.alsascan.AlsaDevicesParser devicesParser = new com.android.alsascan.AlsaDevicesParser(); devicesParser.scan(); //devicesParser.Log(); boolean hasPlaybackDevices = devicesParser.hasPlaybackDevices(); boolean hasCaptureDevices = devicesParser.hasCaptureDevices(); boolean hasMIDI = devicesParser.hasMIDIDevices(); if (DEBUG_AUDIO) { Slog.d(TAG, "usb: hasPlayback:" + hasPlaybackDevices + " hasCapture:" + hasCaptureDevices); Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:" + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol); } //TODO(pmclean) // For now just assume that any USB device that is attached is: // 1. An audio interface and // 2. is card:1 device:0 int cardNum = 1; int deviceNum = 0; sendDeviceNotification(cardNum, deviceNum, true, hasPlaybackDevices, hasCaptureDevices, hasMIDI); } // OK this is non-obvious, but true. One can't tell if the device being attached is even // potentially an audio device without parsing the interface descriptors, so punt on any // such test until endUsbDeviceAdded() when we have that info. if (isBlackListed(deviceName) || isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) { Loading @@ -217,6 +211,7 @@ public class UsbHostManager { mNewInterfaces = new ArrayList<UsbInterface>(); mNewEndpoints = new ArrayList<UsbEndpoint>(); } return true; } Loading Loading @@ -270,6 +265,17 @@ public class UsbHostManager { mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()])); } // Is there an audio interface in there? final int kUsbClassId_Audio = 0x01; boolean isAudioDevice = false; for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < mNewInterfaces.size(); ntrfaceIndex++) { UsbInterface ntrface = mNewInterfaces.get(ntrfaceIndex); if (ntrface.getInterfaceClass() == kUsbClassId_Audio) { isAudioDevice = true; } } synchronized (mLock) { if (mNewDevice != null) { mNewDevice.setConfigurations( Loading @@ -285,6 +291,48 @@ public class UsbHostManager { mNewInterfaces = null; mNewEndpoints = null; } if (!isAudioDevice) { return; // bail } //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is // present, unlike the waitForAlsaFile() 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(); // 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. mConnectedUsbCard = cardsParser.getNumCardRecords() - 1; mConnectedUsbDeviceNum = 0; if (!waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) { return; } mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard); mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard); mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard); if (DEBUG_AUDIO) { Slog.d(TAG, "usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture); } sendDeviceNotification(mConnectedUsbCard, mConnectedUsbDeviceNum, true, mConnectedHasPlayback, mConnectedHasCapture, mConnectedHasMIDI); } /* Called from JNI in monitorUsbHostBus to report USB device removal */ Loading @@ -293,8 +341,19 @@ public class UsbHostManager { Slog.d(TAG, "usb:UsbHostManager.usbDeviceRemoved() nm:" + deviceName); } // Same assumptions as the fake-out above sendDeviceNotification(1, 0, false, /*NA*/false, /*NA*/false, /*NA*/false); if (mConnectedUsbCard != -1 && mConnectedUsbDeviceNum != -1) { sendDeviceNotification(mConnectedUsbCard, mConnectedUsbDeviceNum, false, mConnectedHasPlayback, mConnectedHasCapture, mConnectedHasMIDI); mConnectedUsbCard = -1; mConnectedUsbDeviceNum = -1; mConnectedHasPlayback = false; mConnectedHasCapture = false; mConnectedHasMIDI = false; } synchronized (mLock) { UsbDevice device = mDevices.remove(deviceName); Loading Loading
core/java/android/alsa/AlsaDevicesParser.java +35 −0 Original line number Diff line number Diff line Loading @@ -205,14 +205,49 @@ public class AlsaDevicesParser { return mHasPlaybackDevices; } public boolean hasPlaybackDevices(int card) { for (int index = 0; index < deviceRecords_.size(); index++) { AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) { return true; } } return false; } public boolean hasCaptureDevices() { return mHasCaptureDevices; } public boolean hasCaptureDevices(int card) { for (int index = 0; index < deviceRecords_.size(); index++) { AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) { return true; } } return false; } public boolean hasMIDIDevices() { return mHasMIDIDevices; } public boolean hasMIDIDevices(int card) { for (int index = 0; index < deviceRecords_.size(); index++) { AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) { return true; } } return false; } public void scan() { deviceRecords_.clear(); Loading
media/java/android/media/AudioService.java +22 −14 Original line number Diff line number Diff line Loading @@ -4083,8 +4083,19 @@ public class AudioService extends IAudioService.Stub { } } } } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) || action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG)) { state = intent.getIntExtra("state", 0); int alsaCard = intent.getIntExtra("card", -1); int alsaDevice = intent.getIntExtra("device", -1); String params = (alsaCard == -1 && alsaDevice == -1 ? "" : "card=" + alsaCard + ";device=" + alsaDevice); // Playback Device device = AudioSystem.DEVICE_OUT_USB_ACCESSORY; setWiredDeviceConnectionState(device, state, params); } else if (action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { state = intent.getIntExtra("state", 0); int alsaCard = intent.getIntExtra("card", -1); Loading @@ -4096,21 +4107,18 @@ public class AudioService extends IAudioService.Stub { String params = (alsaCard == -1 && alsaDevice == -1 ? "" : "card=" + alsaCard + ";device=" + alsaDevice); //TODO(pmclean) - Ignore for now the hasPlayback & hasCapture flags since we // still need to call setWiredDeviceConnectionState() on disconnect (when we // don't have card/device files to parse for this info). We will need to store // that info here when we get smarter about multiple USB card/devices. // Playback Device device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ? AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE; if (hasPlayback) { device = AudioSystem.DEVICE_OUT_USB_DEVICE; setWiredDeviceConnectionState(device, state, params); } // Capture Device device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ? AudioSystem.DEVICE_IN_USB_ACCESSORY : AudioSystem.DEVICE_IN_USB_DEVICE; if (hasCapture) { device = AudioSystem.DEVICE_IN_USB_DEVICE; setWiredDeviceConnectionState(device, state, params); } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { boolean broadcast = false; int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; synchronized (mScoClients) { Loading
services/usb/java/com/android/server/usb/UsbHostManager.java +128 −69 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.os.Parcelable; import android.os.UserHandle; import android.util.Slog; import com.android.alsascan.AlsaCardsParser; import com.android.alsascan.AlsaDevicesParser; import com.android.internal.annotations.GuardedBy; import java.io.File; Loading @@ -37,7 +39,6 @@ import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner; /** * UsbHostManager manages USB state in host mode. Loading @@ -62,6 +63,15 @@ public class UsbHostManager { private ArrayList<UsbInterface> mNewInterfaces; private ArrayList<UsbEndpoint> mNewEndpoints; // Attributes of any connected USB audio device. //TODO(pmclean) When we extend to multiple, USB Audio devices, we will need to get // more clever about this. private int mConnectedUsbCard = -1; private int mConnectedUsbDeviceNum = -1; private boolean mConnectedHasPlayback = false; private boolean mConnectedHasCapture = false; private boolean mConnectedHasMIDI = false; @GuardedBy("mLock") private UsbSettingsManager mCurrentSettings; Loading Loading @@ -126,9 +136,27 @@ public class UsbHostManager { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } static boolean isBuiltInUsbDevice(String deviceName) { // This may be too broad an assumption return deviceName.equals("/dev/bus/usb/001/001"); private boolean waitForAlsaFile(int card, int device, boolean capture) { // These values were empirically determined. final int kNumRetries = 5; final int kSleepTime = 500; // ms String alsaDevPath = "/dev/snd/pcmC" + card + "D" + device + (capture ? "c" : "p"); File alsaDevFile = new File(alsaDevPath); boolean exists = false; for (int retry = 0; !exists && retry < kNumRetries; retry++) { exists = alsaDevFile.exists(); if (!exists) { try { Thread.sleep(kSleepTime); } catch (IllegalThreadStateException ex) { Slog.d(TAG, "usb: IllegalThreadStateException while waiting for ALSA file."); } catch (java.lang.InterruptedException ex) { Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file."); } } } return exists; } /* Called from JNI in monitorUsbHostBus() to report new USB devices Loading @@ -151,47 +179,13 @@ public class UsbHostManager { // midi streaming: 0x03 // some useful debugging info Slog.d(TAG, "usb:UsbHostManager.usbDeviceAdded()"); Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:" + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol); } if (!isBuiltInUsbDevice(deviceName)) { //TODO(pmclean) we will need this when we need to support USB interfaces // beyond card1, device0 but turn them off for now //com.android.alsascan.AlsaCardsParser cardsParser = // new com.android.alsascan.AlsaCardsParser(); //cardsParser.scan(); //cardsParser.Log(); // But we need to parse the device to determine its capabilities. com.android.alsascan.AlsaDevicesParser devicesParser = new com.android.alsascan.AlsaDevicesParser(); devicesParser.scan(); //devicesParser.Log(); boolean hasPlaybackDevices = devicesParser.hasPlaybackDevices(); boolean hasCaptureDevices = devicesParser.hasCaptureDevices(); boolean hasMIDI = devicesParser.hasMIDIDevices(); if (DEBUG_AUDIO) { Slog.d(TAG, "usb: hasPlayback:" + hasPlaybackDevices + " hasCapture:" + hasCaptureDevices); Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:" + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol); } //TODO(pmclean) // For now just assume that any USB device that is attached is: // 1. An audio interface and // 2. is card:1 device:0 int cardNum = 1; int deviceNum = 0; sendDeviceNotification(cardNum, deviceNum, true, hasPlaybackDevices, hasCaptureDevices, hasMIDI); } // OK this is non-obvious, but true. One can't tell if the device being attached is even // potentially an audio device without parsing the interface descriptors, so punt on any // such test until endUsbDeviceAdded() when we have that info. if (isBlackListed(deviceName) || isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) { Loading @@ -217,6 +211,7 @@ public class UsbHostManager { mNewInterfaces = new ArrayList<UsbInterface>(); mNewEndpoints = new ArrayList<UsbEndpoint>(); } return true; } Loading Loading @@ -270,6 +265,17 @@ public class UsbHostManager { mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()])); } // Is there an audio interface in there? final int kUsbClassId_Audio = 0x01; boolean isAudioDevice = false; for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < mNewInterfaces.size(); ntrfaceIndex++) { UsbInterface ntrface = mNewInterfaces.get(ntrfaceIndex); if (ntrface.getInterfaceClass() == kUsbClassId_Audio) { isAudioDevice = true; } } synchronized (mLock) { if (mNewDevice != null) { mNewDevice.setConfigurations( Loading @@ -285,6 +291,48 @@ public class UsbHostManager { mNewInterfaces = null; mNewEndpoints = null; } if (!isAudioDevice) { return; // bail } //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is // present, unlike the waitForAlsaFile() 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(); // 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. mConnectedUsbCard = cardsParser.getNumCardRecords() - 1; mConnectedUsbDeviceNum = 0; if (!waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) { return; } mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard); mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard); mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard); if (DEBUG_AUDIO) { Slog.d(TAG, "usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture); } sendDeviceNotification(mConnectedUsbCard, mConnectedUsbDeviceNum, true, mConnectedHasPlayback, mConnectedHasCapture, mConnectedHasMIDI); } /* Called from JNI in monitorUsbHostBus to report USB device removal */ Loading @@ -293,8 +341,19 @@ public class UsbHostManager { Slog.d(TAG, "usb:UsbHostManager.usbDeviceRemoved() nm:" + deviceName); } // Same assumptions as the fake-out above sendDeviceNotification(1, 0, false, /*NA*/false, /*NA*/false, /*NA*/false); if (mConnectedUsbCard != -1 && mConnectedUsbDeviceNum != -1) { sendDeviceNotification(mConnectedUsbCard, mConnectedUsbDeviceNum, false, mConnectedHasPlayback, mConnectedHasCapture, mConnectedHasMIDI); mConnectedUsbCard = -1; mConnectedUsbDeviceNum = -1; mConnectedHasPlayback = false; mConnectedHasCapture = false; mConnectedHasMIDI = false; } synchronized (mLock) { UsbDevice device = mDevices.remove(deviceName); Loading