Fix regression related the incoming call notifier in Telecom.

Telecom has an incoming call notification that is shown in some cases
where answering a new incoming call may cause a call in another app to be
disconnected.  The CTS Verifier suite "telecom incoming self-managed
connection" test verifies that when the user answers an incoming call from
that dialog that one of the ongoing calls is dropped and the incoming call
can be answered.

To correct this, added a "request origin" parameter to the "answerCall"
method in CallsManager.  This is propagated to call sequencing and used
to indicate that the origin of the call answer request is Telecom call
disambiguation taking place in the incoming call notifier.  This allows
sequencing to allow a managed call to be disconnected in favor of a
voip call, even though that is against our usual sequencing policy.

Flag: NONE Bug fix.
Test: Manual test by re-running the CTS verifier test.
Test: Added testAnswerCallAcceptedFromTelecom in the CallSequencingTests
suite.
Fixes: 407486249
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3de6a5f7c1c8625fa0fa74b86b53177732914262)
Merged-In: I02b2fcaf30b5dfa0a65db30adb646756838de945
Change-Id: I02b2fcaf30b5dfa0a65db30adb646756838de945
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 4d59fc8..f959c52 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -42,6 +42,7 @@
 import static android.telecom.TelecomManager.VERY_SHORT_CALL_TIME_MS;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -155,6 +156,8 @@
 import com.android.server.telecom.callsequencing.TransactionManager;
 import com.android.server.telecom.callsequencing.voip.VoipCallMonitorLegacy;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -189,6 +192,23 @@
  */
 public class CallsManager extends Call.ListenerBase
         implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy {
+    /**
+     * The origin of the request is not known.
+     */
+    public static final int REQUEST_ORIGIN_UNKNOWN = -1;
+
+    /**
+     * The request originated from a Telecom-provided disambiguation.
+     */
+    public static final int REQUEST_ORIGIN_TELECOM_DISAMBIGUATION = 1;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "REQUEST_ORIGIN_" },
+            value = {REQUEST_ORIGIN_UNKNOWN, REQUEST_ORIGIN_TELECOM_DISAMBIGUATION})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RequestOrigin {}
 
     // TODO: Consider renaming this CallsManagerPlugin.
     @VisibleForTesting
@@ -3268,19 +3288,32 @@
     }
 
     /**
+     * Similar to {@link #answerCall(Call, int, int)}, instructs Telecom to answer the specified
+     * call.  This prototype assumes that the origin of the request is
+     * {@link #REQUEST_ORIGIN_UNKNOWN} for the time being.  In most cases this is likely a user
+     * request to answer a call, but could be internal to Telecom.
+     * @param call The call to answer.
+     * @param videoState The video state in which to answer the call.
+     */
+    public void answerCall(Call call, int videoState) {
+        answerCall(call, videoState, REQUEST_ORIGIN_UNKNOWN);
+    }
+
+    /**
      * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
      * the user opting to answer said call.
      *
      * @param call The call to answer.
      * @param videoState The video state in which to answer the call.
+     * @param requestOrigin The origin of the request being made.
      */
     @VisibleForTesting
-    public void answerCall(Call call, int videoState) {
+    public void answerCall(Call call, int videoState, @RequestOrigin int requestOrigin) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to answer a non-existent call %s", call);
         }
-        mCallSequencingAdapter.answerCall(call, videoState);
+        mCallSequencingAdapter.answerCall(call, videoState, requestOrigin);
     }
 
     /**
@@ -3293,7 +3326,7 @@
      * <p>
      * Note: This is only used when {@link FeatureFlags#enableCallSequencing()} is false.
      */
