Reduce latency for InCallUI by adding an intermediate state

Immediately add a call to Telecomm before the outgoing call broadcast
intent returns, this will reduce the latency for the InCallUI by
allowing the InCallActivity to start before Telecomm has responded.

Bug: 16396523

Change-Id: I4edd533083d3d7edb7a14d179c174223374a4282
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 16dc323..82827ad 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -188,7 +188,7 @@
      * that the user is attempting to connect to via the gateway, the actual handle to dial in
      * order to connect the call via the gateway, as well as the package name of the gateway
      * service. */
-    private final GatewayInfo mGatewayInfo;
+    private GatewayInfo mGatewayInfo;
 
     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
 
@@ -307,7 +307,7 @@
             boolean isConference) {
         mState = isConference ? CallState.ACTIVE : CallState.NEW;
         mRepository = repository;
-        setHandle(handle, CallPropertyPresentation.ALLOWED);
+        setHandle(handle);
         mGatewayInfo = gatewayInfo;
         mConnectionManagerPhoneAccountHandle = connectionManagerPhoneAccountHandle;
         mTargetPhoneAccountHandle = targetPhoneAccountHandle;
@@ -383,6 +383,11 @@
         return mHandlePresentation;
     }
 
+
+    void setHandle(Uri handle) {
+        setHandle(handle, CallPropertyPresentation.ALLOWED);
+    }
+
     void setHandle(Uri handle, int presentation) {
         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
             mHandle = handle;
@@ -466,6 +471,10 @@
         return mGatewayInfo;
     }
 
+    void setGatewayInfo(GatewayInfo gatewayInfo) {
+        mGatewayInfo = gatewayInfo;
+    }
+
     PhoneAccountHandle getConnectionManagerPhoneAccount() {
         return mConnectionManagerPhoneAccountHandle;
     }
diff --git a/src/com/android/telecomm/CallActivity.java b/src/com/android/telecomm/CallActivity.java
index 607d994..0e231b0 100644
--- a/src/com/android/telecomm/CallActivity.java
+++ b/src/com/android/telecomm/CallActivity.java
@@ -21,10 +21,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Bundle;
 import android.telecomm.PhoneAccountHandle;
 import android.telecomm.TelecommManager;
 import android.telecomm.TelecommManager;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
 /**
@@ -105,8 +107,17 @@
      * @param intent Call intent containing data about the handle to call.
      */
     private void processOutgoingCallIntent(Intent intent) {
+        String uriString = intent.getData().getSchemeSpecificPart();
+        Uri handle = Uri.fromParts(
+                PhoneNumberUtils.isUriNumber(uriString) ? "sip" : "tel", uriString, null);
+        PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
+                TelecommManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+
+        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
+        Call call = mCallsManager.startOutgoingCall(handle, phoneAccountHandle);
+
         NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
-                mCallsManager, intent, isDefaultDialer());
+                mCallsManager, call, intent, isDefaultDialer());
         final boolean success = broadcaster.processIntent();
         setResult(success ? RESULT_OK : RESULT_CANCELED);
     }
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index e75a9a1..b7cc233 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -259,7 +259,7 @@
      * Starts the process to attach the call to a connection service.
      *
      * @param phoneAccountHandle The phone account which contains the component name of the connection
-     *                     serivce to use for this call.
+     *                     service to use for this call.
      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
      */
     void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
@@ -281,6 +281,43 @@
         call.startCreateConnection();
     }
 
+
+    /**
+     * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
+     *
+     * NOTE: emergency calls will never pass through this because they call
+     * placeOutgoingCall directly.
+     *
+     */
+    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle) {
+        // We only allow a single outgoing call at any given time. Before placing a call, make sure
+        // there doesn't already exist another outgoing call.
+        Call call = getFirstCallWithState(CallState.NEW, CallState.DIALING);
+
+        if (call != null) {
+            Log.i(this, "Canceling simultaneous outgoing call.");
+            return null;
+        }
+
+        // Create a call with original handle. The handle may be changed when the call is attached
+        // to a connection service, but in most cases will remain the same.
+        call = new Call(
+                mConnectionServiceRepository,
+                handle,
+                null /* gatewayInfo */,
+                null /* connectionManagerPhoneAccount */,
+                phoneAccountHandle,
+                false /* isIncoming */,
+                false /* isConference */);
+        call.setState(CallState.CONNECTING);
+
+        // TODO: Move this to be a part of addCall()
+        call.addListener(this);
+        addCall(call);
+
+        return call;
+    }
+
     /**
      * Attempts to issue/connect the specified call.
      *
@@ -290,14 +327,11 @@
      * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
      * @param videoState The desired video state for the outgoing call.
      */
