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

Commit 898361c1 authored by Stanley Tng's avatar Stanley Tng Committed by android-build-merger
Browse files

Merge "Fix the Hearing Aids connected state in Settings App" into pi-dev

am: aef6efee

Change-Id: Ic4ac46d5de65f83ca9e5b27bef44de17c5b94fe8
parents 19e32a0e aef6efee
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -505,5 +505,6 @@ public class BluetoothEventManager {
                callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
            }
        }
        mDeviceManager.onProfileConnectionStateChanged(device, state, bluetoothProfile);
    }
}
+96 −7
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;

@@ -323,16 +324,31 @@ public class CachedBluetoothDeviceManager {
            if (cachedDevice.getHiSyncId() == hiSyncId) {
                if (firstMatchedIndex != -1) {
                    /* Found the second one */
                    mCachedDevices.remove(i);
                    mHearingAidDevicesNotAddedInCache.add(cachedDevice);
                    int indexToRemoveFromUi;
                    CachedBluetoothDevice deviceToRemoveFromUi;

                    // Since the hiSyncIds have been updated for a connected pair of hearing aids,
                    // we remove the entry of one the hearing aids from the UI. Unless the
                    // hiSyncId get updated, the system does not know its a hearing aid, so we add
                    // hiSyncId get updated, the system does not know it is a hearing aid, so we add
                    // both the hearing aids as separate entries in the UI first, then remove one
                    // of them after the hiSyncId is populated.
                    log("onHiSyncIdChanged: removed device=" + cachedDevice + ", with hiSyncId="
                        + hiSyncId);
                    mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
                    // of them after the hiSyncId is populated. We will choose the device that
                    // is not connected to be removed.
                    if (cachedDevice.isConnected()) {
                        indexToRemoveFromUi = firstMatchedIndex;
                        deviceToRemoveFromUi = mCachedDevices.get(firstMatchedIndex);
                        mCachedDevicesMapForHearingAids.put(hiSyncId, cachedDevice);
                    } else {
                        indexToRemoveFromUi = i;
                        deviceToRemoveFromUi = cachedDevice;
                        mCachedDevicesMapForHearingAids.put(hiSyncId,
                                                            mCachedDevices.get(firstMatchedIndex));
                    }

                    mCachedDevices.remove(indexToRemoveFromUi);
                    mHearingAidDevicesNotAddedInCache.add(deviceToRemoveFromUi);
                    log("onHiSyncIdChanged: removed from UI device=" + deviceToRemoveFromUi
                        + ", with hiSyncId=" + hiSyncId);
                    mBtManager.getEventManager().dispatchDeviceRemoved(deviceToRemoveFromUi);
                    break;
                } else {
                    mCachedDevicesMapForHearingAids.put(hiSyncId, cachedDevice);
@@ -342,6 +358,72 @@ public class CachedBluetoothDeviceManager {
        }
    }

    private CachedBluetoothDevice getHearingAidOtherDevice(CachedBluetoothDevice thisDevice,
                                                           long hiSyncId) {
        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
            return null;
        }

        // Searched the lists for the other side device with the matching hiSyncId.
        for (CachedBluetoothDevice notCachedDevice : mHearingAidDevicesNotAddedInCache) {
            if ((hiSyncId == notCachedDevice.getHiSyncId()) &&
                (!Objects.equals(notCachedDevice, thisDevice))) {
                return notCachedDevice;
            }
        }

        CachedBluetoothDevice cachedDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
        if (!Objects.equals(cachedDevice, thisDevice)) {
            return cachedDevice;
        }
        return null;
    }

    private void hearingAidSwitchDisplayDevice(CachedBluetoothDevice toDisplayDevice,
                                           CachedBluetoothDevice toHideDevice, long hiSyncId)
    {
        log("hearingAidSwitchDisplayDevice: toDisplayDevice=" + toDisplayDevice
            + ", toHideDevice=" + toHideDevice);

        // Remove the "toHideDevice" device from the UI.
        mHearingAidDevicesNotAddedInCache.add(toHideDevice);
        mCachedDevices.remove(toHideDevice);
        mBtManager.getEventManager().dispatchDeviceRemoved(toHideDevice);

        // Add the "toDisplayDevice" device to the UI.
        mHearingAidDevicesNotAddedInCache.remove(toDisplayDevice);
        mCachedDevices.add(toDisplayDevice);
        mCachedDevicesMapForHearingAids.put(hiSyncId, toDisplayDevice);
        mBtManager.getEventManager().dispatchDeviceAdded(toDisplayDevice);
    }

    public synchronized void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
                                                             int state, int bluetoothProfile) {
        if (bluetoothProfile == BluetoothProfile.HEARING_AID
            && cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
            && cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {

            long hiSyncId = cachedDevice.getHiSyncId();

            CachedBluetoothDevice otherDevice = getHearingAidOtherDevice(cachedDevice, hiSyncId);
            if (otherDevice == null) {
                // no other side device. Nothing to do.
                return;
            }

            if (state == BluetoothProfile.STATE_CONNECTED &&
                mHearingAidDevicesNotAddedInCache.contains(cachedDevice)) {
                hearingAidSwitchDisplayDevice(cachedDevice, otherDevice, hiSyncId);
            } else if (state == BluetoothProfile.STATE_DISCONNECTED
                       && otherDevice.isConnected()) {
                CachedBluetoothDevice mapDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
                if ((mapDevice != null) && (Objects.equals(cachedDevice, mapDevice))) {
                    hearingAidSwitchDisplayDevice(otherDevice, cachedDevice, hiSyncId);
                }
            }
        }
    }

    public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
        final long hiSyncId = device.getHiSyncId();

@@ -353,9 +435,16 @@ public class CachedBluetoothDeviceManager {
                // TODO: Look for more cleanups on unpairing the device.
                mHearingAidDevicesNotAddedInCache.remove(i);
                if (device == cachedDevice) continue;
                log("onDeviceUnpaired: Unpair device=" + cachedDevice);
                cachedDevice.unpair();
            }
        }

        CachedBluetoothDevice mappedDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
        if ((mappedDevice != null) && (!Objects.equals(device, mappedDevice))) {
            log("onDeviceUnpaired: Unpair mapped device=" + mappedDevice);
            mappedDevice.unpair();
        }
    }

    public synchronized void dispatchAudioModeChanged() {
+3 −0
Original line number Diff line number Diff line
@@ -98,5 +98,8 @@ public class BluetoothEventManagerTest {

        verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice,
                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);

        verify(mCachedDeviceManager).onProfileConnectionStateChanged(mCachedBluetoothDevice,
                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
    }
}
+189 −5
Original line number Diff line number Diff line
@@ -235,7 +235,7 @@ public class CachedBluetoothDeviceManagerTest {
     * Test to verify onHiSyncIdChanged() for hearing aid devices with same HiSyncId.
     */
    @Test
    public void testOnDeviceAdded_sameHiSyncId_populateInDifferentLists() {
    public void testOnHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
@@ -261,16 +261,53 @@ public class CachedBluetoothDeviceManagerTest {
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
        assertThat(devices).contains(cachedDevice2);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
            .contains(cachedDevice2);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids)
            .containsKey(HISYNCID1);
    }

    /**
     * Test to verify onHiSyncIdChanged() for 2 hearing aid devices with same HiSyncId but one
     * device is connected and other is disconnected. The connected device should be chosen.
     */
    @Test
    public void testOnHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice2);
        assertThat(cachedDevice2).isNotNull();
        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);

        /* Device 1 is connected and Device 2 is disconnected */
        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
            thenReturn(BluetoothProfile.STATE_CONNECTED);
        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
            thenReturn(BluetoothProfile.STATE_DISCONNECTED);

        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();

        cachedDevice1.setHiSyncId(HISYNCID1);
        cachedDevice2.setHiSyncId(HISYNCID1);
        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);

        // Only the connected device, device 1, should be visible to UI.
        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
            containsExactly(HISYNCID1, cachedDevice1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).
            containsExactly(cachedDevice2);
    }

    /**
     * Test to verify onHiSyncIdChanged() for hearing aid devices with different HiSyncId.
     */
    @Test
    public void testOnDeviceAdded_differentHiSyncId_populateInSameList() {
    public void testOnHiSyncIdChanged_differentHiSyncId_populateInSameList() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
@@ -302,6 +339,153 @@ public class CachedBluetoothDeviceManagerTest {
            .contains(cachedDevice2);
    }

    /**
     * Test to verify onProfileConnectionStateChanged() for single hearing aid device connection.
     */
    @Test
    public void testOnProfileConnectionStateChanged_singleDeviceConnected_visible() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);

        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();

        cachedDevice1.setHiSyncId(HISYNCID1);
        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);

        // Connect the Device 1
        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
            BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);

        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
            containsExactly(HISYNCID1, cachedDevice1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();

        // Disconnect the Device 1
        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
            BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);

        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
            containsExactly(HISYNCID1, cachedDevice1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
    }

    /**
     * Test to verify onProfileConnectionStateChanged() for two hearing aid devices where both
     * devices are disconnected and they get connected.
     */
    @Test
    public void testOnProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice2);
        assertThat(cachedDevice2).isNotNull();
        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);

        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();

        cachedDevice1.setHiSyncId(HISYNCID1);
        cachedDevice2.setHiSyncId(HISYNCID1);
        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);

        // There should be one cached device but can be either one.
        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);

        // Connect the Device 1
        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
            BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);

        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
            containsExactly(HISYNCID1, cachedDevice1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice2);
        assertThat(mCachedDeviceManager.mCachedDevices).contains(cachedDevice1);

        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
            thenReturn(BluetoothProfile.STATE_CONNECTED);
        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
            thenReturn(BluetoothProfile.STATE_DISCONNECTED);

        // Connect the Device 2
        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice2,
            BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);

        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
        assertThat(mCachedDeviceManager.mCachedDevices).hasSize(1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
    }

    /**
     * Test to verify onProfileConnectionStateChanged() for two hearing aid devices where both
     * devices are connected and they get disconnected.
     */
    @Test
    public void testOnProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
            mLocalProfileManager, mDevice2);
        assertThat(cachedDevice2).isNotNull();
        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
            thenReturn(BluetoothProfile.STATE_CONNECTED);
        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
            thenReturn(BluetoothProfile.STATE_CONNECTED);

        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();

        cachedDevice1.setHiSyncId(HISYNCID1);
        cachedDevice2.setHiSyncId(HISYNCID1);
        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);

        /* Disconnect the Device 1 */
        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
            BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);

        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice2);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
        assertThat(mCachedDeviceManager.mCachedDevices).contains(cachedDevice2);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids)
            .containsExactly(HISYNCID1, cachedDevice2);

        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
            thenReturn(BluetoothProfile.STATE_DISCONNECTED);
        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
            thenReturn(BluetoothProfile.STATE_CONNECTED);

        /* Disconnect the Device 2 */
        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice2,
            BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);

        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
        assertThat(mCachedDeviceManager.mCachedDevices).hasSize(1);
        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
    }

    /**
     * Test to verify OnDeviceUnpaired() for a paired hearing Aid device pair.
     */