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

Commit fec0a2b9 authored by Sungsoo Lim's avatar Sungsoo Lim Committed by Automerger Merge Worker
Browse files

Merge "Lazy activation of A2DP or HFP" am: 952b8688

parents 5bf1919d 952b8688
Loading
Loading
Loading
Loading
+115 −15
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.RejectedExecutionException;

/**
 * The active device manager is responsible for keeping track of the
@@ -130,15 +131,18 @@ class ActiveDeviceManager {
    private Handler mHandler = null;
    private final AudioManager mAudioManager;
    private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
    private final AudioManagerOnModeChangedListener mAudioManagerOnModeChangedListener;

    private final List<BluetoothDevice> mA2dpConnectedDevices = new ArrayList<>();
    private final List<BluetoothDevice> mHfpConnectedDevices = new ArrayList<>();
    private final List<BluetoothDevice> mHearingAidConnectedDevices = new ArrayList<>();
    private final List<BluetoothDevice> mLeAudioConnectedDevices = new ArrayList<>();
    private final List<BluetoothDevice> mLeHearingAidConnectedDevices = new ArrayList<>();
    private List<BluetoothDevice> mPendingLeHearingAidActiveDevice = new ArrayList<>();
    private final List<BluetoothDevice> mPendingLeHearingAidActiveDevice = new ArrayList<>();
    private BluetoothDevice mA2dpActiveDevice = null;
    private BluetoothDevice mPendingA2dpActiveDevice = null;
    private BluetoothDevice mHfpActiveDevice = null;
    private BluetoothDevice mPendingHfpActiveDevice = null;
    private BluetoothDevice mHearingAidActiveDevice = null;
    private BluetoothDevice mLeAudioActiveDevice = null;
    private BluetoothDevice mLeHearingAidActiveDevice = null;
@@ -245,16 +249,43 @@ class ActiveDeviceManager {
                        if (mA2dpConnectedDevices.contains(device)) {
                            break;      // The device is already connected
                        }
                        // New connected A2DP device
                        mA2dpConnectedDevices.add(device);
                        if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) {
                            // New connected device: select it as active
                            // Lazy active A2DP if it is not being used.
                            // This will prevent the deactivation of LE audio
                            // earlier than the activation of HFP.
                            switch (mAudioManager.getMode()) {
                                case AudioManager.MODE_NORMAL:
                                    break;
                                case AudioManager.MODE_RINGTONE: {
                                    HeadsetService headsetService = mFactory.getHeadsetService();
                                    if (mHfpActiveDevice == null && headsetService != null
                                            && headsetService.isInbandRingingEnabled()) {
                                        mPendingA2dpActiveDevice = device;
                                    }
                                    break;
                                }
                                case AudioManager.MODE_IN_CALL:
                                case AudioManager.MODE_IN_COMMUNICATION:
                                case AudioManager.MODE_CALL_SCREENING:
                                case AudioManager.MODE_CALL_REDIRECT:
                                case AudioManager.MODE_COMMUNICATION_REDIRECT: {
                                    if (mHfpActiveDevice == null) {
                                        mPendingA2dpActiveDevice = device;
                                    }
                                }
                            }
                            if (mPendingA2dpActiveDevice == null) {
                                // select the device as active if not lazy active
                                setA2dpActiveDevice(device);
                                setLeAudioActiveDevice(null);
                            }
                        }
                        break;
                    }
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                        // Device disconnected
                        // A2DP device disconnected
                        if (DBG) {
                            Log.d(TAG,
                                    "handleMessage(MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED): "
@@ -308,16 +339,37 @@ class ActiveDeviceManager {
                        if (mHfpConnectedDevices.contains(device)) {
                            break;      // The device is already connected
                        }
                        // New connected HFP device.
                        mHfpConnectedDevices.add(device);
                        if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null) {
                            // New connected device: select it as active
                            // Lazy active HFP if it is not being used.
                            // This will prevent the deactivation of LE audio
                            // earlier than the activation of A2DP.
                            switch (mAudioManager.getMode()) {
                                case AudioManager.MODE_NORMAL:
                                    if (mA2dpActiveDevice == null) {
                                        mPendingHfpActiveDevice = device;
                                    }
                                    break;
                                case AudioManager.MODE_RINGTONE: {
                                    HeadsetService headsetService = mFactory.getHeadsetService();
                                    if (headsetService == null
                                            || !headsetService.isInbandRingingEnabled()) {
                                        mPendingHfpActiveDevice = device;
                                    }
                                    break;
                                }
                            }
                            if (mPendingHfpActiveDevice == null) {
                                // select the device as active if not lazy active
                                setHfpActiveDevice(device);
                                setLeAudioActiveDevice(null);
                            }
                        }
                        break;
                    }
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                        // Device disconnected
                        // HFP device disconnected
                        if (DBG) {
                            Log.d(TAG,
                                    "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): "
@@ -371,7 +423,7 @@ class ActiveDeviceManager {
                            break;      // The device is already connected
                        }
                        mHearingAidConnectedDevices.add(device);
                        // New connected device: select it as active
                        // New connected hearing aid device: select it as active
                        setHearingAidActiveDevice(device);
                        setA2dpActiveDevice(null);
                        setHfpActiveDevice(null);
@@ -379,7 +431,7 @@ class ActiveDeviceManager {
                        break;
                    }
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                        // Device disconnected
                        // Hearing aid device disconnected
                        if (DBG) {
                            Log.d(TAG, "handleMessage(MESSAGE_HEARING_AID_ACTION_CONNECTION_STATE"
                                    + "_CHANGED): device " + device + " disconnected");
@@ -435,7 +487,7 @@ class ActiveDeviceManager {
                        mLeAudioConnectedDevices.add(device);
                        if (mHearingAidActiveDevice == null && mLeHearingAidActiveDevice == null
                                && mPendingLeHearingAidActiveDevice.isEmpty()) {
                            // New connected device: select it as active
                            // New connected LE audio device: select it as active
                            setLeAudioActiveDevice(device);
                            setA2dpActiveDevice(null);
                            setHfpActiveDevice(null);
@@ -448,7 +500,7 @@ class ActiveDeviceManager {
                        break;
                    }
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                        // Device disconnected
                        // LE audio device disconnected
                        if (DBG) {
                            Log.d(TAG, "handleMessage(MESSAGE_LE_AUDIO_ACTION_CONNECTION_STATE"
                                    + "_CHANGED): device " + device + " disconnected");
@@ -511,7 +563,7 @@ class ActiveDeviceManager {
                        } else if (Objects.equals(mLeAudioActiveDevice, device)) {
                            mLeHearingAidActiveDevice = device;
                        } else {
                            // New connected device: select it as active
                            // New connected LE hearing aid device: select it as active
                            setLeHearingAidActiveDevice(device);
                            setHearingAidActiveDevice(null);
                            setA2dpActiveDevice(null);
@@ -520,7 +572,7 @@ class ActiveDeviceManager {
                        break;
                    }
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                        // Device disconnected
                        // LE hearing aid device disconnected
                        if (DBG) {
                            Log.d(TAG, "handleMessage(MESSAGE_HAP_ACTION_CONNECTION_STATE"
                                    + "_CHANGED): device " + device + " disconnected");
@@ -558,6 +610,40 @@ class ActiveDeviceManager {
        }
    }

    private class AudioManagerOnModeChangedListener implements AudioManager.OnModeChangedListener {
        public void onModeChanged(int mode) {
            switch (mode) {
                case AudioManager.MODE_NORMAL: {
                    if (mPendingA2dpActiveDevice != null) {
                        setA2dpActiveDevice(mPendingA2dpActiveDevice);
                        setLeAudioActiveDevice(null);
                    }
                    break;
                }
                case AudioManager.MODE_RINGTONE: {
                    final HeadsetService headsetService = mFactory.getHeadsetService();
                    if (headsetService != null && headsetService.isInbandRingingEnabled()
                            && mPendingHfpActiveDevice != null) {
                        setHfpActiveDevice(mPendingHfpActiveDevice);
                        setLeAudioActiveDevice(null);
                    }
                    break;
                }
                case AudioManager.MODE_IN_CALL:
                case AudioManager.MODE_IN_COMMUNICATION:
                case AudioManager.MODE_CALL_SCREENING:
                case AudioManager.MODE_CALL_REDIRECT:
                case AudioManager.MODE_COMMUNICATION_REDIRECT:  {
                    if (mPendingHfpActiveDevice != null) {
                        setHfpActiveDevice(mPendingHfpActiveDevice);
                        setLeAudioActiveDevice(null);
                    }
                    break;
                }
            }
        }
    }

    /** Notifications of audio device connection and disconnection events. */
    @SuppressLint("AndroidFrameworkRequiresPermission")
    private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback {
@@ -604,6 +690,7 @@ class ActiveDeviceManager {
        mFactory = factory;
        mAudioManager = service.getSystemService(AudioManager.class);
        mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback();
        mAudioManagerOnModeChangedListener = new AudioManagerOnModeChangedListener();
    }

    void start() {
@@ -630,6 +717,11 @@ class ActiveDeviceManager {
        mAdapterService.registerReceiver(mReceiver, filter);

        mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
        mAudioManager.addOnModeChangedListener(command -> {
            if (!mHandler.post(command)) {
                throw new RejectedExecutionException(mHandler + " is shutting down");
            }
        }, mAudioManagerOnModeChangedListener);
    }

    void cleanup() {
@@ -672,6 +764,10 @@ class ActiveDeviceManager {
            return;
        }
        mA2dpActiveDevice = device;
        mPendingA2dpActiveDevice = null;
        if (mPendingHfpActiveDevice != null) {
            setHfpActiveDevice(mPendingHfpActiveDevice);
        }
    }

    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -687,6 +783,10 @@ class ActiveDeviceManager {
            return;
        }
        mHfpActiveDevice = device;
        mPendingHfpActiveDevice = null;
        if (mPendingA2dpActiveDevice != null) {
            setA2dpActiveDevice(mPendingA2dpActiveDevice);
        }
    }

    private void setHearingAidActiveDevice(BluetoothDevice device) {
+11 −1
Original line number Diff line number Diff line
@@ -236,6 +236,8 @@ public class ActiveDeviceManagerTest {
     */
    @Test
    public void onlyHeadsetConnected_setHeadsetActive() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);

        headsetConnected(mHeadsetDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice);
    }
@@ -245,6 +247,8 @@ public class ActiveDeviceManagerTest {
     */
    @Test
    public void secondHeadsetConnected_setSecondHeadsetActive() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);

        headsetConnected(mHeadsetDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice);

@@ -257,6 +261,8 @@ public class ActiveDeviceManagerTest {
     */
    @Test
    public void lastHeadsetDisconnected_clearHeadsetActive() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);

        headsetConnected(mHeadsetDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice);

@@ -269,6 +275,8 @@ public class ActiveDeviceManagerTest {
     */
    @Test
    public void headsetActiveDeviceSelected_setActive() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);

        headsetConnected(mHeadsetDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice);

@@ -288,6 +296,8 @@ public class ActiveDeviceManagerTest {
     */
    @Test
    public void headsetSecondDeviceDisconnected_fallbackDeviceActive() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);

        headsetConnected(mSecondaryAudioDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice);

@@ -501,7 +511,7 @@ public class ActiveDeviceManagerTest {
    public void leAudioActive_setHeadsetActiveExplicitly() {
        Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
                LeAudioService.isEnabled());

        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
        leAudioActiveDeviceChanged(mLeAudioDevice);
        headsetConnected(mA2dpHeadsetDevice);
        headsetActiveDeviceChanged(mA2dpHeadsetDevice);