Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59f48276 authored by Eric Laurent's avatar Eric Laurent
Browse files

Added support for USB audio devices

Two types of USB audio devices are defined:
- USB audio device: the audio device in USB device mode while
the Android device is in USB host mode.
- USB audio accessory: the audio device in USB host mode while
the Android device is in USB device mode.

Renamed intents for analog and digital docks to avoid confusion:
 - ACTION_USB_ANLG_HEADSET_PLUG to ACTION_ANALOG_AUDIO_DOCK_PLUG
 - ACTION_USB_DGTL_HEADSET_PLUG to ACTION_DIGITAL_AUDIO_DOCK_PLUG

Factorized code in AudioService broadcast receiver.

Change-Id: I1b6d0257a9d68ecb9495c78c98bac8c67fec7891
parent 85d9e020
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -2000,8 +2000,8 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_USB_ANLG_HEADSET_PLUG =
            "android.intent.action.USB_ANLG_HEADSET_PLUG";
    public static final String ACTION_ANALOG_AUDIO_DOCK_PLUG =
            "android.intent.action.ANALOG_AUDIO_DOCK_PLUG";

    /**
     * Broadcast Action: A digital audio speaker/headset plugged in or unplugged.
@@ -2015,8 +2015,8 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_USB_DGTL_HEADSET_PLUG =
            "android.intent.action.USB_DGTL_HEADSET_PLUG";
    public static final String ACTION_DIGITAL_AUDIO_DOCK_PLUG =
            "android.intent.action.DIGITAL_AUDIO_DOCK_PLUG";

    /**
     * Broadcast Action: A HMDI cable was plugged or unplugged
@@ -2034,7 +2034,7 @@ public class Intent implements Parcelable, Cloneable {
            "android.intent.action.HDMI_AUDIO_PLUG";

    /**
     * Broadcast Action: A USB audio device was plugged in or unplugged.
     * Broadcast Action: A USB audio accessory was plugged in or unplugged.
     *
     * <p>The intent will have the following extra values:
     * <ul>
@@ -2046,11 +2046,11 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_USB_AUDIO_DEVICE_PLUG =
            "android.intent.action.USB_AUDIO_DEVICE_PLUG";
    public static final String ACTION_USB_AUDIO_ACCESSORY_PLUG =
            "android.intent.action.USB_AUDIO_ACCESSORY_PLUG";

    /**
     * Broadcast Action: A USB audio accessory was plugged in or unplugged.
     * Broadcast Action: A USB audio device was plugged in or unplugged.
     *
     * <p>The intent will have the following extra values:
     * <ul>
@@ -2062,8 +2062,8 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_USB_AUDIO_ACCESSORY_PLUG =
            "android.intent.action.USB_AUDIO_ACCESSORY_PLUG";
    public static final String ACTION_USB_AUDIO_DEVICE_PLUG =
            "android.intent.action.USB_AUDIO_DEVICE_PLUG";

    /**
     * <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
+8 −0
Original line number Diff line number Diff line
@@ -2237,6 +2237,14 @@ public class AudioManager {
     *  docking station
     */
    public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET;
    /** {@hide} The audio output device code for a USB audio accessory. The accessory is in USB host
     * mode and the Android device in USB device mode
     */
    public static final int DEVICE_OUT_USB_ACCESSORY = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
    /** {@hide} The audio output device code for a USB audio device. The device is in USB device
     * mode and the Android device in USB host mode
     */
    public static final int DEVICE_OUT_USB_DEVICE = AudioSystem.DEVICE_OUT_USB_DEVICE;
    /** {@hide} This is not used as a returned value from {@link #getDevicesForStream}, but could be
     *  used in the future in a set method to select whatever default device is chosen by the
     *  platform-specific implementation.
+71 −118
Original line number Diff line number Diff line
@@ -389,9 +389,11 @@ public class AudioService extends IAudioService.Stub {
        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
        intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
        intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
        intentFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
        intentFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
        intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
        intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
        intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2800,6 +2802,28 @@ public class AudioService extends IAudioService.Stub {
        }
    }

    private boolean handleDeviceConnection(boolean connected, int device, String params) {
        synchronized (mConnectedDevices) {
            boolean isConnected = (mConnectedDevices.containsKey(device) &&
                    mConnectedDevices.get(device).equals(params));

            if (isConnected && !connected) {
                AudioSystem.setDeviceConnectionState(device,
                                              AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                              params);
                 mConnectedDevices.remove(device);
                 return true;
            } else if (!isConnected && connected) {
                 AudioSystem.setDeviceConnectionState(device,
                                                      AudioSystem.DEVICE_STATE_AVAILABLE,
                                                      params);
                 mConnectedDevices.put(new Integer(device), params);
                 return true;
            }
        }
        return false;
    }

    /* cache of the address of the last dock the device was connected to */
    private String mDockAddress;

