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

Commit aef6efee authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents 4b0351d5 6159842d
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.
     */