Add handover permission, fill in some missing API gaps.

Adding the ACCEPT_HANDOVER runtime permission which an app must have in
order to accept handovers (this is per design).

Adding missing onHandoverComplete method in the android.telecom.Connection
API (per design).

Finishing plumbing for android.telecom.Call#onHandoverComplete API.

Fix issue where the new handover API methods would never get called; the
legacy handover extra was being used in this case when it should not have
been.

Bug: 65415068
Test: Verified using new CTS tests
Change-Id: If1558f6a23911862c02ac5b18fb62d86911ed7e2
Merged-In: If1558f6a23911862c02ac5b18fb62d86911ed7e2
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 8c7d6b3..8a4b046 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -1961,6 +1961,15 @@
         }
     }
 
+    /** {@hide} */
+    final void internalOnHandoverComplete() {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(() -> callback.onHandoverComplete(call));
+        }
+    }
+
     private void fireStateChanged(final int newState) {
         for (CallbackRecord<Callback> record : mCallbackRecords) {
             final Call call = this;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d71fde2..8150efa 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2800,6 +2800,15 @@
     public void onCallEvent(String event, Bundle extras) {}
 
     /**
+     * Notifies this {@link Connection} that a handover has completed.
+     * <p>
+     * A handover is initiated with {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int,
+     * Bundle)} on the initiating side of the handover, and on the receiving side with
+     * {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)}.
+     */
+    public void onHandoverComplete() {}
+
+    /**
      * Notifies this {@link Connection} of a change to the extras made outside the
      * {@link ConnectionService}.
      * <p>
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index e37aeb4..990b0b4 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -140,6 +140,7 @@
     private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
     private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
     private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+    private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
     private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
     private static final String SESSION_START_RTT = "CS.+RTT";
     private static final String SESSION_STOP_RTT = "CS.-RTT";
@@ -179,6 +180,7 @@
     private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
     private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
     private static final int MSG_HANDOVER_FAILED = 32;
+    private static final int MSG_HANDOVER_COMPLETE = 33;
 
     private static Connection sNullConnection;
 
@@ -298,6 +300,19 @@
         }
 
         @Override
+        public void handoverComplete(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_HANDOVER_COMPLETE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_HANDOVER_COMPLETE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void abort(String callId, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_ABORT);
             try {
@@ -1028,6 +1043,19 @@
                     }
                     break;
                 }
+                case MSG_HANDOVER_COMPLETE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_HANDOVER_COMPLETE);
+                        String callId = (String) args.arg1;
+                        notifyHandoverComplete(callId);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_ON_EXTRAS_CHANGED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -1445,19 +1473,24 @@
             final ConnectionRequest request,
             boolean isIncoming,
             boolean isUnknown) {
+        boolean isLegacyHandover = request.getExtras() != null &&
+                request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
+        boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
+                TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
         Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
-                        "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
-                isIncoming,
-                isUnknown);
+                        "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
+                callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
+                isHandover);
 
         Connection connection = null;
-        if (getApplicationContext().getApplicationInfo().targetSdkVersion >
-                Build.VERSION_CODES.O_MR1 && request.getExtras() != null &&
-                request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) {
+        if (isHandover) {
+            PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
+                    ? (PhoneAccountHandle) request.getExtras().getParcelable(
+                    TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
             if (!isIncoming) {
-                connection = onCreateOutgoingHandoverConnection(callManagerAccount, request);
+                connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
             } else {
-                connection = onCreateIncomingHandoverConnection(callManagerAccount, request);
+                connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
             }
         } else {
             connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
@@ -1754,6 +1787,19 @@
     }
 
     /**
+     * Notifies a {@link Connection} that a handover has completed.
+     *
+     * @param callId The ID of the call which completed handover.
+     */
+    private void notifyHandoverComplete(String callId) {
+        Log.d(this, "notifyHandoverComplete(%s)", callId);
+        Connection connection = findConnectionForAction(callId, "notifyHandoverComplete");
+        if (connection != null) {
+            connection.onHandoverComplete();
+        }
+    }
+
+    /**
      * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.
      * <p>
      * These extra changes can originate from Telecom itself, or from an {@link InCallService} via
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 74fa62d..fcf04c9 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -81,6 +81,7 @@
     private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
     private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
     private static final int MSG_ON_HANDOVER_FAILED = 12;
+    private static final int MSG_ON_HANDOVER_COMPLETE = 13;
 
     /** Default Handler used to consolidate binder method calls onto a single thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -157,6 +158,11 @@
                     mPhone.internalOnHandoverFailed(callId, error);
                     break;
                 }
+                case MSG_ON_HANDOVER_COMPLETE: {
+                    String callId = (String) msg.obj;
+                    mPhone.internalOnHandoverComplete(callId);
+                    break;
+                }
                 default:
                     break;
             }
@@ -237,6 +243,11 @@
         public void onHandoverFailed(String callId, int error) {
             mHandler.obtainMessage(MSG_ON_HANDOVER_FAILED, error, 0, callId).sendToTarget();
         }
+
+        @Override
+        public void onHandoverComplete(String callId) {
+            mHandler.obtainMessage(MSG_ON_HANDOVER_COMPLETE, callId).sendToTarget();
+        }
     }
 
     private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index b5394b9..99f94f2 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -230,6 +230,13 @@
         }
     }
 
+    final void internalOnHandoverComplete(String callId) {
+        Call call = mCallByTelecomCallId.get(callId);
+        if (call != null) {
+            call.internalOnHandoverComplete();
+        }
+    }
+
     /**
      * Called to destroy the phone and cleanup any lingering calls.
      */
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 96c6e0a..7e89745 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -379,6 +379,17 @@
     public static final String EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER";
 
     /**
+     * When {@code true} indicates that a request to create a new connection is for the purpose of
+     * a handover.  Note: This is used with the
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)} API as part of the
+     * internal communication mechanism with the {@link android.telecom.ConnectionService}.  It is
+     * not the same as the legacy {@link #EXTRA_IS_HANDOVER} extra.
+     * @hide
+     */
+    public static final String EXTRA_IS_HANDOVER_CONNECTION =
+            "android.telecom.extra.IS_HANDOVER_CONNECTION";
+
+    /**
      * Parcelable extra used with {@link #EXTRA_IS_HANDOVER} to indicate the source
      * {@link PhoneAccountHandle} when initiating a handover which {@link ConnectionService}
      * the handover is from.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 02e1ff8..3dbc8dd 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -104,6 +104,8 @@
     void handoverFailed(String callId, in ConnectionRequest request,
             int error, in Session.Info sessionInfo);
 
+    void handoverComplete(String callId, in Session.Info sessionInfo);
+
     void connectionServiceFocusLost(in Session.Info sessionInfo);
 
     void connectionServiceFocusGained(in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 110109e..b9563fa 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -56,4 +56,6 @@
     void onRttInitiationFailure(String callId, int reason);
 
     void onHandoverFailed(String callId, int error);
+
+    void onHandoverComplete(String callId);
 }