@@ -2810,6 +2834,8 @@ public class AudioService extends IAudioService.Stub {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            int device;
            int state;

            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
@@ -2834,15 +2860,15 @@ public class AudioService extends IAudioService.Stub {
                }
                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                               BluetoothProfile.STATE_DISCONNECTED);
                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                handleA2dpConnectionStateChange(btDevice, state);
            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                               BluetoothProfile.STATE_DISCONNECTED);
                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
                device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
                String address = null;

                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
@@ -2868,129 +2894,56 @@ public class AudioService extends IAudioService.Stub {
                    address = "";
                }

                synchronized (mConnectedDevices) {
                    boolean isConnected = (mConnectedDevices.containsKey(device) &&
                                           mConnectedDevices.get(device).equals(address));

                boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
                if (handleDeviceConnection(connected, device, address)) {
                    synchronized (mScoClients) {
                        if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                            AudioSystem.setDeviceConnectionState(device,
                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                             address);
                            mConnectedDevices.remove(device);
                        if (connected) {
                            mBluetoothHeadsetDevice = btDevice;
                        } else {
                            mBluetoothHeadsetDevice = null;
                            resetBluetoothSco();
                        } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                            AudioSystem.setDeviceConnectionState(device,
                                                                 AudioSystem.DEVICE_STATE_AVAILABLE,
                                                                 address);
                            mConnectedDevices.put(new Integer(device), address);
                            mBluetoothHeadsetDevice = btDevice;
                        }
                    }
                }
            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
                int state = intent.getIntExtra("state", 0);
                state = intent.getIntExtra("state", 0);
                int microphone = intent.getIntExtra("microphone", 0);

                synchronized (mConnectedDevices) {
                if (microphone != 0) {
                        boolean isConnected =
                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
                        if (state == 0 && isConnected) {
                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                    "");
                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
                        } else if (state == 1 && !isConnected)  {
                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
                                    AudioSystem.DEVICE_STATE_AVAILABLE,
                                    "");
                            mConnectedDevices.put(
                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
                        }
                    device = AudioSystem.DEVICE_OUT_WIRED_HEADSET;
                } else {
                        boolean isConnected =
                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
                        if (state == 0 && isConnected) {
                            AudioSystem.setDeviceConnectionState(
                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                    "");
                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
                        } else if (state == 1 && !isConnected)  {
                            AudioSystem.setDeviceConnectionState(
                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
                                    AudioSystem.DEVICE_STATE_AVAILABLE,
                                    "");
                            mConnectedDevices.put(
                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
                        }
                    }
                }
            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
                int state = intent.getIntExtra("state", 0);
                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
                synchronized (mConnectedDevices) {
                    boolean isConnected =
                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
                    if (state == 0 && isConnected) {
                        AudioSystem.setDeviceConnectionState(
                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
                                                        AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                        "");
                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
                    } else if (state == 1 && !isConnected)  {
                        AudioSystem.setDeviceConnectionState(
                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
                                                        AudioSystem.DEVICE_STATE_AVAILABLE,
                                                        "");
                        mConnectedDevices.put(
                                new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
                    }
                    device = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
                }
                handleDeviceConnection((state == 1), device, "");
            } else if (action.equals(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG)) {
                state = intent.getIntExtra("state", 0);
                Log.v(TAG, "Broadcast Receiver: Got ACTION_ANALOG_AUDIO_DOCK_PLUG, state = "+state);
                handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, "");
            } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
                int state = intent.getIntExtra("state", 0);
                state = intent.getIntExtra("state", 0);
                Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
                synchronized (mConnectedDevices) {
                    boolean isConnected =
                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
                    if (state == 0 && isConnected) {
                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                             "");
                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
                    } else if (state == 1 && !isConnected)  {
                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
                                                             "");
                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
                    }
                }
            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
                int state = intent.getIntExtra("state", 0);
                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
                synchronized (mConnectedDevices) {
                    boolean isConnected =
                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
                    if (state == 0 && isConnected) {
                        AudioSystem.setDeviceConnectionState(
                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                         "");
                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
                    } else if (state == 1 && !isConnected)  {
                        AudioSystem.setDeviceConnectionState(
                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
                                                         "");
                        mConnectedDevices.put(
                                new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
                    }
                }
                handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_AUX_DIGITAL, "");
            } else if (action.equals(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG)) {
                state = intent.getIntExtra("state", 0);
                Log.v(TAG,
                      "Broadcast Receiver Got ACTION_DIGITAL_AUDIO_DOCK_PLUG, state = " + state);
                handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, "");
            } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
                           action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
                state = intent.getIntExtra("state", 0);
                int alsaCard = intent.getIntExtra("card", -1);
                int alsaDevice = intent.getIntExtra("device", -1);
                String params = "card=" + alsaCard + ";device=" + alsaDevice;
                device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
                        AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
                Log.v(TAG, "Broadcast Receiver: Got "
                        + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
                              "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
                        + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
                handleDeviceConnection((state == 1), device, params);
            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                boolean broadcast = false;
                int state = AudioManager.SCO_AUDIO_STATE_ERROR;
                int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
                synchronized (mScoClients) {
                    int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                    // broadcast intent if the connection was initated by AudioService
@@ -3002,7 +2955,7 @@ public class AudioService extends IAudioService.Stub {
                    }
                    switch (btState) {
                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                        state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                        scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
@@ -3010,7 +2963,7 @@ public class AudioService extends IAudioService.Stub {
                        }
                        break;
                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
                        state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
                        scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
                        mScoAudioState = SCO_STATE_INACTIVE;
                        clearAllScoClients(0, false);
                        break;
@@ -3027,11 +2980,11 @@ public class AudioService extends IAudioService.Stub {
                    }
                }
                if (broadcast) {
                    broadcastScoConnectionState(state);
                    broadcastScoConnectionState(scoAudioState);
                    //FIXME: this is to maintain compatibility with deprecated intent
                    // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
                    Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
                    mContext.sendStickyBroadcast(newIntent);
                }
            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+11 −0
Original line number Diff line number Diff line
@@ -202,6 +202,9 @@ public class AudioSystem
    public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
    public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
    public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
    public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
    public static final int DEVICE_OUT_USB_DEVICE = 0x4000;

    public static final int DEVICE_OUT_DEFAULT = 0x8000;
    public static final int DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE |
                                              DEVICE_OUT_SPEAKER |
@@ -216,10 +219,18 @@ public class AudioSystem
                                              DEVICE_OUT_AUX_DIGITAL |
                                              DEVICE_OUT_ANLG_DOCK_HEADSET |
                                              DEVICE_OUT_DGTL_DOCK_HEADSET |
                                              DEVICE_OUT_USB_ACCESSORY |
                                              DEVICE_OUT_USB_DEVICE |
                                              DEVICE_OUT_DEFAULT);
    public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
                                                   DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                                                   DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
    public static final int DEVICE_OUT_ALL_SCO = (DEVICE_OUT_BLUETOOTH_SCO |
                                                  DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
                                                  DEVICE_OUT_BLUETOOTH_SCO_CARKIT);
    public static final int DEVICE_OUT_ALL_USB = (DEVICE_OUT_USB_ACCESSORY |
                                                  DEVICE_OUT_USB_DEVICE);

    // input devices
    public static final int DEVICE_IN_COMMUNICATION = 0x10000;
    public static final int DEVICE_IN_AMBIENT = 0x20000;
+2 −2
Original line number Diff line number Diff line
@@ -301,13 +301,13 @@ class WiredAccessoryObserver extends UEventObserver {

                //  Pack up the values and broadcast them to everyone
                if (headset == BIT_USB_HEADSET_ANLG) {
                    intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
                    intent = new Intent(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    intent.putExtra("state", state);
                    intent.putExtra("name", headsetName);
                    ActivityManagerNative.broadcastStickyIntent(intent, null);
                } else if (headset == BIT_USB_HEADSET_DGTL) {
                    intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
                    intent = new Intent(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    intent.putExtra("state", state);
                    intent.putExtra("name", headsetName);