Add flag for call audio mode change.

Adding a flag for the new behavior which takes place when swapping between
a PSTN and VOIP call; this was a bug fix made to correct missing audio
mode transitions.

Bug: 289861657
Test: atest CallAudioManagerTest
Change-Id: I0feba38a3a529d139f251b995c0ddda40d89465b
diff --git a/flags/telecom_callaudioroutestatemachine_flags.aconfig b/flags/telecom_callaudioroutestatemachine_flags.aconfig
index c231551..e64fa68 100644
--- a/flags/telecom_callaudioroutestatemachine_flags.aconfig
+++ b/flags/telecom_callaudioroutestatemachine_flags.aconfig
@@ -5,4 +5,11 @@
   namespace: "telecom"
   description: "Fix supported routes wrongly include bluetooth issue."
   bug: "292599751"
+}
+
+flag {
+  name: "ensure_audio_mode_updates_on_foreground_call_change"
+  namespace: "telecom"
+  description: "Ensure that the audio mode is updated anytime the foreground call changes."
+  bug: "289861657"
 }
\ No newline at end of file
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 6557dc6..427b0ce 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -31,6 +31,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+import com.android.server.telecom.flags.FeatureFlags;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -62,6 +63,7 @@
     private final Ringer mRinger;
     private final RingbackPlayer mRingbackPlayer;
     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
+    private final FeatureFlags mFeatureFlags;
 
     private Call mStreamingCall;
     private Call mForegroundCall;
@@ -76,7 +78,8 @@
             Ringer ringer,
             RingbackPlayer ringbackPlayer,
             BluetoothStateReceiver bluetoothStateReceiver,
-            DtmfLocalTonePlayer dtmfLocalTonePlayer) {
+            DtmfLocalTonePlayer dtmfLocalTonePlayer,
+            FeatureFlags featureFlags) {
         mActiveDialingOrConnectingCalls = new LinkedHashSet<>(1);
         mRingingCalls = new LinkedHashSet<>(1);
         mHoldingCalls = new LinkedHashSet<>(1);
@@ -102,6 +105,7 @@
         mRingbackPlayer = ringbackPlayer;
         mBluetoothStateReceiver = bluetoothStateReceiver;
         mDtmfLocalTonePlayer = dtmfLocalTonePlayer;
+        mFeatureFlags = featureFlags;
 
         mPlayerFactory.setCallAudioManager(this);
         mCallAudioModeStateMachine.setCallAudioManager(this);
@@ -772,20 +776,26 @@
                     possibleConnectingCall = call;
                 }
             }
-            // Prefer a connecting call
-            if (possibleConnectingCall != null) {
-                mForegroundCall = possibleConnectingCall;
+            if (mFeatureFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
+                // Prefer a connecting call
+                if (possibleConnectingCall != null) {
+                    mForegroundCall = possibleConnectingCall;
+                } else {
+                    // Next, prefer an active or dialing call which is not in the process of being
+                    // disconnected.
+                    mForegroundCall = mActiveDialingOrConnectingCalls
+                            .stream()
+                            .filter(c -> (c.getState() == CallState.ACTIVE
+                                    || c.getState() == CallState.DIALING)
+                                    && !c.isLocallyDisconnecting())
+                            .findFirst()
+                            // If we can't find one, then just fall back to the first one.
+                            .orElse(mActiveDialingOrConnectingCalls.iterator().next());
+                }
             } else {
-                // Next, prefer an active or dialing call which is not in the process of being
-                // disconnected.
-                mForegroundCall = mActiveDialingOrConnectingCalls
-                        .stream()
-                        .filter(c -> (c.getState() == CallState.ACTIVE
-                                || c.getState() == CallState.DIALING)
-                                && !c.isLocallyDisconnecting())
-                        .findFirst()
-                        // If we can't find one, then just fall back to the first one.
-                        .orElse(mActiveDialingOrConnectingCalls.iterator().next());
+                // Legacy (buggy) behavior.
+                mForegroundCall = possibleConnectingCall == null ?
+                        mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall;
             }
         } else if (mRingingCalls.size() > 0) {
             mForegroundCall = mRingingCalls.iterator().next();
@@ -806,7 +816,8 @@
             mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                     CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
 
-            if (mForegroundCall != null) {
+            if (mForegroundCall != null
+                    && mFeatureFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
                 // Ensure the voip audio mode for the new foreground call is taken into account.
                 mCallAudioModeStateMachine.sendMessageWithArgs(
                         CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE,
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 0e78ca1..ba02009 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -657,7 +657,7 @@
                 this, callAudioModeStateMachineFactory.create(systemStateHelper,
                 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), featureFlags),
                 playerFactory, mRinger, new RingbackPlayer(playerFactory),
-                bluetoothStateReceiver, mDtmfLocalTonePlayer);
+                bluetoothStateReceiver, mDtmfLocalTonePlayer, featureFlags);
 
         mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(mRequester);
         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
index c8ceea9..df281ca 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
@@ -36,6 +36,7 @@
 import com.android.server.telecom.Ringer;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+import com.android.server.telecom.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -79,6 +80,8 @@
     @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
     @Mock private TelecomSystem.SyncRoot mLock;
 
+    @Mock private FeatureFlags mFlags;
+
     private CallAudioManager mCallAudioManager;
 
     @Override
@@ -94,6 +97,7 @@
             return mockInCallTonePlayer;
         }).when(mPlayerFactory).createPlayer(anyInt());
         when(mCallsManager.getLock()).thenReturn(mLock);
