Prepare event-based handovers for deprecation.

Add a toast from the handover initiation to warn the caller of the
deprecation to come.  This will be removed near the end of the Q
development cycle.

Fix a bug in the event-based handover where a destination CS doesn't
create a handover call and can cause a Telecom crash.

Update the telecom test app to support handovers using the public APIs.

Bug: 110846969
Test: Manual using test app.
Change-Id: Icb31216ef0f2a7cf5e6c34975c9eab0b3e5782c9
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 342755d..76eed40 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -50,6 +50,7 @@
 import android.text.TextUtils;
 import android.util.StatsLog;
 import android.os.UserHandle;
+import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.IVideoProvider;
@@ -2272,10 +2273,20 @@
     public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
         if (mConnectionService != null) {
             if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
-                if (targetSdkVer > Build.VERSION_CODES.O_MR1) {
+                if (targetSdkVer > Build.VERSION_CODES.P) {
                     Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
-                            " for API > 27(O-MR1)");
-                    // TODO: Add "return" after DUO team adds new API support for handover
+                            " for API > 28(P)");
+                    // Event-based Handover APIs are deprecated, so inform the user.
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(mContext, "WARNING: Event-based handover APIs are deprecated "
+                                            + "and will no longer function in Android Q.",
+                                    Toast.LENGTH_LONG).show();
+                        }
+                    });
+
+                    // Uncomment and remove toast at feature complete: return;
                 }
 
                 // Handover requests are targeted at Telecom, not the ConnectionService.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 1575cc4..eb5f4ae 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -3559,6 +3559,10 @@
                 mCallAudioManager.getCallAudioState());
         Call handoverToCall = startOutgoingCall(handoverFromCall.getHandle(), handoverToHandle,
                 extras, getCurrentUserHandle(), null /* originalIntent */);
+        if (handoverToCall == null) {
+            handoverFromCall.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+            return;
+        }
         Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER,
                 "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId());
         handoverFromCall.setHandoverDestinationCall(handoverToCall);
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 48451d1..02443ba 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -19,9 +19,10 @@
           package="com.android.server.telecom.testapps">
 
     <uses-sdk
-        android:minSdkVersion="23"
-        android:targetSdkVersion="23" />
+        android:minSdkVersion="28"
+        android:targetSdkVersion="28" />
 
+    <uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
index 93a2c7f..959b855 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
@@ -119,12 +119,12 @@
         mPlaceIncomingCallButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                placeIncomingCall(false /* isHandoverFrom */);
+                placeIncomingCall();
             }
         });
         mHandoverFrom = (Button) findViewById(R.id.handoverFrom);
         mHandoverFrom.setOnClickListener((v -> {
-            placeIncomingCall(true /* isHandoverFrom */);
+            initiateHandover();
         }));
 
         mUseAcct1Button = findViewById(R.id.useAcct1Button);
@@ -176,7 +176,14 @@
         tm.placeCall(Uri.parse(mNumber.getText().toString()), extras);
     }
 
-    private void placeIncomingCall(boolean isHandoverFrom) {
+    private void initiateHandover() {
+        TelecomManager tm = TelecomManager.from(this);
+        PhoneAccountHandle phoneAccountHandle = getSelectedPhoneAccountHandle();
+        Uri address = Uri.parse(mNumber.getText().toString());
+        tm.acceptHandover(address, VideoProfile.STATE_BIDIRECTIONAL, phoneAccountHandle);
+    }
+
+    private void placeIncomingCall() {
         TelecomManager tm = TelecomManager.from(this);
         PhoneAccountHandle phoneAccountHandle = getSelectedPhoneAccountHandle();
 
@@ -196,9 +203,6 @@
             extras.putInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE,
                     VideoProfile.STATE_BIDIRECTIONAL);
         }
-        if (isHandoverFrom) {
-            extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
-        }
         tm.addNewIncomingCall(getSelectedPhoneAccountHandle(), extras);
     }
 
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
index fca05dc..f2b6496 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
@@ -24,9 +24,7 @@
 import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
 
-import java.util.Objects;
 import java.util.Random;
 
 /**
@@ -45,13 +43,25 @@
             PhoneAccountHandle connectionManagerAccount,
             final ConnectionRequest request) {
 
-        return createSelfManagedConnection(request, false);
+        return createSelfManagedConnection(request, false, false /* isHandover */);
     }
 
     @Override
     public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
-        return createSelfManagedConnection(request, true);
+        return createSelfManagedConnection(request, true, false /* isHandover */);
+    }
+
+    @Override
+    public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+            ConnectionRequest request) {
+        return createSelfManagedConnection(request, false, true /* isHandover */);
+    }
+
+    @Override
+    public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+            ConnectionRequest request) {
+        return createSelfManagedConnection(request, true, true /* isHandover */);
     }
 
     @Override
@@ -77,7 +87,8 @@
         mCallList.notifyConnectionServiceFocusGained();
     }
 
-    private Connection createSelfManagedConnection(ConnectionRequest request, boolean isIncoming) {
+    private Connection createSelfManagedConnection(ConnectionRequest request, boolean isIncoming,
+            boolean isHandover) {
         SelfManagedConnection connection = new SelfManagedConnection(mCallList,
                 getApplicationContext(), isIncoming);
         connection.setListener(mCallList.getConnectionListener());
@@ -98,11 +109,10 @@
         if (requestExtras != null) {
             boolean isHoldable = requestExtras.getBoolean(EXTRA_HOLDABLE, false);
             Log.i(this, "createConnection: isHandover=%b, handoverFrom=%s, holdable=%b",
-                    requestExtras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER),
+                    isHandover,
                     requestExtras.getString(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT),
                     isHoldable);
-            connection.setIsHandover(requestExtras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER,
-                    false));
+            connection.setIsHandover(isHandover);
             if (isHoldable) {
                 connection.setConnectionCapabilities(connection.getConnectionCapabilities() |
                         Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
index 9851253..2a5b33a 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Bundle;
 import android.telecom.Call;
@@ -209,11 +210,8 @@
 
         handoverButton.setOnClickListener((v) -> {
             Call call = mCallList.getCall(0);
-            Bundle extras = new Bundle();
-            extras.putParcelable(Call.EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE,
-                    getHandoverToPhoneAccountHandle());
-            extras.putInt(Call.EXTRA_HANDOVER_VIDEO_STATE, VideoProfile.STATE_BIDIRECTIONAL);
-            call.sendCallEvent(Call.EVENT_REQUEST_HANDOVER, extras);
+            call.handoverTo(getHandoverToPhoneAccountHandle(), VideoProfile.STATE_BIDIRECTIONAL,
+                    null);
         });
     }
 
@@ -263,17 +261,8 @@
     }
 
     private PhoneAccountHandle getHandoverToPhoneAccountHandle() {
-        TelecomManager tm = TelecomManager.from(this);
-
-        List<PhoneAccountHandle> handles = tm.getAllPhoneAccountHandles();
-        Optional<PhoneAccountHandle> found = handles.stream().filter(h -> {
-            PhoneAccount account = tm.getPhoneAccount(h);
-            Bundle extras = account.getExtras();
-            return extras != null && extras.getBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO);
-        }).findFirst();
-        PhoneAccountHandle foundHandle = found.orElse(null);
-        Log.i(TestInCallUI.class.getSimpleName(), "getHandoverToPhoneAccountHandle() = " +
-            foundHandle);
-        return foundHandle;
+        return new PhoneAccountHandle(new ComponentName(
+                SelfManagedCallList.class.getPackage().getName(),
+                SelfManagedConnectionService.class.getName()), "1");
     }
 }