Add support for remote incoming calls: impl

This CL changes how incoming calls are routed. We now
treat incoming calls the same as outgoing calls.
This allows a ConnectionService to attach to a incoming call
using a remote connection.

Change-Id: I5232d062ad3b559f4fe7c8224e7234b2c6bf8431
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index e6a3dbc..5f28df8 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -57,7 +57,7 @@
  *  from the time the call intent was received by Telecomm (vs. the time the call was
  *  connected etc).
  */
-final class Call implements OutgoingCallResponse {
+final class Call implements CreateConnectionResponse {
     /**
      * Listener for events on the call.
      */
@@ -147,14 +147,19 @@
                 }
             };
 
+    private final Runnable mDirectToVoicemailRunnable = new Runnable() {
+        @Override
+        public void run() {
+            processDirectToVoicemail();
+        }
+    };
+
     /** True if this is an incoming call. */
     private final boolean mIsIncoming;
 
     /**
-     * The time this call was created, typically also the time this call was added to the set
-     * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
-     * Beyond logging and such, may also be used for bookkeeping and specifically for marking
-     * certain call attempts as failed attempts.
+     * The time this call was created. Beyond logging and such, may also be used for bookkeeping
+     * and specifically for marking certain call attempts as failed attempts.
      */
     private final long mCreationTimeMillis = System.currentTimeMillis();
 
@@ -213,7 +218,7 @@
     /** Set of listeners on this call. */
     private Set<Listener> mListeners = Sets.newHashSet();
 
-    private OutgoingCallProcessor mOutgoingCallProcessor;
+    private CreateConnectionProcessor mCreateConnectionProcessor;
 
     /** Caller information retrieved from the latest contact query. */
     private CallerInfo mCallerInfo;
@@ -245,6 +250,7 @@
 
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
+    private final ConnectionServiceRepository mRepository;
 
     /**
      * Persists the specified parameters and initializes the new instance.
@@ -254,9 +260,15 @@
      * @param account Account information to use for the call.
      * @param isIncoming True if this is an incoming call.
      */
-    Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
-            boolean isIncoming, boolean isConference) {
+    Call(
+            ConnectionServiceRepository repository,
+            Uri handle,
+            GatewayInfo gatewayInfo,
+            PhoneAccount account,
+            boolean isIncoming,
+            boolean isConference) {
         mState = isConference ? CallState.ACTIVE : CallState.NEW;
+        mRepository = repository;
         setHandle(handle, CallPropertyPresentation.ALLOWED);
         mGatewayInfo = gatewayInfo;
         mPhoneAccount = account;
@@ -499,43 +511,7 @@
         }
     }
 
