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

Commit 1b25e1db authored by Pranav Madapurmath's avatar Pranav Madapurmath Committed by Android (Google) Code Review
Browse files

Merge "Resolve audio not routing to watch issue" into main

parents 67ba32d1 95d0739c
Loading
Loading
Loading
Loading
+25 −14
Original line number Diff line number Diff line
@@ -800,7 +800,6 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        if (bluetoothRoute != null) {
            Log.i(this, "request to route to bluetooth route: %s (active=%b)", bluetoothRoute,
                    mIsActive);
            updateActiveBluetoothDevice(new Pair<>(type, deviceAddress));
            routeTo(mIsActive, bluetoothRoute);
        } else {
            Log.i(this, "request to route to unavailable bluetooth route - type (%s), address (%s)",
@@ -844,10 +843,6 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
            // Fallback to an available route excluding the previously active device.
            routeTo(mIsActive, getBaseRoute(true, previouslyActiveDeviceAddress));
        }
        // Clear out the active device for the BT audio type.
        if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
            updateActiveBluetoothDevice(new Pair(type, null));
        }
    }

    private void handleMuteChanged(boolean mute) {
@@ -1023,16 +1018,29 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        // If SCO is once again connected or there's a pending message for BT_AUDIO_CONNECTED, then
        // we know that the device has reconnected or is in the middle of connecting. Ignore routing
        // out of this BT device.
        if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue() && areExcludedBtAndDestBtSame
        boolean isExcludedDeviceConnectingOrConnected = areExcludedBtAndDestBtSame
                && (mIsScoAudioConnected || mPendingAudioRoute.getPendingMessages()
                .contains(btDevicePendingMsg))) {
                .contains(btDevicePendingMsg));
        // Check if the pending audio route or current route is already different from the route
        // including the BT device that should be excluded from route selection.
        boolean isCurrentOrDestRouteDifferent = btAddressToExclude != null
                && ((mIsPending && !btAddressToExclude.equals(mPendingAudioRoute.getDestRoute()
                .getBluetoothAddress())) || (!mIsPending && !btAddressToExclude.equals(
                        mCurrentRoute.getBluetoothAddress())));
        if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
            if (isExcludedDeviceConnectingOrConnected) {
                Log.i(this, "BT device with address (%s) is currently connecting/connected. "
                    + "Ignore route switch.");
        } else {
                        + "Ignoring route switch.", btAddressToExclude);
                return;
            } else if (isCurrentOrDestRouteDifferent) {
                Log.i(this, "Current or pending audio route isn't routed to device with address "
                        + "(%s). Ignoring route switch.", btAddressToExclude);
                return;
            }
        }
        routeTo(mIsActive, calculateBaselineRoute(isExplicitUserRequest, includeBluetooth,
                btAddressToExclude));
    }
    }

    private void handleSpeakerOn() {
        if (isPending()) {
@@ -1441,8 +1449,11 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
                continue;
            }
            // Check if the most recently active device is a watch device.
            if (i == (bluetoothRoutes.size() - 1) && device.equals(mCallAudioState
                    .getActiveBluetoothDevice()) && mBluetoothRouteManager.isWatch(device)) {
            boolean isActiveDevice = mActiveBluetoothDevice != null
                    && device.getAddress().equals(mActiveBluetoothDevice.second);
            if (i == (bluetoothRoutes.size() - 1) && mBluetoothRouteManager.isWatch(device)
                    && (device.equals(mCallAudioState.getActiveBluetoothDevice())
                    || isActiveDevice)) {
                Log.i(this, "getActiveWatchOrNonWatchDeviceRoute: Routing to active watch - %s",
                        bluetoothRoutes.get(0));
                return bluetoothRoutes.get(0);
+5 −8
Original line number Diff line number Diff line
@@ -252,17 +252,14 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
            CallAudioRouteController audioRouteController = (CallAudioRouteController)
                    mCallAudioRouteAdapter;
            if (device == null) {
                if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
                    audioRouteController.updateActiveBluetoothDevice(
                            new Pair(audioRouteType, null));
                }
                // Update the active device cache immediately.
                audioRouteController.updateActiveBluetoothDevice(new Pair(audioRouteType, null));
                mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_GONE,
                        audioRouteType);
            } else {
                if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
                // Update the active device cache immediately.
                audioRouteController.updateActiveBluetoothDevice(
                        new Pair(audioRouteType, device.getAddress()));
                }
                mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
                        audioRouteType, device.getAddress());
                if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID
