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

Commit 9b80c536 authored by Angela Wang's avatar Angela Wang
Browse files

Sync the preset for device that doesn't support synchronization

When one of the device that doesn't support synchronization back from a power cycle, we need to check if the presets of both ears are identical. If not, we should sync the preset to the latest value.

Bug: 333726416
Test: atest HearingAidDeviceManagerTest
Test: atest LocalBluetoothProfileManagerTest
Change-Id: I14c3f6b2722ed7c7d039a6dad7aa0812e231b587
parent c595dc65
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -175,6 +175,22 @@ public class CachedBluetoothDeviceManager {
        return null;
    }

    /**
     * Sync device status of the pair of the hearing aid if needed.
     *
     * @param device the remote device
     */
    public synchronized void syncDeviceWithinHearingAidSetIfNeeded(CachedBluetoothDevice device,
            int state, int profileId) {
        if (profileId == BluetoothProfile.HAP_CLIENT
                || profileId == BluetoothProfile.HEARING_AID
                || profileId == BluetoothProfile.CSIP_SET_COORDINATOR) {
            if (state == BluetoothProfile.STATE_CONNECTED) {
                mHearingAidDeviceManager.syncDeviceIfNeeded(device);
            }
        }
    }

    /**
     * Search for existing sub device {@link CachedBluetoothDevice}.
     *
+39 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package com.android.settingslib.bluetooth;

import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
@@ -108,6 +110,10 @@ public class HearingAidDeviceManager {
        return hiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
    }

    private boolean isValidGroupId(int groupId) {
        return groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
    }

    private CachedBluetoothDevice getCachedDevice(long hiSyncId) {
        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
@@ -258,6 +264,27 @@ public class HearingAidDeviceManager {
        }
    }

    void syncDeviceIfNeeded(CachedBluetoothDevice device) {
        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
        final HapClientProfile hap = profileManager.getHapClientProfile();
        // Sync preset if device doesn't support synchronization on the remote side
        if (hap != null && !hap.supportsSynchronizedPresets(device.getDevice())) {
            final CachedBluetoothDevice mainDevice = findMainDevice(device);
            if (mainDevice != null) {
                int mainPresetIndex = hap.getActivePresetIndex(mainDevice.getDevice());
                int presetIndex = hap.getActivePresetIndex(device.getDevice());
                if (mainPresetIndex != BluetoothHapClient.PRESET_INDEX_UNAVAILABLE
                        && mainPresetIndex != presetIndex) {
                    if (DEBUG) {
                        Log.d(TAG, "syncing preset from " + presetIndex + "->"
                                + mainPresetIndex + ", device=" + device);
                    }
                    hap.selectPreset(device.getDevice(), mainPresetIndex);
                }
            }
        }
    }

    private void setAudioRoutingConfig(CachedBluetoothDevice device) {
        AudioDeviceAttributes hearingDeviceAttributes =
                mRoutingHelper.getMatchedHearingDeviceAttributes(device);
@@ -326,7 +353,19 @@ public class HearingAidDeviceManager {
    }

    CachedBluetoothDevice findMainDevice(CachedBluetoothDevice device) {
        if (device == null || mCachedDevices == null) {
            return null;
        }

        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
            if (isValidGroupId(cachedDevice.getGroupId())) {
                Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
                for (CachedBluetoothDevice memberDevice : memberSet) {
                    if (memberDevice != null && memberDevice.equals(device)) {
                        return cachedDevice;
                    }
                }
            }
            if (isValidHiSyncId(cachedDevice.getHiSyncId())) {
                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
                if (subDevice != null && subDevice.equals(device)) {
+2 −0
Original line number Diff line number Diff line
@@ -408,6 +408,8 @@ public class LocalBluetoothProfileManager {
            boolean needDispatchProfileConnectionState = true;
            if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
                    || cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                mDeviceManager.syncDeviceWithinHearingAidSetIfNeeded(cachedDevice, newState,
                        mProfile.getProfileId());
                needDispatchProfileConnectionState = !mDeviceManager
                        .onProfileConnectionStateChangedIfProcessed(cachedDevice, newState,
                        mProfile.getProfileId());
+74 −11
Original line number Diff line number Diff line
@@ -72,14 +72,18 @@ public class HearingAidDeviceManagerTest {
    @Rule
    public MockitoRule mMockitoRule = MockitoJUnit.rule();

    private final static long HISYNCID1 = 10;
    private final static long HISYNCID2 = 11;
    private final static String DEVICE_NAME_1 = "TestName_1";
    private final static String DEVICE_NAME_2 = "TestName_2";
    private final static String DEVICE_ALIAS_1 = "TestAlias_1";
    private final static String DEVICE_ALIAS_2 = "TestAlias_2";
    private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
    private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
    private static final long HISYNCID1 = 10;
    private static final long HISYNCID2 = 11;
    private static final int GROUP_ID_1 = 20;
    private static final int GROUP_ID_2 = 21;
    private static final int PRESET_INDEX_1 = 1;
    private static final int PRESET_INDEX_2 = 2;
    private static final String DEVICE_NAME_1 = "TestName_1";
    private static final String DEVICE_NAME_2 = "TestName_2";
    private static final String DEVICE_ALIAS_1 = "TestAlias_1";
    private static final String DEVICE_ALIAS_2 = "TestAlias_2";
    private static final String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
    private static final String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
    private final BluetoothClass DEVICE_CLASS =
            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
    private final Context mContext = ApplicationProvider.getApplicationContext();
@@ -706,14 +710,73 @@ public class HearingAidDeviceManagerTest {
    }

    @Test
    public void findMainDevice() {
    public void findMainDevice_sameHiSyncId() {
        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.setSubDevice(mCachedDevice2);

        assertThat(mHearingAidDeviceManager.findMainDevice(mCachedDevice2)).
                isEqualTo(mCachedDevice1);
        assertThat(mHearingAidDeviceManager.findMainDevice(mCachedDevice2)).isEqualTo(
                mCachedDevice1);
    }

    @Test
    public void findMainDevice_sameGroupId() {
        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_2);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.addMemberDevice(mCachedDevice2);

        assertThat(mHearingAidDeviceManager.findMainDevice(mCachedDevice2)).isEqualTo(
                mCachedDevice1);
    }

    @Test
    public void syncDeviceWithinSet_synchronized_differentPresetIndex_shouldNotSync() {
        when(mHapClientProfile.getActivePresetIndex(mDevice1)).thenReturn(PRESET_INDEX_1);
        when(mHapClientProfile.getActivePresetIndex(mDevice2)).thenReturn(PRESET_INDEX_2);
        when(mHapClientProfile.supportsSynchronizedPresets(mDevice1)).thenReturn(true);
        when(mHapClientProfile.supportsSynchronizedPresets(mDevice2)).thenReturn(true);
        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_2);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.addMemberDevice(mCachedDevice2);

        mHearingAidDeviceManager.syncDeviceIfNeeded(mCachedDevice1);

        verify(mHapClientProfile, never()).selectPreset(any(), anyInt());
    }

    @Test
    public void syncDeviceWithinSet_unsynchronized_samePresetIndex_shouldNotSync() {
        when(mHapClientProfile.getActivePresetIndex(mDevice1)).thenReturn(PRESET_INDEX_1);
        when(mHapClientProfile.getActivePresetIndex(mDevice2)).thenReturn(PRESET_INDEX_1);
        when(mHapClientProfile.supportsSynchronizedPresets(mDevice1)).thenReturn(false);
        when(mHapClientProfile.supportsSynchronizedPresets(mDevice2)).thenReturn(false);
        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_2);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.addMemberDevice(mCachedDevice2);

        mHearingAidDeviceManager.syncDeviceIfNeeded(mCachedDevice1);

        verify(mHapClientProfile, never()).selectPreset(any(), anyInt());
    }

    @Test
    public void syncDeviceWithinSet_unsynchronized_differentPresetIndex_shouldSync() {
        when(mHapClientProfile.getActivePresetIndex(mDevice1)).thenReturn(PRESET_INDEX_1);
        when(mHapClientProfile.getActivePresetIndex(mDevice2)).thenReturn(PRESET_INDEX_2);
        when(mHapClientProfile.supportsSynchronizedPresets(mDevice1)).thenReturn(false);
        when(mHapClientProfile.supportsSynchronizedPresets(mDevice2)).thenReturn(false);
        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_2);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.addMemberDevice(mCachedDevice2);

        mHearingAidDeviceManager.syncDeviceIfNeeded(mCachedDevice2);

        verify(mHapClientProfile).selectPreset(mDevice2, PRESET_INDEX_1);
    }

    private HearingAidInfo getLeftAshaHearingAidInfo(long hiSyncId) {
+28 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothPan;
@@ -55,7 +56,9 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
    private final static long HISYNCID = 10;
    private static final long HISYNCID = 10;

    private static final int GROUP_ID = 1;
    @Mock
    private LocalBluetoothManager mBtManager;
    @Mock
@@ -201,7 +204,8 @@ public class LocalBluetoothProfileManagerTest {
     * CachedBluetoothDeviceManager method
     */
    @Test
    public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchDeviceManager() {
    public void
            stateChangedHandler_receiveHearingAidConnectionStateChanged_dispatchDeviceManager() {
        mShadowBluetoothAdapter.setSupportedProfiles(generateList(
                new int[] {BluetoothProfile.HEARING_AID}));
        mProfileManager.updateLocalProfiles();
@@ -218,6 +222,28 @@ public class LocalBluetoothProfileManagerTest {
                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
    }

    /**
     * Verify BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED with uuid intent will dispatch
     * to {@link CachedBluetoothDeviceManager} method
     */
    @Test
    public void stateChangedHandler_receiveHapClientConnectionStateChanged_dispatchDeviceManager() {
        mShadowBluetoothAdapter.setSupportedProfiles(generateList(
                new int[] {BluetoothProfile.HAP_CLIENT}));
        mProfileManager.updateLocalProfiles();
        when(mCachedBluetoothDevice.getGroupId()).thenReturn(GROUP_ID);

        mIntent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
        mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
        mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);

        mContext.sendBroadcast(mIntent);

        verify(mDeviceManager).syncDeviceWithinHearingAidSetIfNeeded(mCachedBluetoothDevice,
                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HAP_CLIENT);
    }

    /**
     * Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuid will dispatch to
     * profile connection state changed callback