-    /**
-     * Starts the incoming call flow through the switchboard. When switchboard completes, it will
-     * invoke handle[Un]SuccessfulIncomingCall.
-     */
-    void startIncoming() {
-        Switchboard.getInstance().retrieveIncomingCall(this);
-    }
-
-    /**
-     * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
-     * from the contacts provider. The call is not yet exposed to the user at this point and
-     * the result of the query will determine if the call is rejected or passed through to the
-     * in-call UI.
-     */
-    void handleVerifiedIncoming(ConnectionRequest request) {
-        mPhoneAccount = request.getAccount();
-
-        // We do not handle incoming calls immediately when they are verified by the connection
-        // service. We allow the caller-info-query code to execute first so that we can read the
-        // direct-to-voicemail property before deciding if we want to show the incoming call to the
-        // user or if we want to reject the call.
-        mDirectToVoicemailQueryPending = true;
-
-        // Setting the handle triggers the caller info lookup code.
-        setHandle(request.getHandle(), CallPropertyPresentation.ALLOWED);
-
-        // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
-        // showing the user the incoming call screen.
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                processDirectToVoicemail();
-            }
-        }, Timeouts.getDirectToVoicemailMillis());
-    }
-
-    void processDirectToVoicemail() {
+    private void processDirectToVoicemail() {
         if (mDirectToVoicemailQueryPending) {
             if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
                 Log.i(this, "Directing call to voicemail: %s.", this);
@@ -557,60 +533,81 @@
         }
     }
 
-    void handleFailedIncoming() {
-        clearConnectionService();
-
-        // TODO: Needs more specific disconnect error for this case.
-        setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
-        setState(CallState.DISCONNECTED);
-
-        // TODO(santoscordon): Replace this with state transitions related to "connecting".
-        for (Listener l : mListeners) {
-            l.onFailedIncomingCall(this);
-        }
-    }
-
     /**
-     * Starts the outgoing call sequence.  Upon completion, there should exist an active connection
-     * through a connection service (or the call will have failed).
+     * Starts the create connection sequence. Upon completion, there should exist an active
+     * connection through a connection service (or the call will have failed).
      */
-    void startOutgoing() {
-        Preconditions.checkState(mOutgoingCallProcessor == null);
-
-        mOutgoingCallProcessor = new OutgoingCallProcessor(
-                this, Switchboard.getInstance().getConnectionServiceRepository(), this);
-        mOutgoingCallProcessor.process();
+    void startCreateConnection() {
+        Preconditions.checkState(mCreateConnectionProcessor == null);
+        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
+        mCreateConnectionProcessor.process();
     }
 
     @Override
-    public void onOutgoingCallSuccess() {
-        // TODO(santoscordon): Replace this with state transitions related to "connecting".
-        for (Listener l : mListeners) {
-            l.onSuccessfulOutgoingCall(this);
+    public void handleCreateConnectionSuccessful(ConnectionRequest request) {
+        mCreateConnectionProcessor = null;
+        mPhoneAccount = request.getAccount();
+
+        if (mIsIncoming) {
+            // We do not handle incoming calls immediately when they are verified by the connection
+            // service. We allow the caller-info-query code to execute first so that we can read the
+            // direct-to-voicemail property before deciding if we want to show the incoming call to
+            // the user or if we want to reject the call.
+            mDirectToVoicemailQueryPending = true;
+
+            // Setting the handle triggers the caller info lookup code.
+            setHandle(request.getHandle(), request.getHandlePresentation());
+
+            // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
+            // showing the user the incoming call screen.
+            mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
+        } else {
+            for (Listener l : mListeners) {
+                l.onSuccessfulOutgoingCall(this);
+            }
         }
-        mOutgoingCallProcessor = null;
     }
 
     @Override
-    public void onOutgoingCallFailure(int code, String msg) {
-        // TODO(santoscordon): Replace this with state transitions related to "connecting".
-        for (Listener l : mListeners) {
-            l.onFailedOutgoingCall(this, code, msg);
-        }
+    public void handleCreateConnectionFailed(int code, String msg) {
+        mCreateConnectionProcessor = null;
+        if (mIsIncoming) {
+            clearConnectionService();
+            setDisconnectCause(code, null);
+            setState(CallState.DISCONNECTED);
 
-        clearConnectionService();
-        mOutgoingCallProcessor = null;
+            Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onFailedIncomingCall(this);
+            }
+        } else {
+            Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onFailedOutgoingCall(this, code, msg);
+            }
+            clearConnectionService();
+        }
     }
 
     @Override
-    public void onOutgoingCallCancel() {
-        // TODO(santoscordon): Replace this with state transitions related to "connecting".
-        for (Listener l : mListeners) {
-            l.onCancelledOutgoingCall(this);
-        }
+    public void handleCreateConnectionCancelled() {
+        mCreateConnectionProcessor = null;
+        if (mIsIncoming) {
+            clearConnectionService();
+            setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
+            setState(CallState.DISCONNECTED);
 
-        clearConnectionService();
-        mOutgoingCallProcessor = null;
+            Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onFailedIncomingCall(this);
+            }
+        } else {
+            Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onCancelledOutgoingCall(this);
+            }
+            clearConnectionService();
+        }
     }
 
     /**
@@ -656,8 +653,8 @@
     }
 
     void abort() {
-        if (mOutgoingCallProcessor != null) {
-            mOutgoingCallProcessor.abort();
+        if (mCreateConnectionProcessor != null) {
+            mCreateConnectionProcessor.abort();
         }
     }