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

Commit 0e1d8d3a authored by Grace Jia's avatar Grace Jia Committed by Android (Google) Code Review
Browse files

Merge "Use AudioFocusRequest to request/abandon audio focus." into main

parents fe523c40 7362c76f
Loading
Loading
Loading
Loading
+72 −14
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

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;
@@ -29,6 +31,7 @@ import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.telecom.flags.FeatureFlags;

public class CallAudioModeStateMachine extends StateMachine {
    /**
@@ -38,11 +41,27 @@ public class CallAudioModeStateMachine extends StateMachine {
    private LocalLog mLocalLog = new LocalLog(20);
    public static class Factory {
        public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper,
                AudioManager am) {
            return new CallAudioModeStateMachine(systemStateHelper, am);
                                                AudioManager am, FeatureFlags featureFlags) {
            return new CallAudioModeStateMachine(systemStateHelper, am, featureFlags);
        }
    }

    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;
@@ -212,6 +231,8 @@ 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) {
@@ -310,7 +331,14 @@ public class CallAudioModeStateMachine extends StateMachine {
                    return HANDLED;
                case AUDIO_OPERATIONS_COMPLETE:
                    Log.i(LOG_TAG, "Abandoning audio focus: now UNFOCUSED");
                    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.
@@ -381,7 +409,14 @@ public class CallAudioModeStateMachine extends StateMachine {
                    return HANDLED;
                case AUDIO_OPERATIONS_COMPLETE:
                    Log.i(LOG_TAG, "Abandoning audio focus: 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.
@@ -406,8 +441,13 @@ public class CallAudioModeStateMachine extends StateMachine {
                }

                if (mCallAudioManager.startRinging()) {
                    if (mFeatureFlags.telecomResolveHiddenDependencies()) {
                        mCurrentAudioFocusRequest = RING_AUDIO_FOCUS_REQUEST;
                        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) {
@@ -504,8 +544,13 @@ 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;
                mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST);
            } else {
                mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            }
            mAudioManager.setMode(AudioManager.MODE_IN_CALL);
            mLocalLog.log("Mode MODE_IN_CALL");
            mMostRecentMode = AudioManager.MODE_IN_CALL;
@@ -587,8 +632,13 @@ 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;
                mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST);
            } else {
                mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            }
            mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
            mLocalLog.log("Mode MODE_IN_COMMUNICATION");
            mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION;
@@ -742,8 +792,13 @@ 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;
                mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST);
            } else {
                mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            }
            mAudioManager.setMode(mMostRecentMode);
            mLocalLog.log("Mode " + mMostRecentMode);
            mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
@@ -815,16 +870,18 @@ public class CallAudioModeStateMachine extends StateMachine {
    private final AudioManager mAudioManager;
    private final SystemStateHelper mSystemStateHelper;
    private CallAudioManager mCallAudioManager;
    private FeatureFlags mFeatureFlags;

    private int mMostRecentMode;
    private boolean mIsInitialized = false;

    public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
            AudioManager audioManager) {
            AudioManager audioManager, FeatureFlags featureFlags) {
        super(CallAudioModeStateMachine.class.getSimpleName());
        mAudioManager = audioManager;
        mSystemStateHelper = systemStateHelper;
        mMostRecentMode = AudioManager.MODE_NORMAL;
        mFeatureFlags = featureFlags;

        createStates();
    }
@@ -833,11 +890,12 @@ public class CallAudioModeStateMachine extends StateMachine {
     * Used for testing
     */
    public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
            AudioManager audioManager, Looper looper) {
            AudioManager audioManager, Looper looper, FeatureFlags featureFlags) {
        super(CallAudioModeStateMachine.class.getSimpleName(), looper);
        mAudioManager = audioManager;
        mSystemStateHelper = systemStateHelper;
        mMostRecentMode = AudioManager.MODE_NORMAL;
        mFeatureFlags = featureFlags;

        createStates();
    }
