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

Commit bc3829e9 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

Hearing Aid: fix logic in connect and disconnect

* Hearing Aid Service reads the HiSyncId map and connect to the given
device and also the other side device (if any) at the same time. Before
connecting, the Hearing Aid Service disconnects devices from other pair.
* Simplify the logic in connect and disconnect to handle devices with
same HiSyncId.

Bug: 69623109
Test: unit test
Change-Id: Ib6e966e2aa51c991d89c9993f685aa15c039b89b
(cherry picked from commit 8b683ddb8ff86882ba18e05246d1b5b4fdb44b29)
parent 98b3ce1f
Loading
Loading
Loading
Loading
+42 −28
Original line number Diff line number Diff line
@@ -211,6 +211,9 @@ public class HearingAidService extends ProfileService {
        if (DBG) {
            Log.d(TAG, "connect(): " + device);
        }
        if (device == null) {
            return false;
        }

        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
            return false;
@@ -221,16 +224,35 @@ public class HearingAidService extends ProfileService {
            return false;
        }

        long hiSyncId = mDeviceHiSyncIdMap.getOrDefault(device,
                BluetoothHearingAid.HI_SYNC_ID_INVALID);

        if (hiSyncId != mActiveDeviceHiSyncId) {
            for (BluetoothDevice connectedDevice : getConnectedDevices()) {
                disconnect(connectedDevice);
            }
        }

        for (BluetoothDevice storedDevice : mDeviceHiSyncIdMap.keySet()) {
            if (mDeviceHiSyncIdMap.getOrDefault(storedDevice,
                    BluetoothHearingAid.HI_SYNC_ID_INVALID) == hiSyncId) {
                synchronized (mStateMachines) {
            HearingAidStateMachine smConnect = getOrCreateStateMachine(device);
            if (smConnect == null) {
                Log.e(TAG, "Cannot connect to " + device + " : no state machine");
                return false;
                    HearingAidStateMachine sm = getOrCreateStateMachine(storedDevice);
                    if (sm == null) {
                        Log.e(TAG, "Ignored connect request for " + device + " : no state machine");
                        continue;
                    }
            smConnect.sendMessage(HearingAidStateMachine.CONNECT);
            return true;
                    sm.sendMessage(HearingAidStateMachine.CONNECT);

                }
                if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID
                        && !device.equals(storedDevice)) {
                    break;
                }
            }
        }
        return true;
    }

    boolean disconnect(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
@@ -242,32 +264,24 @@ public class HearingAidService extends ProfileService {
        }
        long hiSyncId = mDeviceHiSyncIdMap.getOrDefault(device,
                BluetoothHearingAid.HI_SYNC_ID_INVALID);
        synchronized (mStateMachines) {
            HearingAidStateMachine sm = mStateMachines.get(device);
            if (sm == null) {
                Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine");
            } else {
                sm.sendMessage(HearingAidStateMachine.DISCONNECT);
            }
        }
        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
            return true;
        }

        for (BluetoothDevice storedDevice : mDeviceHiSyncIdMap.keySet()) {
            if (mDeviceHiSyncIdMap.getOrDefault(storedDevice,
                    BluetoothHearingAid.HI_SYNC_ID_INVALID) != hiSyncId
                    || storedDevice.equals(device)) {
                continue;
            }
                    BluetoothHearingAid.HI_SYNC_ID_INVALID) == hiSyncId) {
                synchronized (mStateMachines) {
                    HearingAidStateMachine sm = mStateMachines.get(storedDevice);
                    if (sm == null) {
                    Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine");
                        Log.e(TAG, "Ignored disconnect request for " + device
                                + " : no state machine");
                        continue;
                    }
                    sm.sendMessage(HearingAidStateMachine.DISCONNECT);
                }
                if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID
                        && !device.equals(storedDevice)) {
                    break;
                }
            }
        }
        return true;
    }
+89 −2
Original line number Diff line number Diff line
@@ -106,8 +106,8 @@ public class HearingAidServiceTest {

        // Get a device for testing
        mLeftDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
        mRightDevice = mAdapter.getRemoteDevice("00:01:02:03:04:06");
        mSingleDevice = mAdapter.getRemoteDevice("00:01:02:03:04:00");
        mRightDevice = mAdapter.getRemoteDevice("00:01:02:33:44:55");
        mSingleDevice = mAdapter.getRemoteDevice("10:11:12:13:14:15");
        mService.setPriority(mLeftDevice, BluetoothProfile.PRIORITY_UNDEFINED);
        mService.setPriority(mRightDevice, BluetoothProfile.PRIORITY_UNDEFINED);
        mService.setPriority(mSingleDevice, BluetoothProfile.PRIORITY_UNDEFINED);
@@ -337,6 +337,93 @@ public class HearingAidServiceTest {
                mService.getConnectionState(mLeftDevice));
    }

    /**
     * Test that the Hearing Aid Service connects to left and right device at the same time.
     */
    @Test
    public void testConnectAPair_connectBothDevices() {
        HearingAidStackEvent connCompletedEvent;

        // Update the device priority so okToConnect() returns true
        mService.setPriority(mLeftDevice, BluetoothProfile.PRIORITY_ON);
        mService.setPriority(mRightDevice, BluetoothProfile.PRIORITY_ON);
        doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
        doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));

        // Send a connect request
        Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));

        // Verify the connection state broadcast, and that we are in Connecting state
        verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
                BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
                mService.getConnectionState(mLeftDevice));
        verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
                BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
                mService.getConnectionState(mRightDevice));
    }

    /**
     * Test that the service disconnects the current pair before connecting to another pair.
     */
    @Test
    public void testConnectAnotherPair_disconnectCurrentPair() {
        HearingAidStackEvent connCompletedEvent;

        // Update the device priority so okToConnect() returns true
        mService.setPriority(mLeftDevice, BluetoothProfile.PRIORITY_ON);
        mService.setPriority(mRightDevice, BluetoothProfile.PRIORITY_ON);
        mService.setPriority(mSingleDevice, BluetoothProfile.PRIORITY_ON);
        doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
        doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));

        // Send a connect request
        Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));

        // Verify the connection state broadcast, and that we are in Connecting state
        verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
                BluetoothProfile.STATE_DISCONNECTED);
        verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
                BluetoothProfile.STATE_DISCONNECTED);


        // Send a message to trigger connection completed
        connCompletedEvent = new HearingAidStackEvent(
                HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
        connCompletedEvent.device = mLeftDevice;
        connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
        mService.messageFromNative(connCompletedEvent);
        connCompletedEvent = new HearingAidStackEvent(
                HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
        connCompletedEvent.device = mRightDevice;
        connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
        mService.messageFromNative(connCompletedEvent);

        // Verify the connection state broadcast, and that we are in Connected state for right side
        verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED,
                BluetoothProfile.STATE_CONNECTING);
        verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED,
                BluetoothProfile.STATE_CONNECTING);

        // Send a connect request for another pair
        Assert.assertTrue("Connect failed", mService.connect(mSingleDevice));

        // Verify the connection state broadcast, and that the first pair is in Disconnecting state
        verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING,
                BluetoothProfile.STATE_CONNECTED);
        verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTING,
                BluetoothProfile.STATE_CONNECTED);
        Assert.assertFalse(mService.getConnectedDevices().contains(mLeftDevice));
        Assert.assertFalse(mService.getConnectedDevices().contains(mRightDevice));

        // Verify the connection state broadcast, and that the second device is in Connecting state
        verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_CONNECTING,
                BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
                mService.getConnectionState(mSingleDevice));
    }

    /**
     * Test that the outgoing connect/disconnect and audio switch is successful.
     */