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

Commit c7e85802 authored by Angela Wang's avatar Angela Wang
Browse files

Fix some connection issues for dual mode hearing devices

1. Fix NPE crash when ASHA profile connected
For dual mode device, the other device in the same pair will be grouped in memberDevice rather than grouped in subDevice. Before switchSubDeviceContent() is called, we should make sure that subDevice is really exist, otherwise, switchMemberDeviceContent() should be called instead.

2. Fix UI issue when main device is disconnected
When the devices in the same group is connected/disconnected, CsipDeviceManager will update the relationship between main/member devices. However, when we turn off the LE audio toggle for the dual moded devices, the CSIP will be disabled and hence the CsipDeviceManager will not be called to update the relationship. To correctly update the relationship between devices in the same pair when LE audio toggle is off, we should call switchMemberDeviceContent() in HearingAidDeviceManager when ASHA profile is connected/disconnected.

Bug: 345485443
Bug: 345379628
Flag: EXEMPT bugfix
Test: atest HearingAidDeviceManagerTest
Test: manually check the ui and no crashes occur
Change-Id: I266e7228f6a7c4f67fef1da5b20a1962927a8e48
parent 2767bf3c
Loading
Loading
Loading
Loading
+36 −23
Original line number Diff line number Diff line
@@ -209,44 +209,34 @@ public class HearingAidDeviceManager {
                CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
                if (mainDevice != null) {
                    if (mainDevice.isConnected()) {
                        // When main device exists and in connected state, receiving sub device
                        // connection. To refresh main device UI
                        // Sub/member device is connected and main device is connected
                        // To refresh main device UI
                        mainDevice.refresh();
                    } else {
                        // When both Hearing Aid devices are disconnected, receiving sub device
                        // connection. To switch content and dispatch to notify UI change
                        mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
                        mainDevice.switchSubDeviceContent();
                        mainDevice.refresh();
                        // It is necessary to do remove and add for updating the mapping on
                        // preference and device
                        mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
                        // Sub/member device is connected and main device is disconnected
                        // To switch content and dispatch to notify UI change
                        switchDeviceContent(mainDevice, cachedDevice);
                    }
                    return true;
                }
                break;
            case BluetoothProfile.STATE_DISCONNECTED:
                mainDevice = findMainDevice(cachedDevice);
                if (cachedDevice.getUnpairing()) {
                    return true;
                }
                mainDevice = findMainDevice(cachedDevice);
                if (mainDevice != null) {
                    // When main device exists, receiving sub device disconnection
                    // Sub/member device is disconnected and main device exists
                    // To update main device UI
                    mainDevice.refresh();
                    return true;
                }
                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
                if (subDevice != null && subDevice.isConnected()) {
                    // Main device is disconnected and sub device is connected
                    // To copy data from sub device to main device
                    mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
                    cachedDevice.switchSubDeviceContent();
                    cachedDevice.refresh();
                    // It is necessary to do remove and add for updating the mapping on
                    // preference and device
                    mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);

                CachedBluetoothDevice connectedSecondaryDevice = getConnectedSecondaryDevice(
                        cachedDevice);
                if (connectedSecondaryDevice != null) {
                    // Main device is disconnected and sub/member device is connected
                    // To switch content and dispatch to notify UI change
                    switchDeviceContent(cachedDevice, connectedSecondaryDevice);
                    return true;
                }
                break;
@@ -254,6 +244,29 @@ public class HearingAidDeviceManager {
        return false;
    }

    private void switchDeviceContent(CachedBluetoothDevice mainDevice,
            CachedBluetoothDevice secondaryDevice) {
        mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
        if (mainDevice.getSubDevice() != null
                && mainDevice.getSubDevice().equals(secondaryDevice)) {
            mainDevice.switchSubDeviceContent();
        } else {
            mainDevice.switchMemberDeviceContent(secondaryDevice);
        }
        mainDevice.refresh();
        // It is necessary to do remove and add for updating the mapping on
        // preference and device
        mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
    }

    private CachedBluetoothDevice getConnectedSecondaryDevice(CachedBluetoothDevice cachedDevice) {
        if (cachedDevice.getSubDevice() != null && cachedDevice.getSubDevice().isConnected()) {
            return cachedDevice.getSubDevice();
        }
        return cachedDevice.getMemberDevice().stream().filter(
                CachedBluetoothDevice::isConnected).findAny().orElse(null);
    }

    void onActiveDeviceChanged(CachedBluetoothDevice device) {
        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) {
            if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice(
+47 −0
Original line number Diff line number Diff line
@@ -681,6 +681,53 @@ public class HearingAidDeviceManagerTest {
        verify(mCachedDevice1).refresh();
    }


    /**
     * Test onProfileConnectionStateChangedIfProcessed.
     * When main device is disconnected, to verify switch() result for member device connected
     * event
     */
    @Test
    public void onProfileConnectionStateChanged_connect_member_mainDisconnected_switch() {
        when(mCachedDevice1.isConnected()).thenReturn(false);
        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.addMemberDevice(mCachedDevice2);

        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
                mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();

        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
        verify(mCachedDevice1).refresh();
    }

    /**
     * Test onProfileConnectionStateChangedIfProcessed.
     * When member device is connected, to verify switch() result for main device disconnected
     * event
     */
    @Test
    public void onProfileConnectionStateChanged_disconnect_main_subDeviceConnected_switch() {
        when(mCachedDevice2.isConnected()).thenReturn(true);
        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1);
        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
        mCachedDevice1.addMemberDevice(mCachedDevice2);

        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
                mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue();

        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
        verify(mCachedDevice1).refresh();
    }

    @Test
    public void onActiveDeviceChanged_connected_callSetStrategies() {
        when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(