+58 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import android.media.audiopolicy.AudioProductStrategy;
import android.os.UserHandle;
import android.telecom.CallAudioState;
import android.telecom.VideoProfile;
import android.util.Pair;

import androidx.test.filters.SmallTest;

@@ -1099,6 +1100,63 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
                any(CallAudioState.class), eq(expectedState));
    }

    @Test
    @SmallTest
    public void testRouteToWatchWhenCallAnsweredOnWatch_MultipleBtDevices() {
        when(mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()).thenReturn(true);
        // Connect first BT device.
        verifyConnectBluetoothDevice(AudioRoute.TYPE_BLUETOOTH_SCO);
        // Connect another BT device.
        String scoDeviceAddress = "00:00:00:00:00:03";
        BluetoothDevice watchDevice =
                BluetoothRouteManagerTest.makeBluetoothDevice(scoDeviceAddress);
        when(mBluetoothRouteManager.isWatch(eq(watchDevice))).thenReturn(true);
        BLUETOOTH_DEVICES.add(watchDevice);

        mController.sendMessageWithSessionInfo(BT_DEVICE_ADDED, AudioRoute.TYPE_BLUETOOTH_SCO,
                watchDevice);
        CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
                | CallAudioState.ROUTE_BLUETOOTH, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
        verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
                any(CallAudioState.class), eq(expectedState));

        // Signal that watch is now the active device. This is done in BluetoothStateReceiver and
        // then BT_ACTIVE_DEVICE_PRESENT will be sent to the controller to be processed.
        mController.updateActiveBluetoothDevice(
                new Pair<>(AudioRoute.TYPE_BLUETOOTH_SCO, watchDevice.getAddress()));
        // Emulate scenario with call answered on watch. Ensure at this point that audio was routed
        // into watch
        mController.sendMessageWithSessionInfo(SWITCH_FOCUS, ACTIVE_FOCUS, 0);
        mController.sendMessageWithSessionInfo(BT_AUDIO_CONNECTED,
                0, watchDevice);
        mController.sendMessageWithSessionInfo(BT_AUDIO_DISCONNECTED,
                0, BLUETOOTH_DEVICE_1);
        expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
                        | CallAudioState.ROUTE_BLUETOOTH, watchDevice, BLUETOOTH_DEVICES);
        verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
                any(CallAudioState.class), eq(expectedState));

        // Hardcode signal from BT stack signaling to Telecom that watch is now the active device.
        // This should just be a no-op since audio was already routed when processing active focus.
        mController.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
                AudioRoute.TYPE_BLUETOOTH_SCO, scoDeviceAddress);
        verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
                any(CallAudioState.class), eq(expectedState));

        // Mimic behavior of controller processing BT_AUDIO_DISCONNECTED for BLUETOOTH_DEVICE_1 and
        // verify that audio remains routed to the watch and not routed to earpiece (this should
        // be taking into account what the BT active device is as reported to us by the BT stack).
        mController.sendMessageWithSessionInfo(SWITCH_BASELINE_ROUTE,
                INCLUDE_BLUETOOTH_IN_BASELINE, BLUETOOTH_DEVICE_1.getAddress());
        verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
                any(CallAudioState.class), eq(expectedState));

        BLUETOOTH_DEVICES.remove(watchDevice);
    }


    @Test
    @SmallTest
    public void testAbandonCallAudioFocusAfterCallEnd() {