+        when(mFlags.ensureAudioModeUpdatesOnForegroundCallChange()).thenReturn(true);
         mCallAudioManager = new CallAudioManager(
                 mCallAudioRouteStateMachine,
                 mCallsManager,
@@ -102,7 +106,8 @@
                 mRinger,
                 mRingbackPlayer,
                 mBluetoothStateReceiver,
-                mDtmfLocalTonePlayer);
+                mDtmfLocalTonePlayer,
+                mFlags);
     }
 
     @Override
@@ -278,19 +283,27 @@
         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         assertMessageArgEquality(expectedArgs, captor.getValue());
-        // Expet another invocation due to audio mode change signal.
-        verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
-                anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
-
+        if (mFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
+            // Expect another invocation due to audio mode change signal.
+            verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
+                    anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        } else {
+            verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
+                    anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        }
 
         when(call.getState()).thenReturn(CallState.ACTIVE);
         mCallAudioManager.onCallStateChanged(call, CallState.DIALING, CallState.ACTIVE);
         verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         assertMessageArgEquality(expectedArgs, captor.getValue());
-        verify(mCallAudioModeStateMachine, times(4)).sendMessageWithArgs(
-                anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
-
+        if (mFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
+            verify(mCallAudioModeStateMachine, times(4)).sendMessageWithArgs(
+                    anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        } else {
+            verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
+                    anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        }
         disconnectCall(call);
         stopTone();
 
@@ -298,6 +311,12 @@
         verifyProperCleanup();
     }
 
+    @Test
+    public void testSingleOutgoingCallWithoutAudioModeUpdateOnForegroundCallChange() {
+        when(mFlags.ensureAudioModeUpdatesOnForegroundCallChange()).thenReturn(false);
+        testSingleOutgoingCall();
+    }
+
     @MediumTest
     @Test
     public void testRingbackStartStop() {
@@ -329,9 +348,14 @@
         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         assertMessageArgEquality(expectedArgs, captor.getValue());
-        // Expect an extra time due to audio mode change signal
-        verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
-                anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        if (mFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
+            // Expect an extra time due to audio mode change signal
+            verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
+                    anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        } else {
+            verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
+                    anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
+        }
 
         // Ensure we started ringback.
         verify(mRingbackPlayer).startRingbackForCall(any(Call.class));
@@ -353,6 +377,12 @@
         verify(mRingbackPlayer, times(1)).startRingbackForCall(any(Call.class));
     }
 
+    @Test
+    public void testRingbackStartStopWithoutAudioModeUpdateOnForegroundCallChange() {
+        when(mFlags.ensureAudioModeUpdatesOnForegroundCallChange()).thenReturn(false);
+        testRingbackStartStop();
+    }
+
     @SmallTest
     @Test
     public void testNewCallGoesToAudioProcessing() {
@@ -708,6 +738,10 @@
     @SmallTest
     @Test
     public void testTriggerAudioManagerModeChange() {
+        if (!mFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
+            // Skip if the new behavior isn't in use.
+            return;
+        }
         // Start with an incoming PSTN call
         Call pstnCall = mock(Call.class);
         when(pstnCall.getState()).thenReturn(CallState.RINGING);