-    public void answerCallOld(Call call, int videoState) {
+    public void answerCallOld(Call call, int videoState, @RequestOrigin int requestOrigin) {
         if (call.isTransactionalCall()) {
             // InCallAdapter is requesting to answer the given transactioanl call. Must get an ack
             // from the client via a transaction before answering.
diff --git a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
index 523b841..2efc79c 100644
--- a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
+++ b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
@@ -184,7 +184,8 @@
                 // Answer the current ringing call.
                 Call incomingCall = mCallsManager.getIncomingCallNotifier().getIncomingCall();
                 if (incomingCall != null) {
-                    mCallsManager.answerCall(incomingCall, incomingCall.getVideoState());
+                    mCallsManager.answerCall(incomingCall, incomingCall.getVideoState(),
+                            CallsManager.REQUEST_ORIGIN_TELECOM_DISAMBIGUATION);
                 }
             } finally {
                 Log.endSession();
diff --git a/src/com/android/server/telecom/callsequencing/CallSequencingController.java b/src/com/android/server/telecom/callsequencing/CallSequencingController.java
index d418cff..f396257 100644
--- a/src/com/android/server/telecom/callsequencing/CallSequencingController.java
+++ b/src/com/android/server/telecom/callsequencing/CallSequencingController.java
@@ -169,11 +169,16 @@
      * sequencing provided that the calls that are being manipulated are across phone accounts.
      * @param incomingCall The incoming call to be answered.
      * @param videoState The video state configuration for the provided call.
+     * @param requestOrigin The origin of the request to answer the call; this can impact sequencing
+     *                      decisions as requests that Telecom makes can override rules we have set
+     *                      for actions which originate from outside.
      */
-    public void answerCall(Call incomingCall, int videoState) {
+    public void answerCall(Call incomingCall, int videoState,
+            @CallsManager.RequestOrigin int requestOrigin) {
         Log.i(this, "answerCall: Beginning call sequencing transaction for answering "
                 + "incoming call.");
-        holdActiveCallForNewCallWithSequencing(incomingCall).thenComposeAsync((result) -> {
+        holdActiveCallForNewCallWithSequencing(incomingCall, requestOrigin)
+                .thenComposeAsync((result) -> {
                 if (result) {
                     mCallsManager.requestFocusActionAnswerCall(incomingCall, videoState);
                 } else {
@@ -190,7 +195,8 @@
      * @param call The self-managed call that's waiting to go active.
      */
     public void handleSetSelfManagedCallActive(Call call) {
-        holdActiveCallForNewCallWithSequencing(call).thenComposeAsync((result) -> {
+        holdActiveCallForNewCallWithSequencing(call, CallsManager.REQUEST_ORIGIN_UNKNOWN)
+                .thenComposeAsync((result) -> {
                 if (result) {
                     Log.i(this, "markCallAsActive: requesting focus for self managed call "
                             + "before setting active.");
@@ -218,7 +224,7 @@
      */
     public void transactionHoldPotentialActiveCallForNewCallSequencing(
             Call newCall, OutcomeReceiver<Boolean, CallException> callback) {
-        holdActiveCallForNewCallWithSequencing(newCall)
+        holdActiveCallForNewCallWithSequencing(newCall, CallsManager.REQUEST_ORIGIN_UNKNOWN)
                 .thenComposeAsync((result) -> {
                     if (result) {
                         // Either we were able to hold the active call or the active call was
@@ -245,13 +251,14 @@
      * Attempts to hold the active call so that the provided call can go active. This is done via
      * call sequencing and the resulting future is an indication of whether that request
      * has succeeded.
+     *
      * @param call The call that's waiting to go active.
      * @return The {@link CompletableFuture} indicating the result of whether the
-     *         active call was able to be held (if applicable).
+     * active call was able to be held (if applicable).
      */
     @VisibleForTesting
     public CompletableFuture<Boolean> holdActiveCallForNewCallWithSequencing(
-            Call call) {
+            Call call, int requestOrigin) {
         Call activeCall = (Call) mCallsManager.getConnectionServiceFocusManager()
                 .getCurrentFocusCall();
         Log.i(this, "holdActiveCallForNewCallWithSequencing, newCall: %s, "
@@ -292,8 +299,14 @@
                     // and the held call is a carrier call, then disconnect the held call. The
                     // idea is that if we have a held carrier call and the incoming call is a
                     // VOIP call, we don't want to force the carrier call to auto-disconnect).
-                    if (isManagedCall(heldCall) && isVoipCall(call)) {
+                    // Note: If the origin of this request was from the Telecom call incoming call
+                    // disambiguation notification, we will allow the request to continue.
+                    if (isManagedCall(heldCall) && isVoipCall(call) && requestOrigin
+                            != CallsManager.REQUEST_ORIGIN_TELECOM_DISAMBIGUATION) {
                         // Otherwise, fail the transaction.
+                        Log.w(this, "holdActiveCallForNewCallWithSequencing: ignoring request to "
+                                + "disconnect carrier call %s for voip call %s.", activeCall,
+                                heldCall);
                         return CompletableFuture.completedFuture(false);
                     } else {
                         isSequencingRequiredHeldAndActive = !arePhoneAccountsSame(
diff --git a/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java b/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
index 611bb9e..b2cfcab 100644
--- a/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
+++ b/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
@@ -70,12 +70,14 @@
      * (mIsCallSequencingEnabled) is enabled.
      * @param incomingCall The incoming call that should be answered.
      * @param videoState The video state configuration associated with the call.
+     * @param requestOrigin The origin of the request.
      */
-    public void answerCall(Call incomingCall, int videoState) {
+    public void answerCall(Call incomingCall, int videoState,
+            @CallsManager.RequestOrigin int requestOrigin) {
         if (mIsCallSequencingEnabled && !incomingCall.isTransactionalCall()) {
-            mSequencingController.answerCall(incomingCall, videoState);
+            mSequencingController.answerCall(incomingCall, videoState, requestOrigin);
         } else {
-            mCallsManager.answerCallOld(incomingCall, videoState);
+            mCallsManager.answerCallOld(incomingCall, videoState, requestOrigin);
         }
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/CallSequencingTests.java b/tests/src/com/android/server/telecom/tests/CallSequencingTests.java
index 66dc879..fc476f8 100644
--- a/tests/src/com/android/server/telecom/tests/CallSequencingTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallSequencingTests.java
@@ -194,7 +194,7 @@
     public void testAnswerCall() {
         // This will allow holdActiveCallForNewCallWithSequencing to immediately return true
         setActiveCallFocus(null);
-        mController.answerCall(mNewCall, 0);
+        mController.answerCall(mNewCall, 0, CallsManager.REQUEST_ORIGIN_UNKNOWN);
         verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS))
                 .requestFocusActionAnswerCall(eq(mNewCall), eq(0));
     }
@@ -203,13 +203,28 @@
     @Test
     public void testAnswerCallFail() {
         setupHoldActiveCallForNewCallFailMocks();
-        mController.answerCall(mNewCall, 0);
+        mController.answerCall(mNewCall, 0, CallsManager.REQUEST_ORIGIN_UNKNOWN);
         verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(0))
                 .requestFocusActionAnswerCall(eq(mNewCall), eq(0));
     }
 
     @SmallTest
     @Test
+    public void testAnswerCallAcceptedFromTelecom() {
+        setPhoneAccounts(mNewCall, mActiveCall, false);
+        setActiveCallFocus(mActiveCall);
+        when(mCallsManager.canHold(mActiveCall)).thenReturn(true);
+        when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(true));
+
+        when(mHeldCall.isSelfManaged()).thenReturn(false);
+        when(mNewCall.isSelfManaged()).thenReturn(true);
+        mController.answerCall(mNewCall, 0, CallsManager.REQUEST_ORIGIN_TELECOM_DISAMBIGUATION);
+        verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(1))
+                .requestFocusActionAnswerCall(eq(mNewCall), eq(0));
+    }
+
+    @SmallTest
+    @Test
     public void testSetSelfManagedCallActive() {
         // This will allow holdActiveCallForNewCallWithSequencing to immediately return true
         setActiveCallFocus(null);
@@ -270,7 +285,8 @@
     public void testHoldCallForNewCall_NoActiveCall() {
         setActiveCallFocus(null);
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         assertTrue(waitForFutureResult(resultFuture, false));
     }
 
@@ -285,13 +301,15 @@
         // Cross phone account case (sequencing enabled)
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         assertTrue(waitForFutureResult(resultFuture, false));
 
         // Same phone account case
         setPhoneAccounts(mNewCall, mActiveCall, true);
         assertTrue(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
-        resultFuture = mController.holdActiveCallForNewCallWithSequencing(mNewCall);
+        resultFuture = mController.holdActiveCallForNewCallWithSequencing(mNewCall,
+                CallsManager.REQUEST_ORIGIN_UNKNOWN);
         assertTrue(waitForFutureResult(resultFuture, false));
     }
 
@@ -312,7 +330,8 @@
         // disconnect the active (carrier) call.
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         verify(mHeldCall, timeout(SEQUENCING_TIMEOUT_MS)).disconnect();
         verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold();
         verify(mNewCall).increaseHeldByThisCallCount();
@@ -332,7 +351,8 @@
         // Cross phone account case (sequencing enabled)
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold();
         verify(mNewCall).increaseHeldByThisCallCount();
         assertTrue(waitForFutureResult(resultFuture, false));
@@ -352,7 +372,8 @@
 
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).disconnect(anyString());
         assertTrue(waitForFutureResult(resultFuture, false));
     }
@@ -372,7 +393,8 @@
         // disconnect the active (carrier) call.
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         assertFalse(waitForFutureResult(resultFuture, true));
     }
 
@@ -387,7 +409,8 @@
 
         assertTrue(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         assertTrue(waitForFutureResult(resultFuture, true));
     }
 
@@ -404,7 +427,8 @@
 
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         verify(mNewCall, timeout(SEQUENCING_TIMEOUT_MS)).reject(
                 anyBoolean(), anyString(), anyString());
         assertFalse(waitForFutureResult(resultFuture, true));
@@ -423,7 +447,8 @@
 
         assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall));
         CompletableFuture<Boolean> resultFuture = mController
-                .holdActiveCallForNewCallWithSequencing(mNewCall);
+                .holdActiveCallForNewCallWithSequencing(mNewCall,
+                        CallsManager.REQUEST_ORIGIN_UNKNOWN);
         assertFalse(waitForFutureResult(resultFuture, true));
     }