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

Commit 394a8e1f authored by Paul McLean's avatar Paul McLean
Browse files

Handle simultaneous connect of devices with the same "type" (as in USB)

Change-Id: I163bf8e33ccf5aacc7ba21775916727430327f18
Bug: 19563570
parent ba4df962
Loading
Loading
Loading
Loading
+118 −70
Original line number Diff line number Diff line
@@ -141,6 +141,9 @@ public class AudioService extends IAudioService.Stub {
    /** debug calls to media session apis */
    private static final boolean DEBUG_SESSIONS = Log.isLoggable(TAG + ".SESSIONS", Log.DEBUG);

    /** debug calls to devices APIs */
    protected static final boolean DEBUG_DEVICES = Log.isLoggable(TAG + ".DEVICES", Log.DEBUG);

    /** Allow volume changes to set ringer mode to silent? */
    private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;

@@ -376,7 +379,32 @@ public class AudioService extends IAudioService.Stub {
    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();

    // Devices currently connected
    private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
    // Use makeDeviceListKey() to make a unique key for this list.
    private class DeviceListSpec {
        int mDeviceType;
        String mDeviceName;
        String mDeviceAddress;

        public DeviceListSpec(int deviceType, String deviceName, String deviceAddress) {
            mDeviceType = deviceType;
            mDeviceName = deviceName;
            mDeviceAddress = deviceAddress;
        }

        public String toString() {
            return "[type:0x" + Integer.toHexString(mDeviceType) + " name:" + mDeviceName
                    + " address:" + mDeviceAddress + "]";
        }
    }

    // Generate a unique key for the mConnectedDevices List by composing the device "type"
    // and the "address" associated with a specific instance of that device type
    private String makeDeviceListKey(int device, String deviceAddress) {
        return "0x" + Integer.toHexString(device) + ":" + deviceAddress;
    }

    private final HashMap<String, DeviceListSpec> mConnectedDevices =
            new HashMap<String, DeviceListSpec>();

    // Forced device usage for communications
    private int mForcedUseForComm;
@@ -520,6 +548,8 @@ public class AudioService extends IAudioService.Stub {
        return "card=" + card + ";device=" + device + ";";
    }

    private final String DEVICE_NAME_A2DP = "a2dp-device";

    ///////////////////////////////////////////////////////////////////////////
    // Construction
    ///////////////////////////////////////////////////////////////////////////
@@ -2771,10 +2801,13 @@ public class AudioService extends IAudioService.Stub {
            case BluetoothProfile.A2DP:
                synchronized (mConnectedDevices) {
                    synchronized (mA2dpAvrcpLock) {
                        mA2dp = null;
                        if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
                            makeA2dpDeviceUnavailableNow(
                                    mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
                        // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
                        for(Map.Entry<String, DeviceListSpec> entry
                                : mConnectedDevices.entrySet()) {
                            DeviceListSpec deviceSpec = entry.getValue();
                            if (deviceSpec.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
                                makeA2dpDeviceUnavailableNow(deviceSpec.mDeviceAddress);
                            }
                        }
                    }
                }
@@ -2782,9 +2815,13 @@ public class AudioService extends IAudioService.Stub {

            case BluetoothProfile.A2DP_SINK:
                synchronized (mConnectedDevices) {
                    if (mConnectedDevices.containsKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) {
                        makeA2dpSrcUnavailable(
                                mConnectedDevices.get(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP));
                    // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
                    for(Map.Entry<String, DeviceListSpec> entry
                            : mConnectedDevices.entrySet()) {
                        DeviceListSpec deviceSpec = entry.getValue();
                        if (deviceSpec.mDeviceType == AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) {
                            makeA2dpSrcUnavailable(deviceSpec.mDeviceAddress);
                        }
                    }
                }
                break;
@@ -3272,6 +3309,10 @@ public class AudioService extends IAudioService.Stub {
    public void setWiredDeviceConnectionState(int type, int state, String address,
            String name) {
        synchronized (mConnectedDevices) {
            if (DEBUG_DEVICES) {
                Slog.i(TAG, "setWiredDeviceConnectionState(" + state + " nm: " + name + " addr:"
                        + address + ")");
            }
            int delay = checkSendBecomingNoisyIntent(type, state);
            queueMsgUnderWakeLock(mAudioHandler,
                    MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
@@ -4236,13 +4277,13 @@ public class AudioService extends IAudioService.Stub {
                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
        setBluetoothA2dpOnInt(true);
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_AVAILABLE,
                address,
                "a2dp-device");
                AudioSystem.DEVICE_STATE_AVAILABLE, address, DEVICE_NAME_A2DP);
        // Reset A2DP suspend state each time a new sink is connected
        AudioSystem.setParameters("A2dpSuspended=false");
        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
                address);
        mConnectedDevices.put(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
                new DeviceListSpec(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, DEVICE_NAME_A2DP,
                                   address));
    }

    private void onSendBecomingNoisyIntent() {
@@ -4255,10 +4296,9 @@ public class AudioService extends IAudioService.Stub {
            mAvrcpAbsVolSupported = false;
        }
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_UNAVAILABLE,
                address,
                "a2dp-device");
        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, DEVICE_NAME_A2DP);
        mConnectedDevices.remove(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
        synchronized (mCurAudioRoutes) {
            // Remove A2DP routes as well
            if (mCurAudioRoutes.bluetoothName != null) {
@@ -4275,7 +4315,8 @@ public class AudioService extends IAudioService.Stub {
        // reconnection of the sink.
        AudioSystem.setParameters("A2dpSuspended=true");
        // the device will be made unavailable later, so consider it disconnected right away
        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
        mConnectedDevices.remove(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
        // send the delayed message to make the device unavailable later
        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
@@ -4285,20 +4326,19 @@ public class AudioService extends IAudioService.Stub {
    // must be called synchronized on mConnectedDevices
    private void makeA2dpSrcAvailable(String address) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_AVAILABLE,
                address,
                "a2dp-device");
        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP),
                address);
                AudioSystem.DEVICE_STATE_AVAILABLE, address, DEVICE_NAME_A2DP);
        mConnectedDevices.put(
                makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
                new DeviceListSpec(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, DEVICE_NAME_A2DP,
                                   address));
    }

    // must be called synchronized on mConnectedDevices
    private void makeA2dpSrcUnavailable(String address) {
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_UNAVAILABLE,
                address,
                "a2dp-device");
        mConnectedDevices.remove(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
                AudioSystem.DEVICE_STATE_UNAVAILABLE, address, DEVICE_NAME_A2DP);
        mConnectedDevices.remove(
                makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
    }

    // must be called synchronized on mConnectedDevices
@@ -4325,9 +4365,10 @@ public class AudioService extends IAudioService.Stub {
        }

        synchronized (mConnectedDevices) {
            boolean isConnected =
                (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
            String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                                           btDevice.getAddress());
            DeviceListSpec deviceSpec = mConnectedDevices.get(key);
            boolean isConnected = deviceSpec != null;

            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                if (btDevice.isBluetoothDock()) {
@@ -4388,9 +4429,9 @@ public class AudioService extends IAudioService.Stub {
        }

        synchronized (mConnectedDevices) {
                boolean isConnected =
                (mConnectedDevices.containsKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) &&
                 mConnectedDevices.get(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP).equals(address));
            String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
            DeviceListSpec deviceSpec = mConnectedDevices.get(key);
            boolean isConnected = deviceSpec != null;

            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                makeA2dpSrcUnavailable(address);
@@ -4413,26 +4454,31 @@ public class AudioService extends IAudioService.Stub {
        }
    }

    private boolean handleDeviceConnection(boolean connect, int device, String address, String deviceName) {
        Slog.i(TAG, "handleDeviceConnection(" + connect +
                " dev:" + Integer.toHexString(device) +
                " address:" + address +
                " name:" + deviceName + ")");
    private boolean handleDeviceConnection(boolean connect, int device, String address,
            String deviceName) {
        if (DEBUG_DEVICES) {
            Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" + Integer.toHexString(device)
                    + " address:" + address + " name:" + deviceName + ")");
        }
        synchronized (mConnectedDevices) {
            boolean isConnected = (mConnectedDevices.containsKey(device) &&
                    (address.isEmpty() || mConnectedDevices.get(device).equals(address)));

            if (isConnected && !connect) {
                AudioSystem.setDeviceConnectionState(device,
                                              AudioSystem.DEVICE_STATE_UNAVAILABLE,
            String deviceKey = makeDeviceListKey(device, address);
            if (DEBUG_DEVICES) {
                Slog.i(TAG, "deviceKey:" + deviceKey);
            }
            DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);
            boolean isConnected = deviceSpec != null;
            if (DEBUG_DEVICES) {
                Slog.i(TAG, "deviceSpec:" + deviceSpec + " is(already)Connected:" + isConnected);
            }
            if (connect && !isConnected) {
                AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
                        address, deviceName);
                 mConnectedDevices.remove(device);
                mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
                return true;
            } else if (!isConnected && connect) {
                 AudioSystem.setDeviceConnectionState(device,
                                                      AudioSystem.DEVICE_STATE_AVAILABLE,
            } else if (!connect && isConnected) {
                AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE,
                        address, deviceName);
                 mConnectedDevices.put(new Integer(device), address);
                mConnectedDevices.remove(deviceKey);
                return true;
            }
        }
@@ -4454,9 +4500,10 @@ public class AudioService extends IAudioService.Stub {
        int delay = 0;
        if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
            int devices = 0;
            for (int dev : mConnectedDevices.keySet()) {
                if (((dev & AudioSystem.DEVICE_BIT_IN) == 0) &&
                        ((dev & mBecomingNoisyIntentDevices) != 0)) {
            for (String key : mConnectedDevices.keySet()) {
                int dev = mConnectedDevices.get(key).mDeviceType;
                if (((dev & AudioSystem.DEVICE_BIT_IN) == 0)
                        && ((dev & mBecomingNoisyIntentDevices) != 0)) {
                    devices |= dev;
                }
            }
@@ -4485,12 +4532,13 @@ public class AudioService extends IAudioService.Stub {
        return delay;
    }

    private void sendDeviceConnectionIntent(int device, int state, String address, String deviceName)
    {
    private void sendDeviceConnectionIntent(int device, int state, String address,
            String deviceName) {
        if (DEBUG_DEVICES) {
            Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device) +
                " state:0x" + Integer.toHexString(state) +
                " address:" + address +
                    " state:0x" + Integer.toHexString(state) + " address:" + address +
                    " name:" + deviceName + ");");
        }
        Intent intent = new Intent();

        intent.putExtra(CONNECT_INTENT_KEY_STATE, state);
@@ -4544,12 +4592,12 @@ public class AudioService extends IAudioService.Stub {
    }

    private void onSetWiredDeviceConnectionState(int device, int state, String address,
            String deviceName)
    {
        Slog.i(TAG, "onSetWiredDeviceConnectionState(dev:" + Integer.toHexString(device)
                + " state:" + Integer.toHexString(state)
                + " address:" + address
                + " deviceName:" + deviceName + ");");
            String deviceName) {
        if (DEBUG_DEVICES) {
            Slog.i(TAG, "onSetWiredDeviceConnectionState(dev:" + Integer.toHexString(device) +
                    " state:" + Integer.toHexString(state) + " address:" + address +
                    " deviceName:" + deviceName + ");");
        }

        synchronized (mConnectedDevices) {
            if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
+23 −28
Original line number Diff line number Diff line
@@ -73,8 +73,6 @@ public final class UsbAlsaManager {

    private UsbAudioDevice mAccessoryAudioDevice = null;

    private UsbAudioDevice mSelectedAudioDevice = null;

    // UsbMidiDevice for USB peripheral mode (gadget) device
    private UsbMidiDevice mPeripheralMidiDevice = null;

@@ -186,6 +184,10 @@ public final class UsbAlsaManager {
                int device = (audioDevice == mAccessoryAudioDevice ?
                        AudioSystem.DEVICE_OUT_USB_ACCESSORY :
                        AudioSystem.DEVICE_OUT_USB_DEVICE);
                if (DEBUG) {
                    Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
                            " addr:" + address + " name:" + audioDevice.mDeviceName);
                }
                mAudioService.setWiredDeviceConnectionState(
                        device, state, address, audioDevice.mDeviceName);
            }
@@ -282,23 +284,13 @@ public final class UsbAlsaManager {
    /*
     * Select the default device of the specified card.
     */
    /* package */ boolean selectAudioCard(int card) {
    /* package */ UsbAudioDevice selectAudioCard(int card) {
        if (DEBUG) {
            Slog.d(TAG, "selectAudioCard() 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.
            notifyDeviceState(mSelectedAudioDevice, false);
            mSelectedAudioDevice = null;
            return null;
        }

        mDevicesParser.scan();
@@ -314,30 +306,30 @@ public final class UsbAlsaManager {

        // Playback device file needed/present?
        if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) {
            return false;
            return null;
        }

        // Capture device file needed/present?
        if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) {
            return false;
            return null;
        }

        if (DEBUG) {
            Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
        }

        mSelectedAudioDevice =
        UsbAudioDevice audioDevice =
                new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass);
        mSelectedAudioDevice.mDeviceName = mCardsParser.getCardRecordFor(card).mCardName;
        mSelectedAudioDevice.mDeviceDescription =
                mCardsParser.getCardRecordFor(card).mCardDescription;
        AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
        audioDevice.mDeviceName = cardRecord.mCardName;
        audioDevice.mDeviceDescription = cardRecord.mCardDescription;

        notifyDeviceState(mSelectedAudioDevice, true);
        notifyDeviceState(audioDevice, true);

        return true;
        return audioDevice;
    }

    /* package */ boolean selectDefaultDevice() {
    /* package */ UsbAudioDevice selectDefaultDevice() {
        if (DEBUG) {
            Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
        }
@@ -347,7 +339,8 @@ public final class UsbAlsaManager {

    /* package */ void usbDeviceAdded(UsbDevice usbDevice) {
       if (DEBUG) {
          Slog.d(TAG, "usbDeviceAdded(): " + usbDevice);
          Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() +
                  "nm:" + usbDevice.getProductName());
        }

        // Is there an audio interface in there?
@@ -384,8 +377,10 @@ public final class UsbAlsaManager {
        // If the default isn't a USB device, let the existing "select internal mechanism"
        // handle the selection.
        if (mCardsParser.isCardUsb(addedCard)) {
            selectAudioCard(addedCard);
            mAudioDevices.put(usbDevice, mSelectedAudioDevice);
            UsbAudioDevice audioDevice = selectAudioCard(addedCard);
            if (audioDevice != null) {
                mAudioDevices.put(usbDevice, audioDevice);
            }

            // look for MIDI devices

@@ -420,14 +415,14 @@ public final class UsbAlsaManager {

    /* package */ void usbDeviceRemoved(UsbDevice usbDevice) {
        if (DEBUG) {
          Slog.d(TAG, "deviceRemoved(): " + usbDevice);
          Slog.d(TAG, "deviceRemoved(): " + usbDevice.getManufacturerName() +
                  " " + usbDevice.getProductName());
        }

        UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice);
        if (audioDevice != null) {
            if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) {
                notifyDeviceState(audioDevice, false);
                mSelectedAudioDevice = null;

                // if there any external devices left, select one of them
                selectDefaultDevice();