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

Commit 0ba3a574 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Revert audio focus logic in Telecom.

As part of the effort to resolve hidden dependencies, we switched to use
AudioManager#requestAudioFocus instead of the hidden API.  It turns out
the old hidden API did something special; it "locked" focus onto Telecom.
As a consequence another app could steal call focus from Telecom.

To fix this for now we'll use the old hidden APIs; there is a way to do
this was using AudioFocusRequest.Builder.setLocksFocus(true), however
that appears not to work due to a permissions error. Until this can be
resolved, we'll switch back to the old hidden API until we can figure out
the right workaround.

Added new CUJ tests to validate this behavior and ensure no regression
in the future.

Test: Added new CUJ tests to cover this case.
Bug: 374309755
Flag: com.android.server.telecom.flags.telecom_resolve_hidden_dependenciesNONE bugfix.
Change-Id: Ife53e94aa4b41432e736545d183487c5a715a3aa
parent 7c181fcf
Loading
Loading
Loading
Loading
+17 −71
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.telecom;

import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Looper;
import android.os.Message;
@@ -47,22 +46,6 @@ public class CallAudioModeStateMachine extends StateMachine {
        }
    }

    private static final AudioAttributes RING_AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
            .setLegacyStreamType(AudioManager.STREAM_RING)
            .build();
    public static final AudioFocusRequest RING_AUDIO_FOCUS_REQUEST = new AudioFocusRequest
            .Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
            .setAudioAttributes(RING_AUDIO_ATTRIBUTES).build();

    private static final AudioAttributes CALL_AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
            .setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
            .build();
    public static final AudioFocusRequest CALL_AUDIO_FOCUS_REQUEST = new AudioFocusRequest
            .Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
            .setAudioAttributes(CALL_AUDIO_ATTRIBUTES).build();

    public static class MessageArgs {
        public boolean hasActiveOrDialingCalls;
        public boolean hasRingingCalls;
@@ -232,8 +215,6 @@ public class CallAudioModeStateMachine extends StateMachine {
    public static final String STREAMING_STATE_NAME = StreamingFocusState.class.getSimpleName();
    public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName();

    private AudioFocusRequest mCurrentAudioFocusRequest = null;

    private class BaseState extends State {
        @Override
        public boolean processMessage(Message msg) {
@@ -348,18 +329,9 @@ public class CallAudioModeStateMachine extends StateMachine {
                            + args.toString());
                    return HANDLED;
                case AUDIO_OPERATIONS_COMPLETE:
                    if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                        if (mCurrentAudioFocusRequest != null) {
                    Log.i(this, "AudioOperationsComplete: "
                            + "AudioManager#abandonAudioFocusRequest(); now unfocused");
                            mAudioManager.abandonAudioFocusRequest(mCurrentAudioFocusRequest);
                            mCurrentAudioFocusRequest = null;
                        } else {
                            Log.i(this, "AudioOperationsComplete: already unfocused");
                        }
                    } else {
                    mAudioManager.abandonAudioFocusForCall();
                    }
                    // Clear requested communication device after the call ends.
                    if (mFeatureFlags.clearCommunicationDeviceAfterAudioOpsComplete()) {
                        mCommunicationDeviceTracker.clearCommunicationDevice(
@@ -438,14 +410,7 @@ public class CallAudioModeStateMachine extends StateMachine {
                case AUDIO_OPERATIONS_COMPLETE:
                    Log.i(LOG_TAG, "AudioManager#abandonAudioFocusRequest: now "
                            + "AUDIO_PROCESSING");
                    if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                        if (mCurrentAudioFocusRequest != null) {
                            mAudioManager.abandonAudioFocusRequest(mCurrentAudioFocusRequest);
                            mCurrentAudioFocusRequest = null;
                        }
                    } else {
                    mAudioManager.abandonAudioFocusForCall();
                    }
                    return HANDLED;
                default:
                    // The forced focus switch commands are handled by BaseState.
@@ -468,14 +433,10 @@ public class CallAudioModeStateMachine extends StateMachine {
            }

            if (mCallAudioManager.startRinging()) {
                if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                    mCurrentAudioFocusRequest = RING_AUDIO_FOCUS_REQUEST;
                Log.i(this, "tryStartRinging: AudioManager#requestAudioFocus(RING)");
                    mAudioManager.requestAudioFocus(RING_AUDIO_FOCUS_REQUEST);
                } else {
                mAudioManager.requestAudioFocusForCall(
                        AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                }

                // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode --
                // this trips up the audio system.
                if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) {
@@ -570,14 +531,9 @@ public class CallAudioModeStateMachine extends StateMachine {
        public void enter() {
            Log.i(LOG_TAG, "Audio focus entering SIM CALL state");
            mLocalLog.log("Enter SIM_CALL");
            if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                mCurrentAudioFocusRequest = CALL_AUDIO_FOCUS_REQUEST;
            Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)");
                mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST);
            } else {
            mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            }
            Log.i(this, "enter: AudioManager#setMode(MODE_IN_CALL)");
            mAudioManager.setMode(AudioManager.MODE_IN_CALL);
            mLocalLog.log("Mode MODE_IN_CALL");
@@ -660,14 +616,9 @@ public class CallAudioModeStateMachine extends StateMachine {
        public void enter() {
            Log.i(LOG_TAG, "Audio focus entering VOIP CALL state");
            mLocalLog.log("Enter VOIP_CALL");
            if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                mCurrentAudioFocusRequest = CALL_AUDIO_FOCUS_REQUEST;
            Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)");
                mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST);
            } else {
            mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            }
            Log.i(this, "enter: AudioManager#setMode(MODE_IN_COMMUNICATION)");
            mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
            mLocalLog.log("Mode MODE_IN_COMMUNICATION");
@@ -823,14 +774,9 @@ public class CallAudioModeStateMachine extends StateMachine {
        public void enter() {
            Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state");
            mLocalLog.log("Enter TONE/HOLDING");
            if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                mCurrentAudioFocusRequest = CALL_AUDIO_FOCUS_REQUEST;
            Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)");
                mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST);
            } else {
            mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            }
            Log.i(this, "enter: AudioManager#setMode(%d)", mMostRecentMode);
            mAudioManager.setMode(mMostRecentMode);
            mLocalLog.log("Mode " + mMostRecentMode);
+0 −33
Original line number Diff line number Diff line
@@ -16,15 +16,10 @@

package com.android.server.telecom.tests;

import static com.android.server.telecom.CallAudioModeStateMachine.CALL_AUDIO_FOCUS_REQUEST;
import static com.android.server.telecom.CallAudioModeStateMachine.RING_AUDIO_FOCUS_REQUEST;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -49,7 +44,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;

@RunWith(JUnit4.class)
@@ -329,33 +323,6 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
        verify(mCallAudioManager, times(2)).startRinging();
    }

    @SmallTest
    @Test
    public void testAudioFocusRequestWithResolveHiddenDependencies() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper(), mFeatureFlags, mCommunicationDeviceTracker);
        when(mFeatureFlags.telecomResolveHiddenDependencies()).thenReturn(true);
        ArgumentCaptor<AudioFocusRequest> captor = ArgumentCaptor.forClass(AudioFocusRequest.class);
        sm.setCallAudioManager(mCallAudioManager);

        resetMocks();
        when(mCallAudioManager.startRinging()).thenReturn(true);
        when(mCallAudioManager.isRingtonePlaying()).thenReturn(false);

        sm.sendMessage(CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
        verify(mAudioManager).requestAudioFocus(captor.capture());
        assertTrue(areAudioFocusRequestsMatch(captor.getValue(), RING_AUDIO_FOCUS_REQUEST));

        sm.sendMessage(CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
        verify(mAudioManager, atLeast(1)).requestAudioFocus(captor.capture());
        AudioFocusRequest request = captor.getValue();
        assertTrue(areAudioFocusRequestsMatch(request, CALL_AUDIO_FOCUS_REQUEST));

        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
    }

    private void resetMocks() {
        clearInvocations(mCallAudioManager, mAudioManager);
    }