+1 −1
Original line number Diff line number Diff line
@@ -655,7 +655,7 @@ public class CallsManager extends Call.ListenerBase
                mTimeoutsAdapter, mLock);
        mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                this, callAudioModeStateMachineFactory.create(systemStateHelper,
                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)),
                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), featureFlags),
                playerFactory, mRinger, new RingbackPlayer(playerFactory),
                bluetoothStateReceiver, mDtmfLocalTonePlayer);

+2 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.media.AudioDeviceInfo;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
@@ -110,6 +111,7 @@ public class BasicCallTests extends TelecomSystemTest {
        doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
        mPackageManager = mContext.getPackageManager();
        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
        when(mFeatureFlags.telecomResolveHiddenDependencies()).thenReturn(false);
    }

    @Override
+64 −18
Original line number Diff line number Diff line
@@ -16,14 +16,29 @@

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;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.HandlerThread;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallAudioModeStateMachine;
import com.android.server.telecom.CallAudioRouteStateMachine;
import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder;
import com.android.server.telecom.CallAudioRouteStateMachine;
import com.android.server.telecom.SystemStateHelper;

import org.junit.After;
@@ -31,18 +46,9 @@ 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;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(JUnit4.class)
public class CallAudioModeStateMachineTest extends TelecomTestCase {
    private static final int TEST_TIMEOUT = 1000;
@@ -62,6 +68,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
        super.setUp();
        when(mCallAudioManager.getCallAudioRouteStateMachine())
                .thenReturn(mCallAudioRouteStateMachine);
        when(mFeatureFlags.telecomResolveHiddenDependencies()).thenReturn(false);
    }

    @Override
@@ -76,7 +83,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testNoFocusWhenRingerSilenced() throws Throwable {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -108,7 +115,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testSwitchToStreamingMode() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -138,7 +145,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testExitStreamingMode() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ENTER_STREAMING_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -166,7 +173,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testNoRingWhenDeviceIsAtEar() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
        sm.sendMessage(CallAudioModeStateMachine.NEW_HOLDING_CALL, new Builder()
@@ -202,7 +209,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testRegainFocusWhenHfpIsConnectedSilenced() throws Throwable {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -246,7 +253,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testDoNotRingTwiceWhenHfpConnected() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -284,7 +291,7 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
    @Test
    public void testStartRingingAfterHfpConnectedIfNotAlreadyPlaying() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -318,7 +325,46 @@ public class CallAudioModeStateMachineTest extends TelecomTestCase {
        verify(mCallAudioManager, times(2)).startRinging();
    }

    @SmallTest
    @Test
    public void testAudioFocusRequestWithResolveHiddenDependencies() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        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);
    }

    private boolean areAudioFocusRequestsMatch(AudioFocusRequest r1, AudioFocusRequest r2) {
        if ((r1 == null) || (r2 == null)) {
            return false;
        }

        if (r1.getFocusGain() != r2.getFocusGain()) {
            return false;
        }

        return r1.getAudioAttributes().equals(r2.getAudioAttributes());
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -16,6 +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 android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.HandlerThread;
import android.test.suitebuilder.annotation.SmallTest;
@@ -37,6 +41,7 @@ import java.util.Collection;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
@@ -130,13 +135,14 @@ public class CallAudioModeTransitionTests extends TelecomTestCase {
    @SmallTest
    public void modeTransitionTest() {
        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
                mAudioManager, mTestThread.getLooper());
                mAudioManager, mTestThread.getLooper(), mFeatureFlags);
        sm.setCallAudioManager(mCallAudioManager);
        sm.sendMessage(mParams.initialAudioState);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);

        resetMocks();
        when(mCallAudioManager.startRinging()).thenReturn(true);
        when(mFeatureFlags.telecomResolveHiddenDependencies()).thenReturn(false);
        if (mParams.initialAudioState
                == CallAudioModeStateMachine.ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING) {
            when(mAudioManager.getMode())
Loading