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

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

Merge "Resolve update system audio route NPE and foreground updates" into main

parents dacebb47 68064225
Loading
Loading
Loading
Loading
+86 −19
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    private final Handler mHandler;
    private final WiredHeadsetManager mWiredHeadsetManager;
    private Set<AudioRoute> mAvailableRoutes;
    private Set<AudioRoute> mCallSupportedRoutes;
    private AudioRoute mCurrentRoute;
    private AudioRoute mEarpieceWiredRoute;
    private AudioRoute mSpeakerDockRoute;
@@ -104,6 +105,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    private StatusBarNotifier mStatusBarNotifier;
    private FeatureFlags mFeatureFlags;
    private int mFocusType;
    private int mCallSupportedRouteMask = -1;
    private boolean mIsScoAudioConnected;
    private final Object mLock = new Object();
    private final TelecomSystem.SyncRoot mTelecomLock;
@@ -314,6 +316,9 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
                            handleExitPendingRoute();
                            break;
                        case UPDATE_SYSTEM_AUDIO_ROUTE:
                            // Based on the available routes for foreground call, adjust routing.
                            updateRouteForForeground();
                            // Force update to notify all ICS/CS.
                            updateCallAudioState(new CallAudioState(mIsMute,
                                    mCallAudioState.getRoute(),
                                    mCallAudioState.getSupportedRouteMask(),
@@ -330,6 +335,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    @Override
    public void initialize() {
        mAvailableRoutes = new HashSet<>();
        mCallSupportedRoutes = new HashSet<>();
        mBluetoothRoutes = new LinkedHashMap<>();
        mActiveDeviceCache = new HashMap<>();
        mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_SCO, null);
@@ -485,7 +491,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    }

    private void routeTo(boolean active, AudioRoute destRoute) {
        if (!destRoute.equals(mStreamingRoute) && !getAvailableRoutes().contains(destRoute)) {
        if (destRoute == null || (!destRoute.equals(mStreamingRoute)
                && !getCallSupportedRoutes().contains(destRoute))) {
            Log.i(this, "Ignore routing to unavailable route: %s", destRoute);
            return;
        }
@@ -510,7 +517,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
            Log.i(this, "Enter pending route, orig%s(active=%b), dest%s(active=%b)", mCurrentRoute,
                    mIsActive, destRoute, active);
            // route to pending route
            if (getAvailableRoutes().contains(mCurrentRoute)) {
            if (getCallSupportedRoutes().contains(mCurrentRoute)) {
                mPendingAudioRoute.setOrigRoute(mIsActive, mCurrentRoute);
            } else {
                // Avoid waiting for pending messages for an unavailable route
@@ -841,7 +848,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {

    public void handleSwitchEarpiece() {
        AudioRoute earpieceRoute = mTypeRoutes.get(AudioRoute.TYPE_EARPIECE);
        if (earpieceRoute != null && getAvailableRoutes().contains(earpieceRoute)) {
        if (earpieceRoute != null && getCallSupportedRoutes().contains(earpieceRoute)) {
            routeTo(mIsActive, earpieceRoute);
        } else {
            Log.i(this, "ignore switch earpiece request");
@@ -856,7 +863,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
            bluetoothRoute = getArbitraryBluetoothDevice();
            bluetoothDevice = mBluetoothRoutes.get(bluetoothRoute);
        } else {
            for (AudioRoute route : getAvailableRoutes()) {
            for (AudioRoute route : getCallSupportedRoutes()) {
                if (Objects.equals(address, route.getBluetoothAddress())) {
                    bluetoothRoute = route;
                    bluetoothDevice = mBluetoothRoutes.get(route);
@@ -894,7 +901,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {

    private void handleSwitchHeadset() {
        AudioRoute headsetRoute = mTypeRoutes.get(AudioRoute.TYPE_WIRED);
        if (headsetRoute != null && getAvailableRoutes().contains(headsetRoute)) {
        if (headsetRoute != null && getCallSupportedRoutes().contains(headsetRoute)) {
            routeTo(mIsActive, headsetRoute);
        } else {
            Log.i(this, "ignore switch headset request");
@@ -902,7 +909,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
    }

    private void handleSwitchSpeaker() {
        if (mSpeakerDockRoute != null && getAvailableRoutes().contains(mSpeakerDockRoute)) {
        if (mSpeakerDockRoute != null && getCallSupportedRoutes().contains(mSpeakerDockRoute)) {
            routeTo(mIsActive, mSpeakerDockRoute);
        } else {
            Log.i(this, "ignore switch speaker request");
@@ -920,7 +927,8 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
            // Update status bar notification if we are in a call.
            mStatusBarNotifier.notifySpeakerphone(mCallsManager.hasAnyCalls());
        } else {
            if (mSpeakerDockRoute != null && getAvailableRoutes().contains(mSpeakerDockRoute)) {
            if (mSpeakerDockRoute != null && getCallSupportedRoutes()
                    .contains(mSpeakerDockRoute)) {
                routeTo(mIsActive, mSpeakerDockRoute);
                // Since the route switching triggered by this message, we need to manually send it
                // again so that we won't stuck in the pending route
@@ -984,7 +992,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        synchronized (mLock) {
            int routeMask = 0;
            Set<BluetoothDevice> availableBluetoothDevices = new HashSet<>();
            for (AudioRoute route : getAvailableRoutes()) {
            for (AudioRoute route : getCallSupportedRoutes()) {
                routeMask |= ROUTE_MAP.get(route.getType());
                if (BT_AUDIO_ROUTE_TYPES.contains(route.getType())) {
                    BluetoothDevice deviceToAdd = mBluetoothRoutes.get(route);
@@ -1004,6 +1012,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
                    }
                }
            }

            updateCallAudioState(new CallAudioState(mIsMute, mCallAudioState.getRoute(), routeMask,
                    mCallAudioState.getActiveBluetoothDevice(), availableBluetoothDevices));
        }
@@ -1015,7 +1024,50 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
                mCallAudioState.getSupportedBluetoothDevices()));
    }

    /**
     * Retrieves the current call's supported audio route and adjusts the audio routing if the
     * current route isn't supported.
     */
    private void updateRouteForForeground() {
        boolean updatedRouteForCall = updateCallSupportedAudioRoutes();
        // Ensure that current call audio state has updated routes for current call.
        if (updatedRouteForCall) {
            mCallAudioState = new CallAudioState(mIsMute, mCallAudioState.getRoute(),
                    mCallSupportedRouteMask, mCallAudioState.getActiveBluetoothDevice(),
                    mCallAudioState.getSupportedBluetoothDevices());
            // Update audio route if foreground call doesn't support the current route.
            if ((mCallSupportedRouteMask & mCallAudioState.getRoute()) == 0) {
                routeTo(mIsActive, getBaseRoute(true, null));
            }
        }
    }

    /**
     * Update supported audio routes for the foreground call if present.
     */
    private boolean updateCallSupportedAudioRoutes() {
        int availableRouteMask = 0;
        Call foregroundCall = mCallsManager.getForegroundCall();
        if (foregroundCall != null) {
            int foregroundCallSupportedRouteMask = foregroundCall.getSupportedAudioRoutes();
            for (AudioRoute route : getAvailableRoutes()) {
                int routeType = ROUTE_MAP.get(route.getType());
                availableRouteMask |= routeType;
                if ((routeType & foregroundCallSupportedRouteMask) == routeType) {
                    mCallSupportedRoutes.add(route);
                }
            }
            mCallSupportedRouteMask = availableRouteMask & foregroundCallSupportedRouteMask;
            return true;
        } else {
            mCallSupportedRoutes.clear();
            mCallSupportedRouteMask = -1;
            return false;
        }
    }

    private void updateCallAudioState(CallAudioState newCallAudioState) {
        synchronized (mTelecomLock) {
            Log.i(this, "updateCallAudioState: updating call audio state to %s", newCallAudioState);
            CallAudioState oldState = mCallAudioState;
            mCallAudioState = newCallAudioState;
@@ -1024,6 +1076,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
            mCallsManager.onCallAudioStateChanged(oldState, mCallAudioState);
            updateAudioStateForTrackedCalls(mCallAudioState);
        }
    }

    private void updateAudioStateForTrackedCalls(CallAudioState newCallAudioState) {
        Set<Call> calls = mCallsManager.getTrackedCalls();
@@ -1080,11 +1133,17 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        // are only wearables available.
        AudioRoute activeWatchOrNonWatchDeviceRoute =
                getActiveWatchOrNonWatchDeviceRoute(btAddressToExclude);
        if (mBluetoothRoutes.isEmpty() || !includeBluetooth
                || activeWatchOrNonWatchDeviceRoute == null) {
        if ((!mCallSupportedRoutes.isEmpty() && (mCallSupportedRouteMask
                & CallAudioState.ROUTE_BLUETOOTH) == 0) || mBluetoothRoutes.isEmpty()
                || !includeBluetooth || activeWatchOrNonWatchDeviceRoute == null) {
            Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to "
                    + "available non-BT route.");
            AudioRoute defaultRoute = mEarpieceWiredRoute != null
            boolean callSupportsEarpieceWiredRoute = mCallSupportedRoutes.isEmpty()
                    || mCallSupportedRoutes.contains(mEarpieceWiredRoute);
            // If call supported route doesn't contain earpiece/wired/BT, it should have speaker
            // enabled. Otherwise, no routes would be supported for the call which should never be
            // the case.
            AudioRoute defaultRoute = mEarpieceWiredRoute != null && callSupportsEarpieceWiredRoute
                    ? mEarpieceWiredRoute
                    : mSpeakerDockRoute;
            // Ensure that we default to speaker route if we're in a video call, but disregard it if
@@ -1136,6 +1195,14 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        }
    }

    public Set<AudioRoute> getCallSupportedRoutes() {
        if (mCurrentRoute.equals(mStreamingRoute)) {
            return mStreamingRoutes;
        } else {
            return mCallSupportedRoutes.isEmpty() ? mAvailableRoutes : mCallSupportedRoutes;
        }
    }

    public AudioRoute getCurrentRoute() {
        return mCurrentRoute;
    }
@@ -1155,7 +1222,7 @@ public class CallAudioRouteController implements CallAudioRouteAdapter {
        if (destRoute == null || (destRoute.getBluetoothAddress() != null && !includeBluetooth)) {
            destRoute = getPreferredAudioRouteFromDefault(includeBluetooth, btAddressToExclude);
        }
        if (destRoute != null && !getAvailableRoutes().contains(destRoute)) {
        if (destRoute != null && !getCallSupportedRoutes().contains(destRoute)) {
            destRoute = null;
        }
        Log.i(this, "getBaseRoute - audio routing to %s", destRoute);
+32 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import com.android.server.telecom.AudioRoute;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallAudioRouteController;
import com.android.server.telecom.CallAudioRouteStateMachine;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.PendingAudioRoute;
import com.android.server.telecom.StatusBarNotifier;
@@ -152,6 +153,7 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        when(mCallsManager.getCurrentUserHandle()).thenReturn(
                new UserHandle(UserHandle.USER_SYSTEM));
        when(mCallsManager.getLock()).thenReturn(mLock);
        when(mCallsManager.getForegroundCall()).thenReturn(mCall);
        when(mBluetoothRouteManager.getDeviceManager()).thenReturn(mBluetoothDeviceManager);
        when(mBluetoothDeviceManager.connectAudio(any(BluetoothDevice.class), anyInt()))
                .thenReturn(true);
@@ -172,6 +174,7 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
        mController.setCallAudioManager(mCallAudioManager);
        when(mCallAudioManager.getForegroundCall()).thenReturn(mCall);
        when(mCall.getVideoState()).thenReturn(VideoProfile.STATE_AUDIO_ONLY);
        when(mCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL);
        when(mFeatureFlags.ignoreAutoRouteToWatchDevice()).thenReturn(false);
        when(mFeatureFlags.useRefactoredAudioRouteSwitching()).thenReturn(true);
    }
@@ -799,6 +802,35 @@ public class CallAudioRouteControllerTest extends TelecomTestCase {
            any(CallAudioState.class), eq(expectedState));
    }

    @SmallTest
    @Test
    public void testUpdateRouteForForeground() {
        mController.initialize();
        mController.sendMessageWithSessionInfo(BT_DEVICE_ADDED, AudioRoute.TYPE_BLUETOOTH_SCO,
                BLUETOOTH_DEVICE_1);

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

        // Ensure that supported routes is updated along with the current route to reflect the
        // foreground call's supported audio routes.
        when(mCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_SPEAKER);
        mController.sendMessageWithSessionInfo(
                CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
        mController.sendMessageWithSessionInfo(SPEAKER_ON);
        expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
                CallAudioState.ROUTE_SPEAKER, null, BLUETOOTH_DEVICES);
        verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
                any(CallAudioState.class), eq(expectedState));
        assertEquals(3, mController.getAvailableRoutes().size());
        assertEquals(1, mController.getCallSupportedRoutes().size());
    }

    private void verifyConnectBluetoothDevice(int audioType) {
        mController.initialize();
        mController.setActive(true);