-    void placeOutgoingCall(Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle accountHandle,
-            boolean speakerphoneOn, int videoState) {
-
-        // We only allow a single outgoing call at any given time. Before placing a call, make sure
-        // there doesn't already exist another outgoing call.
-        Call currentOutgoing = getFirstCallWithState(CallState.NEW, CallState.DIALING);
-        if (currentOutgoing != null) {
-            Log.i(this, "Canceling simultaneous outgoing call.");
+    void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
+            PhoneAccountHandle accountHandle, boolean speakerphoneOn, int videoState) {
+        if (call == null) {
+            // don't do anything if the call no longer exists
+            Log.i(this, "Canceling unknown call.");
             return;
         }
 
@@ -311,6 +345,9 @@
                     Log.pii(uriHandle), Log.pii(handle));
         }
 
+        //TODO: phone account is already set in {@link #startOutgoingCall}, refactor so this is
+        // not redundant.
+        //
         // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
         // as if a phoneAccount was not specified (does the default behavior instead).
         if (accountHandle != null) {
@@ -321,27 +358,19 @@
             }
         }
 
-        Call call = new Call(
-                mConnectionServiceRepository,
-                uriHandle,
-                gatewayInfo,
-                null /* connectionManagerPhoneAccount */,
-                accountHandle,
-                false /* isIncoming */,
-                false /* isConference */);
+        call.setHandle(uriHandle);
+        call.setGatewayInfo(gatewayInfo);
         call.setStartWithSpeakerphoneOn(speakerphoneOn);
         call.setVideoState(videoState);
 
-        // TODO: Move this to be a part of addCall()
-        call.addListener(this);
-        addCall(call);
-
         // This block of code will attempt to pre-determine a phone account
         final boolean emergencyCall = TelephonyUtil.shouldProcessAsEmergency(app, call.getHandle());
         if (emergencyCall) {
             // Emergency -- CreateConnectionProcessor will choose accounts automatically
             call.setTargetPhoneAccount(null);
         } else if (accountHandle != null) {
+            // NOTE: this is currently not an option because no feature currently exists to
+            // preset a phone account
             Log.d(this, "CALL with phone account: " + accountHandle);
             call.setTargetPhoneAccount(accountHandle);
         } else {
diff --git a/src/com/android/telecomm/NewOutgoingCallIntentBroadcaster.java b/src/com/android/telecomm/NewOutgoingCallIntentBroadcaster.java
index 410cbfd..71d8a96 100644
--- a/src/com/android/telecomm/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/telecomm/NewOutgoingCallIntentBroadcaster.java
@@ -69,6 +69,7 @@
     private static final String SCHEME_SIP = "sip";
 
     private final CallsManager mCallsManager;
+    private final Call mCall;
     private final Intent mIntent;
     /*
      * Whether or not the outgoing call intent originated from the default phone application. If
@@ -76,9 +77,10 @@
      */
     private final boolean mIsDefaultOrSystemPhoneApp;
 
-    NewOutgoingCallIntentBroadcaster(CallsManager callsManager, Intent intent,
+    NewOutgoingCallIntentBroadcaster(CallsManager callsManager, Call call, Intent intent,
             boolean isDefaultPhoneApp) {
         mCallsManager = callsManager;
+        mCall = call;
         mIntent = intent;
         mIsDefaultOrSystemPhoneApp = isDefaultPhoneApp;
     }
@@ -124,7 +126,7 @@
 
             GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
             PhoneAccountHandle accountHandle = getAccountHandleFromIntent(intent);
-            mCallsManager.placeOutgoingCall(resultHandleUri, gatewayInfo, accountHandle,
+            mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo, accountHandle,
                     mIntent.getBooleanExtra(TelecommManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
                             false),
                     mIntent.getIntExtra(TelecommManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
@@ -205,8 +207,8 @@
             int videoState = mIntent.getIntExtra(
                     TelecommManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                     VideoCallProfile.VideoState.AUDIO_ONLY);
-            mCallsManager.placeOutgoingCall(
-                    Uri.fromParts(scheme, handle, null), null, null, speakerphoneOn, videoState);
+            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, handle, null), null, null,
+                    speakerphoneOn, videoState);
 
             // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
             // so that third parties can still inspect (but not intercept) the outgoing call. When