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

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

Activate the last connected BT device am: 190a6210

parents a1b9acc4 190a6210
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1292,7 +1292,7 @@ public class A2dpService extends ProfileService {
    /**
     * Retrieves the most recently connected device in the A2DP connected devices list.
     */
    private BluetoothDevice getFallbackDevice() {
    public BluetoothDevice getFallbackDevice() {
        DatabaseManager dbManager = mAdapterService.getDatabase();
        return dbManager != null ? dbManager
            .getMostRecentlyConnectedDevicesInList(getConnectedDevices())
+111 −21
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.os.Message;
import android.util.Log;

import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
@@ -51,8 +52,10 @@ import java.util.Objects;

/**
 * The active device manager is responsible for keeping track of the
 * connected A2DP/HFP/AVRCP/HearingAid devices and select which device is
 * connected A2DP/HFP/AVRCP/HearingAid/LE audio devices and select which device is
 * active (for each profile).
 * The active device manager selects a fallback device when the currently active device
 * is disconnected, and it selects BT devices that are lastly activated one.
 *
 * Current policy (subject to change):
 * 1) If the maximum number of connected devices is one, the manager doesn't
@@ -66,24 +69,24 @@ import java.util.Objects;
 *    - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP
 *    - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP
 *    - BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED for HearingAid
 *    - BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED for LE audio
 *    If such broadcast is received (e.g., triggered indirectly by user
 *    action on the UI), the device in the received broacast is marked
 *    action on the UI), the device in the received broadcast is marked
 *    as the current active device for that profile.
 * 5) If there is a HearingAid active device, then A2DP and HFP active devices
 *    must be set to null (i.e., A2DP and HFP cannot have active devices).
 *    The reason is because A2DP or HFP cannot be used together with HearingAid.
 * 5) If there is a HearingAid active device, then A2DP, HFP and LE audio active devices
 *    must be set to null (i.e., A2DP, HFP and LE audio cannot have active devices).
 *    The reason is that A2DP, HFP or LE audio cannot be used together with HearingAid.
 * 6) If there are no connected devices (e.g., during startup, or after all
 *    devices have been disconnected, the active device per profile
 *    (A2DP/HFP/HearingAid) is selected as follows:
 *    (A2DP/HFP/HearingAid/LE audio) is selected as follows:
 * 6.1) The last connected HearingAid device is selected as active.
 *      If there is an active A2DP or HFP device, those must be set to null.
 * 6.2) The last connected A2DP or HFP device is selected as active.
 *      If there is an active A2DP, HFP or LE audio device, those must be set to null.
 * 6.2) The last connected A2DP, HFP or LE audio device is selected as active.
 *      However, if there is an active HearingAid device, then the
 *      A2DP or HFP active device is not set (must remain null).
 *      A2DP, HFP, or LE audio active device is not set (must remain null).
 * 7) If the currently active device (per profile) is disconnected, the
 *    Active Device Manager just marks that the profile has no active device,
 *    but does not attempt to select a new one. Currently, the expectation is
 *    that the user will explicitly select the new active device.
 *    and the lastly activated BT device that is still connected would be selected.
 * 8) If there is already an active device, and the corresponding
 *    ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device
 *    contained in the broadcast is marked as active. However, if
@@ -91,7 +94,7 @@ import java.util.Objects;
 *    as having no active device.
 * 9) If a wired audio device is connected, the audio output is switched
 *    by the Audio Framework itself to that device. We detect this here,
 *    and the active device for each profile (A2DP/HFP/HearingAid) is set
 *    and the active device for each profile (A2DP/HFP/HearingAid/LE audio) is set
 *    to null to reflect the output device state change. However, if the
 *    wired audio device is disconnected, we don't do anything explicit
 *    and apply the default behavior instead:
@@ -252,10 +255,12 @@ class ActiveDeviceManager {
                                    + "device " + device + " disconnected");
                        }
                        mA2dpConnectedDevices.remove(device);
                        if (mA2dpConnectedDevices.isEmpty()
                                && Objects.equals(mA2dpActiveDevice, device)) {
                        if (Objects.equals(mA2dpActiveDevice, device)) {
                            if (mA2dpConnectedDevices.isEmpty()) {
                                setA2dpActiveDevice(null);
                            }
                            setFallbackDeviceActive();
                        }
                    }
                }
                break;
@@ -314,10 +319,12 @@ class ActiveDeviceManager {
                                    + "device " + device + " disconnected");
                        }
                        mHfpConnectedDevices.remove(device);
                        if (mHfpConnectedDevices.isEmpty()
                                && Objects.equals(mHfpActiveDevice, device)) {
                        if (Objects.equals(mHfpActiveDevice, device)) {
                            if (mHfpConnectedDevices.isEmpty()) {
                                setHfpActiveDevice(null);
                            }
                            setFallbackDeviceActive();
                        }
                    }
                }
                break;
@@ -392,10 +399,12 @@ class ActiveDeviceManager {
                                    + "_CHANGED): device " + device + " disconnected");
                        }
                        mLeAudioConnectedDevices.remove(device);
                        if (mLeAudioConnectedDevices.isEmpty()
                                && Objects.equals(mLeAudioActiveDevice, device)) {
                        if (Objects.equals(mLeAudioActiveDevice, device)) {
                            if (mLeAudioConnectedDevices.isEmpty()) {
                                setLeAudioActiveDevice(null);
                            }
                            setFallbackDeviceActive();
                        }
                    }
                }
                break;
@@ -658,6 +667,87 @@ class ActiveDeviceManager {
        }
    }

    private void setFallbackDeviceActive() {
        if (DBG) {
            Log.d(TAG, "setFallbackDeviceActive");
        }
        DatabaseManager dbManager = mAdapterService.getDatabase();
        if (dbManager == null) {
            return;
        }

        A2dpService a2dpService = mFactory.getA2dpService();
        BluetoothDevice a2dpFallbackDevice = null;
        if (a2dpService != null) {
            a2dpFallbackDevice = a2dpService.getFallbackDevice();
        }

        HeadsetService headsetService = mFactory.getHeadsetService();
        BluetoothDevice headsetFallbackDevice = null;
        if (headsetService != null) {
            headsetFallbackDevice = headsetService.getFallbackDevice();
        }

        List<BluetoothDevice> connectedDevices = new LinkedList<>();
        connectedDevices.addAll(mLeAudioConnectedDevices);
        switch (mAudioManager.getMode()) {
            case AudioManager.MODE_NORMAL:
                if (a2dpFallbackDevice != null) {
                    connectedDevices.add(a2dpFallbackDevice);
                }
                break;
            case AudioManager.MODE_RINGTONE:
                if (headsetFallbackDevice != null && headsetService.isInbandRingingEnabled()) {
                    connectedDevices.add(headsetFallbackDevice);
                }
                break;
            default:
                if (headsetFallbackDevice != null) {
                    connectedDevices.add(headsetFallbackDevice);
                }
        }
        BluetoothDevice device = dbManager.getMostRecentlyConnectedDevicesInList(connectedDevices);
        if (device != null) {
            if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
                if (Objects.equals(a2dpFallbackDevice, device)) {
                    if (DBG) {
                        Log.d(TAG, "set A2DP device active: " + device);
                    }
                    setA2dpActiveDevice(device);
                    if (headsetFallbackDevice != null) {
                        setHfpActiveDevice(device);
                        setLeAudioActiveDevice(null);
                    }
                } else {
                    if (DBG) {
                        Log.d(TAG, "set LE audio device active: " + device);
                    }
                    setLeAudioActiveDevice(device);
                    setA2dpActiveDevice(null);
                    setHfpActiveDevice(null);
                }
            } else {
                if (Objects.equals(headsetFallbackDevice, device)) {
                    if (DBG) {
                        Log.d(TAG, "set HFP device active: " + device);
                    }
                    setHfpActiveDevice(device);
                    if (a2dpFallbackDevice != null) {
                        setA2dpActiveDevice(a2dpFallbackDevice);
                        setLeAudioActiveDevice(null);
                    }
                } else {
                    if (DBG) {
                        Log.d(TAG, "set LE audio device active: " + device);
                    }
                    setLeAudioActiveDevice(device);
                    setA2dpActiveDevice(null);
                    setHfpActiveDevice(null);
                }
            }
        }
    }

    private void resetState() {
        mA2dpConnectedDevices.clear();
        mA2dpActiveDevice = null;
+7 −2
Original line number Diff line number Diff line
@@ -1927,7 +1927,12 @@ public class HeadsetService extends ProfileService {
        return true;
    }

    boolean isInbandRingingEnabled() {
    /**
     * Checks if headset devices are able to get inband ringing.
     *
     * @return True if inband ringing is enabled.
     */
    public boolean isInbandRingingEnabled() {
        boolean isInbandRingingSupported = getResources().getBoolean(
                com.android.bluetooth.R.bool.config_bluetooth_hfp_inband_ringing_support);
        return isInbandRingingSupported && !SystemProperties.getBoolean(
@@ -2176,7 +2181,7 @@ public class HeadsetService extends ProfileService {
    /**
     * Retrieves the most recently connected device in the A2DP connected devices list.
     */
    private BluetoothDevice getFallbackDevice() {
    public BluetoothDevice getFallbackDevice() {
        DatabaseManager dbManager = mAdapterService.getDatabase();
        return dbManager != null ? dbManager
            .getMostRecentlyConnectedDevicesInList(getConnectedDevices())
+52 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;

@MediumTest
@RunWith(AndroidJUnit4.class)
public class ActiveDeviceManagerTest {
@@ -473,6 +475,56 @@ public class ActiveDeviceManagerTest {
        Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice());
    }

    /**
     * An LE Audio connected. An A2DP connected. The A2DP disconnected.
     * Then the LE Audio should be the active one.
     */
    @Test
    public void leAudioAndA2dpConnectedThenA2dpDisconnected_fallbackToLeAudio() {
        leAudioConnected(mLeAudioDevice);
        verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice);

        a2dpConnected(mA2dpDevice);
        verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice);

        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
        when(mDatabaseManager.getMostRecentlyConnectedDevicesInList(any())).thenAnswer(
                invocation -> {
                    List<BluetoothDevice> devices = invocation.getArgument(0);
                    return (devices != null && devices.contains(mLeAudioDevice))
                            ? mLeAudioDevice : null;
                }
        );
        a2dpDisconnected(mA2dpDevice);
        verify(mA2dpService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(isNull());
        verify(mLeAudioService, timeout(TIMEOUT_MS).times(2)).setActiveDevice(mLeAudioDevice);
    }

    /**
     * An A2DP connected. An LE Audio connected. The LE Audio disconnected.
     * Then the A2DP should be the active one.
     */
    @Test
    public void a2dpAndLeAudioConnectedThenLeAudioDisconnected_fallbackToA2dp() {
        a2dpConnected(mA2dpDevice);
        verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice);

        leAudioConnected(mLeAudioDevice);
        verify(mLeAudioService, timeout(TIMEOUT_MS)).setActiveDevice(mLeAudioDevice);

        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
        when(mA2dpService.getFallbackDevice()).thenReturn(mA2dpDevice);
        when(mDatabaseManager.getMostRecentlyConnectedDevicesInList(any())).thenAnswer(
                invocation -> {
                    List<BluetoothDevice> devices = invocation.getArgument(0);
                    return (devices != null && devices.contains(mA2dpDevice)) ? mA2dpDevice : null;
                }
        );
        leAudioDisconnected(mLeAudioDevice);
        verify(mLeAudioService, timeout(TIMEOUT_MS).atLeast(1)).setActiveDevice(isNull());
        verify(mA2dpService, timeout(TIMEOUT_MS).times(2)).setActiveDevice(mA2dpDevice);
    }

    /**
     * One LE Hearing Aid is connected.
     */