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

Commit b05407bb authored by Thomas Stuart's avatar Thomas Stuart
Browse files

fix requestCallEndpointChange 2nd+ request to BT route

A bug was reported that requesting to switch to the BT route a 2nd time
would not trigger the onCurrentEndpointChanged callback.  Upon
inspection the request was not actually setting the aduio route and
short circuiting a success msg. This was ultimately due to the
isCurrentEndpointRequestedEndpoint method not enforcing a stricter
conditional check.

The fix is to ensure that the routes are the same before checking the bt
active device to deteremine if the user is requesting a new endpoint
that is already active. In short, the conditionals have been simplified.

Fixes: 290573705
Test: 2 new unit test + manual:
            (1) connect BT device to DUT
            (2) start MO using jetpack test app
            (3) switch CallEndpoint to EARPIECE
            (4) switch CallEndpoint back to BT
            expect: success; repeat steps 3+4 multi-times

Change-Id: I8d1c003f8269befc2cc26aafc60dfef3b80f70f3
parent f3be9529
Loading
Loading
Loading
Loading
+18 −13
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ public class CallEndpointController extends CallsManagerListenerBase {
    }

    public void requestCallEndpointChange(CallEndpoint endpoint, ResultReceiver callback) {
        Log.d(this, "requestCallEndpointChange %s", endpoint);
        Log.i(this, "requestCallEndpointChange %s", endpoint);
        int route = mTypeToRouteMap.get(endpoint.getEndpointType());
        String bluetoothAddress = getBluetoothAddress(endpoint);

@@ -99,7 +99,6 @@ public class CallEndpointController extends CallsManagerListenerBase {
        }

        if (isCurrentEndpointRequestedEndpoint(route, bluetoothAddress)) {
            Log.d(this, "requestCallEndpointChange: requested endpoint is already active");
            callback.send(CallEndpoint.ENDPOINT_OPERATION_SUCCESS, new Bundle());
            return;
        }
@@ -130,21 +129,27 @@ public class CallEndpointController extends CallsManagerListenerBase {
            return false;
        }
        CallAudioState currentAudioState = mCallsManager.getCallAudioManager().getCallAudioState();
        // requested non-bt endpoint is already active
        if (requestedRoute != CallAudioState.ROUTE_BLUETOOTH &&
                requestedRoute == currentAudioState.getRoute()) {
        if (requestedRoute == currentAudioState.getRoute()) {
            if (requestedRoute != CallAudioState.ROUTE_BLUETOOTH) {
                // The audio route (earpiece, speaker, etc.) is already active
                // and Telecom can ignore the spam request!
                Log.i(this, "iCERE: user requested a non-BT route that is already active");
                return true;
        }
        // requested bt endpoint is already active
        if (requestedRoute == CallAudioState.ROUTE_BLUETOOTH &&
                currentAudioState.getActiveBluetoothDevice() != null &&
                requestedAddress.equals(
                        currentAudioState.getActiveBluetoothDevice().getAddress())) {
            } else if (hasSameBluetoothAddress(currentAudioState, requestedAddress)) {
                // if the requested (BT route, device) is active, ignore the request...
                Log.i(this, "iCERE: user requested a BT endpoint that is already active");
                return true;
            }
        }
        return false;
    }

    public boolean hasSameBluetoothAddress(CallAudioState audioState, String requestedAddress) {
        boolean hasActiveBtDevice = audioState.getActiveBluetoothDevice() != null;
        return hasActiveBtDevice && requestedAddress.equals(
                audioState.getActiveBluetoothDevice().getAddress());
    }

    private Bundle getErrorResult(int result) {
        String message;
        int resultCode;
+65 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.telecom.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -50,7 +51,9 @@ import org.mockito.Mock;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@RunWith(JUnit4.class)
public class CallEndpointControllerTest extends TelecomTestCase {
@@ -81,6 +84,9 @@ public class CallEndpointControllerTest extends TelecomTestCase {
            availableBluetooth1);
    private static final CallAudioState audioState7 = new CallAudioState(false,
            CallAudioState.ROUTE_STREAMING, CallAudioState.ROUTE_ALL, null, availableBluetooth1);
    private static final CallAudioState audioState8 = new CallAudioState(false,
            CallAudioState.ROUTE_EARPIECE, CallAudioState.ROUTE_ALL, bluetoothDevice1,
            availableBluetooth2);

    private CallEndpointController mCallEndpointController;

@@ -177,6 +183,65 @@ public class CallEndpointControllerTest extends TelecomTestCase {
        verify(mConnectionService, never()).onMuteStateChanged(any(), anyBoolean());
    }

    /**
     * Ensure that {@link CallAudioManager#setAudioRoute(int, String)} is invoked when the user
     * requests to switch to a bluetooth CallEndpoint.  This is an edge case where bluetooth is not
     * the current CallEndpoint but the CallAudioState shows the bluetooth device is
     * active/available.
     */
    @Test
    public void testSwitchFromEarpieceToBluetooth() {
        // simulate an audio state where the EARPIECE is active but a bluetooth device is active.
        mCallEndpointController.onCallAudioStateChanged(null, audioState8 /* Ear but BT active */);
        CallEndpoint btEndpoint = mCallEndpointController.getAvailableEndpoints().stream()
                .filter(e -> e.getEndpointType() == CallEndpoint.TYPE_BLUETOOTH)
                .toList().get(0); // get the only available BT endpoint

        // verify the CallEndpointController shows EARPIECE active + BT endpoint is active device
        assertEquals(CallEndpoint.TYPE_EARPIECE,
                mCallEndpointController.getCurrentCallEndpoint().getEndpointType());
        assertNotNull(btEndpoint);

        // request an endpoint change from earpiece to the bluetooth
        doReturn(audioState8).when(mCallAudioManager).getCallAudioState();
        mCallEndpointController.requestCallEndpointChange(btEndpoint, mResultReceiver);

        // verify the transaction was successful and CallAudioManager#setAudioRoute was called
        verify(mResultReceiver, never()).send(eq(CallEndpoint.ENDPOINT_OPERATION_FAILED), any());
        verify(mCallAudioManager, times(1)).setAudioRoute(eq(CallAudioState.ROUTE_BLUETOOTH),
                eq(bluetoothDevice1.getAddress()));
    }


    /**
     * Ensure that {@link CallAudioManager#setAudioRoute(int, String)} is invoked when the user
     * requests to switch to from one bluetooth device to another.
     */
    @Test
    public void testBtDeviceSwitch() {
        // bluetoothDevice1 should start as active and bluetoothDevice2 is available
        mCallEndpointController.onCallAudioStateChanged(null, audioState2 /* BT active D1 */);
        CallEndpoint currentEndpoint = mCallEndpointController.getCurrentCallEndpoint();
        List<CallEndpoint> btEndpoints = mCallEndpointController.getAvailableEndpoints().stream()
                .filter(e -> e.getEndpointType() == CallEndpoint.TYPE_BLUETOOTH)
                .toList(); // get the only available BT endpoint

        // verify the initial state of the test
        assertEquals(2, btEndpoints.size());
        assertEquals(CallEndpoint.TYPE_BLUETOOTH, currentEndpoint.getEndpointType());
        assertEquals(currentEndpoint, btEndpoints.get(0));
        assertNotEquals(currentEndpoint, btEndpoints.get(1));

        // request an endpoint change from BT D1 --> BT D2
        doReturn(audioState2).when(mCallAudioManager).getCallAudioState();
        mCallEndpointController.requestCallEndpointChange(btEndpoints.get(1), mResultReceiver);

        // verify the transaction was successful and CallAudioManager#setAudioRoute was called
        verify(mResultReceiver, never()).send(eq(CallEndpoint.ENDPOINT_OPERATION_FAILED), any());
        verify(mCallAudioManager, times(1)).setAudioRoute(eq(CallAudioState.ROUTE_BLUETOOTH),
                eq(bluetoothDevice2.getAddress()));
    }

    @Test
    public void testAvailableEndpointChanged() throws Exception {
        mCallEndpointController.onCallAudioStateChanged(audioState1, audioState6);