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

Commit 3a6e242b authored by Hall Liu's avatar Hall Liu
Browse files

Update route when speakerphone changed externally

When some other app changes the speakerphone state during a call and we
get a broadcast about it, update our internal audio route.

Test: unit, manual
Fixes: 34686469
Change-Id: I7b452631267cefc68ce37f121f71ee4a592551b4
parent 29cbb62d
Loading
Loading
Loading
Loading
+63 −2
Original line number Diff line number Diff line
@@ -123,6 +123,11 @@ public class CallAudioRouteStateMachine extends StateMachine {
    // Wired headset, earpiece, or speakerphone, in that order of precedence.
    public static final int SWITCH_BASELINE_ROUTE = 1005;

    // Messages denoting that the speakerphone was turned on/off. Used to update state when we
    // weren't the ones who turned it on/off
    public static final int SPEAKER_ON = 1006;
    public static final int SPEAKER_OFF = 1007;

    public static final int USER_SWITCH_EARPIECE = 1101;
    public static final int USER_SWITCH_BLUETOOTH = 1102;
    public static final int USER_SWITCH_HEADSET = 1103;
@@ -181,6 +186,8 @@ public class CallAudioRouteStateMachine extends StateMachine {
        put(SWITCH_HEADSET, "SWITCH_HEADSET");
        put(SWITCH_SPEAKER, "SWITCH_SPEAKER");
        put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE");
        put(SPEAKER_ON, "SPEAKER_ON");
        put(SPEAKER_OFF, "SPEAKER_OFF");

        put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE");
        put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH");
@@ -374,6 +381,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
            switch (msg.what) {
                case SWITCH_EARPIECE:
                case USER_SWITCH_EARPIECE:
                case SPEAKER_OFF:
                    // Nothing to do here
                    return HANDLED;
                case BT_AUDIO_CONNECTED:
@@ -405,6 +413,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                case SPEAKER_ON:
                    transitionTo(mActiveSpeakerRoute);
                    return HANDLED;
                case SWITCH_FOCUS:
@@ -449,6 +458,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
            switch (msg.what) {
                case SWITCH_EARPIECE:
                case USER_SWITCH_EARPIECE:
                case SPEAKER_OFF:
                    // Nothing to do here
                    return HANDLED;
                case BT_AUDIO_CONNECTED:
@@ -473,6 +483,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                case SPEAKER_ON:
                    transitionTo(mQuiescentSpeakerRoute);
                    return HANDLED;
                case SWITCH_FOCUS:
@@ -594,10 +605,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_HEADSET:
                case USER_SWITCH_HEADSET:
                case SPEAKER_OFF:
                    // Nothing to do
                    return HANDLED;
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                case SPEAKER_ON:
                    transitionTo(mActiveSpeakerRoute);
                    return HANDLED;
                case SWITCH_FOCUS:
@@ -662,10 +675,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_HEADSET:
                case USER_SWITCH_HEADSET:
                case SPEAKER_OFF:
                    // Nothing to do
                    return HANDLED;
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                case SPEAKER_ON:
                    transitionTo(mQuiescentSpeakerRoute);
                    return HANDLED;
                case SWITCH_FOCUS:
@@ -835,9 +850,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    mHasUserExplicitlyLeftBluetooth = true;
                    // fall through
                case SWITCH_SPEAKER:
                case SPEAKER_ON:
                    setBluetoothOff();
                    transitionTo(mActiveSpeakerRoute);
                    return HANDLED;
                case SPEAKER_OFF:
                    return HANDLED;
                case SWITCH_FOCUS:
                    if (msg.arg1 == NO_FOCUS) {
                        // Only disconnect SCO audio here instead of routing away from BT entirely.
@@ -926,8 +944,11 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    mHasUserExplicitlyLeftBluetooth = true;
                    // fall through
                case SWITCH_SPEAKER:
                case SPEAKER_ON:
                    transitionTo(mActiveSpeakerRoute);
                    return HANDLED;
                case SPEAKER_OFF:
                    return HANDLED;
                case SWITCH_FOCUS:
                    if (msg.arg1 == NO_FOCUS) {
                        reinitialize();
@@ -987,6 +1008,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_BLUETOOTH:
                case USER_SWITCH_BLUETOOTH:
                case SPEAKER_OFF:
                    // Nothing to do
                    return HANDLED;
                case SWITCH_HEADSET:
@@ -999,6 +1021,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                case SPEAKER_ON:
                    transitionTo(mQuiescentSpeakerRoute);
                    return HANDLED;
                case SWITCH_FOCUS:
@@ -1143,6 +1166,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
                case USER_SWITCH_SPEAKER:
                    // Nothing to do
                    return HANDLED;
                case SPEAKER_ON:
                    // Expected, since we just transitioned here
                    return HANDLED;
                case SPEAKER_OFF:
                    sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
                    return HANDLED;
                case SWITCH_FOCUS:
                    if (msg.arg1 == NO_FOCUS) {
                        reinitialize();
@@ -1215,8 +1244,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                case SPEAKER_ON:
                    // Nothing to do
                    return HANDLED;
                case SPEAKER_OFF:
                    sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
                    return HANDLED;
                case SWITCH_FOCUS:
                    if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
                        transitionTo(mActiveSpeakerRoute);
@@ -1294,6 +1327,28 @@ public class CallAudioRouteStateMachine extends StateMachine {
        }
    };

    private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.startSession("CARSM.mSPCR");
            try {
                if (AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED.equals(intent.getAction())) {
                    if (mAudioManager != null) {
                        if (mAudioManager.isSpeakerphoneOn()) {
                            sendInternalMessage(SPEAKER_ON);
                        } else {
                            sendInternalMessage(SPEAKER_OFF);
                        }
                    }
                } else {
                    Log.w(this, "Received non-speakerphone-change intent");
                }
            } finally {
                Log.endSession();
            }
        }
    };

    private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute();
    private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute();
    private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute();
@@ -1446,6 +1501,8 @@ public class CallAudioRouteStateMachine extends StateMachine {
        mWasOnSpeaker = false;
        mContext.registerReceiver(mMuteChangeReceiver,
                new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED));
        mContext.registerReceiver(mSpeakerPhoneChangeReceiver,
                new IntentFilter(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED));

        mStatusBarNotifier.notifyMute(initState.isMuted());
        mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
@@ -1531,8 +1588,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
    }

    private void setSpeakerphoneOn(boolean on) {
        if (mAudioManager.isSpeakerphoneOn() != on) {
            Log.i(this, "turning speaker phone %s", on);
            mAudioManager.setSpeakerphoneOn(on);
        } else {
            Log.i(this, "Ignoring speakerphone request -- already %s", on);
        }
        mStatusBarNotifier.notifySpeakerphone(on);
    }

+1 −1
Original line number Diff line number Diff line
@@ -611,7 +611,7 @@ public class BasicCallTests extends TelecomSystemTest {
        waitForHandlerAction(mTelecomSystem.getCallsManager().getCallAudioManager()
                .getCallAudioRouteStateMachine().getHandler(), TEST_TIMEOUT);
        // setSpeakerPhoneOn(false) gets called once during the call initiation phase
        verify(audioManager, timeout(TEST_TIMEOUT).atLeast(2))
        verify(audioManager, timeout(TEST_TIMEOUT).atLeast(1))
                .setSpeakerphoneOn(false);

        mConnectionServiceFixtureA.
+64 −2
Original line number Diff line number Diff line
@@ -219,8 +219,18 @@ public class CallAudioRouteTransitionTests extends TelecomTestCase {
            return null;
        }).when(mockBluetoothRouteManager).connectBluetoothAudio(nullable(String.class));

        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
                params.initialRoute == CallAudioState.ROUTE_SPEAKER);
        // Set the speakerphone state depending on the message being sent. If it's one of the
        // speakerphone override ones, set accordingly. Otherwise consult the initial route.
        boolean speakerphoneOn;
        if (params.action == CallAudioRouteStateMachine.SPEAKER_ON) {
            speakerphoneOn = true;
        } else if (params.action == CallAudioRouteStateMachine.SPEAKER_OFF) {
            speakerphoneOn = false;
        } else {
            speakerphoneOn = params.initialRoute == CallAudioState.ROUTE_SPEAKER;
        }
        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(speakerphoneOn);

        when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
    }

@@ -757,6 +767,58 @@ public class CallAudioRouteTransitionTests extends TelecomTestCase {
                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
        ));

        params.add(new RoutingTestParameters(
                "Speakerphone turned on during earpiece", // name
                CallAudioState.ROUTE_EARPIECE, // initialRoute
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
                NONE, // speakerInteraction
                NONE, // bluetoothInteraction
                CallAudioRouteStateMachine.SPEAKER_ON, // action
                CallAudioState.ROUTE_SPEAKER, // expectedRoute
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
        ));

        params.add(new RoutingTestParameters(
                "Speakerphone turned on during wired headset", // name
                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
                CallAudioState.ROUTE_EARPIECE
                        | CallAudioState.ROUTE_BLUETOOTH
                        | CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
                NONE, // speakerInteraction
                NONE, // bluetoothInteraction
                CallAudioRouteStateMachine.SPEAKER_ON, // action
                CallAudioState.ROUTE_SPEAKER, // expectedRoute
                CallAudioState.ROUTE_EARPIECE
                        | CallAudioState.ROUTE_BLUETOOTH
                        | CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
        ));

        params.add(new RoutingTestParameters(
                "Speakerphone turned on during bluetooth", // name
                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
                NONE, // speakerInteraction
                OFF, // bluetoothInteraction
                CallAudioRouteStateMachine.SPEAKER_ON, // action
                CallAudioState.ROUTE_SPEAKER, // expectedRoute
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
        ));

        params.add(new RoutingTestParameters(
                "Speakerphone turned off externally during speaker", // name
                CallAudioState.ROUTE_SPEAKER, // initialRoute
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
                NONE, // speakerInteraction
                ON, // bluetoothInteraction
                CallAudioRouteStateMachine.SPEAKER_OFF, // action
                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
        ));

        return params;
    }