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();
         }
     }
 
diff --git a/src/com/android/telecomm/CallActivity.java b/src/com/android/telecomm/CallActivity.java
index 0086c99..0f625c7 100644
--- a/src/com/android/telecomm/CallActivity.java
+++ b/src/com/android/telecomm/CallActivity.java
@@ -113,10 +113,13 @@
             return;
         }
 
-        Bundle clientExtras = Bundle.EMPTY;
+        Bundle clientExtras = null;
         if (intent.hasExtra(TelecommConstants.EXTRA_INCOMING_CALL_EXTRAS)) {
             clientExtras = intent.getBundleExtra(TelecommConstants.EXTRA_INCOMING_CALL_EXTRAS);
         }
+        if (clientExtras == null) {
+            clientExtras = Bundle.EMPTY;
+        }
 
         Log.d(this, "Processing incoming call from connection service [%s]",
                 phoneAccount.getComponentName());
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 2d5d5c0..5d07176 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -67,6 +67,8 @@
      */
     private final Set<Call> mCalls = new CopyOnWriteArraySet<Call>();
 
+    private final ConnectionServiceRepository mConnectionServiceRepository =
+            new ConnectionServiceRepository();
     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer = new DtmfLocalTonePlayer();
     private final InCallController mInCallController = new InCallController();
     private final CallAudioManager mCallAudioManager;
@@ -230,9 +232,7 @@
     }
 
     /**
-     * Starts the incoming call sequence by having switchboard gather more information about the
-     * specified call; using the specified connection service component name. Upon success,
-     * execution returns to {@link #onSuccessfulIncomingCall} to start the in-call UI.
+     * Starts the process to attach the call to a connection service.
      *
      * @param phoneAccount The phone account which contains the component name of the connection
      *                     serivce to use for this call.
@@ -240,16 +240,20 @@
      */
     void processIncomingCallIntent(PhoneAccount phoneAccount, Bundle extras) {
         Log.d(this, "processIncomingCallIntent");
-        // Create a call with no handle. Eventually, switchboard will update the call with
-        // additional information from the connection service, but for now we just need one to pass
-        // around.
+        // Create a call with no handle. The handle is eventually set when the call is attached
+        // to a connection service.
         Call call = new Call(
-                null, null, phoneAccount, true /* isIncoming */, false /* isConference */);
+                mConnectionServiceRepository,
+                null /* handle */,
+                null /* gatewayInfo */,
+                phoneAccount,
+                true /* isIncoming */,
+                false /* isConference */);
+
         call.setExtras(extras);
         // TODO(santoscordon): Move this to be a part of addCall()
         call.addListener(this);
-
-        call.startIncoming();
+        call.startCreateConnection();
     }
 
     /**
@@ -275,15 +279,19 @@
         }
 
         Call call = new Call(
-                uriHandle, gatewayInfo, account, false /* isIncoming */, false /* isConference */);
+                mConnectionServiceRepository,
+                uriHandle,
+                gatewayInfo,
+                account,
+                false /* isIncoming */,
+                false /* isConference */);
         call.setStartWithSpeakerphoneOn(speakerphoneOn);
         call.setVideoState(videoState);
 
         // TODO(santoscordon): Move this to be a part of addCall()
         call.addListener(this);
         addCall(call);
-
-        call.startOutgoing();
+        call.startCreateConnection();
     }
 
     /**
@@ -293,7 +301,12 @@
      */
     void conference(Call call) {
         Call conferenceCall = new Call(
-                null, null, null, false /* isIncoming */, true /* isConference */);
+                mConnectionServiceRepository,
+                null /* handle */,
+                null /* gatewayInfo */,
+                null /* phoneAccount */,
+                false /* isIncoming */,
+                true /* isConference */);
         conferenceCall.addListener(this);
         call.conferenceInto(conferenceCall);
     }
diff --git a/src/com/android/telecomm/ConnectionServiceRepository.java b/src/com/android/telecomm/ConnectionServiceRepository.java
index 7672e30..598bdd1 100644
--- a/src/com/android/telecomm/ConnectionServiceRepository.java
+++ b/src/com/android/telecomm/ConnectionServiceRepository.java
@@ -32,12 +32,10 @@
  */
 final class ConnectionServiceRepository
         implements ServiceBinder.Listener<ConnectionServiceWrapper> {
-    private final IncomingCallsManager mIncomingCallsManager;
     private final HashMap<ComponentName, ConnectionServiceWrapper> mServiceCache =
             new HashMap<ComponentName, ConnectionServiceWrapper>();
 
-    ConnectionServiceRepository(IncomingCallsManager incomingCallsManager) {
-        mIncomingCallsManager = incomingCallsManager;
+    ConnectionServiceRepository() {
     }
 
     Collection<ConnectionServiceWrapper> lookupServices() {
@@ -58,7 +56,7 @@
     ConnectionServiceWrapper getService(ComponentName componentName) {
         ConnectionServiceWrapper service = mServiceCache.get(componentName);
         if (service == null) {
-            service = new ConnectionServiceWrapper(componentName, mIncomingCallsManager, this);
+            service = new ConnectionServiceWrapper(componentName, this);
             service.addListener(this);
             mServiceCache.put(componentName, service);
         }
diff --git a/src/com/android/telecomm/ConnectionServiceWrapper.java b/src/com/android/telecomm/ConnectionServiceWrapper.java
index 1b0dcdf..870d61c 100644
--- a/src/com/android/telecomm/ConnectionServiceWrapper.java
+++ b/src/com/android/telecomm/ConnectionServiceWrapper.java
@@ -56,87 +56,67 @@
  * {@link IConnectionService}.
  */
 final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {
-    private static final int MSG_NOTIFY_INCOMING_CALL = 1;
-    private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
-    private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
-    private static final int MSG_CANCEL_OUTGOING_CALL = 4;
-    private static final int MSG_SET_ACTIVE = 5;
-    private static final int MSG_SET_RINGING = 6;
-    private static final int MSG_SET_DIALING = 7;
-    private static final int MSG_SET_DISCONNECTED = 8;
-    private static final int MSG_SET_ON_HOLD = 9;
-    private static final int MSG_SET_REQUESTING_RINGBACK = 10;
-    private static final int MSG_SET_CALL_CAPABILITIES = 11;
-    private static final int MSG_SET_IS_CONFERENCED = 12;
-    private static final int MSG_ADD_CONFERENCE_CALL = 13;
-    private static final int MSG_REMOVE_CALL = 14;
-    private static final int MSG_ON_POST_DIAL_WAIT = 15;
-    private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 16;
-    private static final int MSG_SET_CALL_VIDEO_PROVIDER = 17;
-    private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18;
-    private static final int MSG_SET_STATUS_HINTS = 19;
-    private static final int MSG_SET_HANDLE = 20;
-    private static final int MSG_SET_CALLER_DISPLAY_NAME = 21;
+    private static final int MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL = 1;
+    private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2;
+    private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 3;
+    private static final int MSG_SET_ACTIVE = 4;
+    private static final int MSG_SET_RINGING = 5;
+    private static final int MSG_SET_DIALING = 6;
+    private static final int MSG_SET_DISCONNECTED = 7;
+    private static final int MSG_SET_ON_HOLD = 8;
+    private static final int MSG_SET_REQUESTING_RINGBACK = 9;
+    private static final int MSG_SET_CALL_CAPABILITIES = 10;
+    private static final int MSG_SET_IS_CONFERENCED = 11;
+    private static final int MSG_ADD_CONFERENCE_CALL = 12;
+    private static final int MSG_REMOVE_CALL = 13;
+    private static final int MSG_ON_POST_DIAL_WAIT = 14;
+    private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15;
+    private static final int MSG_SET_CALL_VIDEO_PROVIDER = 16;
+    private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 17;
+    private static final int MSG_SET_STATUS_HINTS = 18;
+    private static final int MSG_SET_HANDLE = 19;
+    private static final int MSG_SET_CALLER_DISPLAY_NAME = 20;
 
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             Call call;
             switch (msg.what) {
-                case MSG_NOTIFY_INCOMING_CALL: {
+                case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: {
                     ConnectionRequest request = (ConnectionRequest) msg.obj;
-                    call = mCallIdMapper.getCall(request.getCallId());
-                    if (call != null && mPendingIncomingCalls.remove(call) &&
-                            call.isIncoming()) {
-                        mIncomingCallsManager.handleSuccessfulIncomingCall(call, request);
+                    if (mPendingResponses.containsKey(request.getCallId())) {
+                        mPendingResponses.remove(
+                                request.getCallId()).handleCreateConnectionSuccessful(request);
                     } else {
-                        // TODO(santoscordon): For this an the other commented logging, we need
-                        // to reenable it.  At the moment all ConnectionServiceAdapters receive
-                        // notification of changes to all calls, even calls which it may not own
-                        // (ala remote connections). We need to fix that and then uncomment the
-                        // logging calls here.
-                        //Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
-                        //        call, request.getId());
+                        //Log.w(this, "handleCreateConnectionSuccessful, unknown call: %s", callId);
                     }
                     break;
                 }
-                case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
-                    ConnectionRequest request = (ConnectionRequest) msg.obj;
-                    if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
-                        mPendingOutgoingCalls.remove(
-                                request.getCallId()).onOutgoingCallSuccess();
-                    } else {
-                        //Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
-                    }
-                    break;
-                }
-                case MSG_HANDLE_FAILED_OUTGOING_CALL: {
+                case MSG_HANDLE_CREATE_CONNECTION_FAILED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         ConnectionRequest request = (ConnectionRequest) args.arg1;
                         int statusCode = args.argi1;
                         String statusMsg = (String) args.arg2;
-                        // TODO(santoscordon): Do something with 'reason' or get rid of it.
-
-                        if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
-                            mPendingOutgoingCalls.remove(request.getCallId())
-                                    .onOutgoingCallFailure(statusCode, statusMsg);
+                        if (mPendingResponses.containsKey(request.getCallId())) {
+                            mPendingResponses.remove(request.getCallId())
+                                    .handleCreateConnectionFailed(statusCode, statusMsg);
                             mCallIdMapper.removeCall(request.getCallId());
                         } else {
-                            //Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
+                            //Log.w(this, "handleCreateConnectionFailed, unknown call: %s", callId);
                         }
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
-                case MSG_CANCEL_OUTGOING_CALL: {
+                case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: {
                     ConnectionRequest request = (ConnectionRequest) msg.obj;
-                    if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
-                        mPendingOutgoingCalls.remove(
-                                request.getCallId()).onOutgoingCallCancel();
+                    if (mPendingResponses.containsKey(request.getCallId())) {
+                        mPendingResponses.remove(
+                                request.getCallId()).handleCreateConnectionCancelled();
                     } else {
-                        //Log.w(this, "cancelOutgoingCall, unknown call: %s", callId);
+                        //Log.w(this, "handleCreateConnectionCancelled, unknown call: %s", callId);
                     }
                     break;
                 }
@@ -268,8 +248,7 @@
                     break;
                 }
                 case MSG_QUERY_REMOTE_CALL_SERVICES: {
-                    ConnectionServiceWrapper.this.queryRemoteConnectionServices(
-                            (RemoteServiceCallback) msg.obj);
+                    queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
                     break;
                 }
                 case MSG_SET_CALL_VIDEO_PROVIDER: {
@@ -335,38 +314,29 @@
 
     private final class Adapter extends IConnectionServiceAdapter.Stub {
         @Override
-        public void notifyIncomingCall(ConnectionRequest request) {
-            logIncoming("notifyIncomingCall %s", request);
+        public void handleCreateConnectionSuccessful(ConnectionRequest request) {
+            logIncoming("handleCreateConnectionSuccessful %s", request);
             mCallIdMapper.checkValidCallId(request.getCallId());
-            mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, request).sendToTarget();
+            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, request).sendToTarget();
         }
 
         @Override
-        public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
-            logIncoming("handleSuccessfulOutgoingCall %s", request);
-            mCallIdMapper.checkValidCallId(request.getCallId());
-            mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, request).sendToTarget();
-        }
-
-        @Override
-        public void handleFailedOutgoingCall(
-                ConnectionRequest request,
-                int errorCode,
-                String errorMsg) {
-            logIncoming("handleFailedOutgoingCall %s %d %s", request, errorCode, errorMsg);
+        public void handleCreateConnectionFailed(
+                ConnectionRequest request, int errorCode, String errorMsg) {
+            logIncoming("handleCreateConnectionFailed %s %d %s", request, errorCode, errorMsg);
             mCallIdMapper.checkValidCallId(request.getCallId());
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = request;
             args.argi1 = errorCode;
             args.arg2 = errorMsg;
-            mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
+            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget();
         }
 
         @Override
-        public void cancelOutgoingCall(ConnectionRequest request) {
-            logIncoming("cancelOutgoingCall %s", request);
+        public void handleCreateConnectionCancelled(ConnectionRequest request) {
+            logIncoming("handleCreateConnectionCancelled %s", request);
             mCallIdMapper.checkValidCallId(request.getCallId());
-            mHandler.obtainMessage(MSG_CANCEL_OUTGOING_CALL, request).sendToTarget();
+            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget();
         }
 
         @Override
@@ -517,11 +487,9 @@
 
     private final Adapter mAdapter = new Adapter();
     private final CallsManager mCallsManager = CallsManager.getInstance();
-    private final Set<Call> mPendingIncomingCalls = new HashSet<>();
     private final Set<Call> mPendingConferenceCalls = new HashSet<>();
     private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
-    private final IncomingCallsManager mIncomingCallsManager;
-    private final Map<String, OutgoingCallResponse> mPendingOutgoingCalls = new HashMap<>();
+    private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
 
     private Binder mBinder = new Binder();
     private IConnectionService mServiceInterface;
@@ -531,15 +499,11 @@
      * Creates a connection service.
      *
      * @param componentName The component name of the service with which to bind.
-     * @param incomingCallsManager Manages the incoming call initialization flow.
      * @param connectionServiceRepository Connection service repository.
      */
     ConnectionServiceWrapper(
-            ComponentName componentName,
-            IncomingCallsManager incomingCallsManager,
-            ConnectionServiceRepository connectionServiceRepository) {
+            ComponentName componentName, ConnectionServiceRepository connectionServiceRepository) {
         super(TelecommConstants.ACTION_CONNECTION_SERVICE, componentName);
-        mIncomingCallsManager = incomingCallsManager;
         mConnectionServiceRepository = connectionServiceRepository;
     }
 
@@ -555,16 +519,15 @@
     }
 
     /**
-     * Attempts to place the specified call, see {@link IConnectionService#call}. Returns the result
-     * asynchronously through the specified callback.
+     * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
      */
-    void call(final Call call, final OutgoingCallResponse callResponse) {
-        Log.d(this, "call(%s) via %s.", call, getComponentName());
+    void createConnection(final Call call, final CreateConnectionResponse response) {
+        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
                 String callId = mCallIdMapper.getCallId(call);
-                mPendingOutgoingCalls.put(callId, callResponse);
+                mPendingResponses.put(callId, response);
 
                 GatewayInfo gatewayInfo = call.getGatewayInfo();
                 Bundle extras = call.getExtras();
@@ -587,10 +550,10 @@
                         call.getVideoState());
 
                 try {
-                    mServiceInterface.call(request);
+                    mServiceInterface.createConnection(request, call.isIncoming());
                 } catch (RemoteException e) {
-                    Log.e(this, e, "Failure to call -- %s", getComponentName());
-                    mPendingOutgoingCalls.remove(callId).onOutgoingCallFailure(
+                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
+                    mPendingResponses.remove(callId).handleCreateConnectionFailed(
                             DisconnectCause.ERROR_UNSPECIFIED, e.toString());
                 }
             }
@@ -598,7 +561,7 @@
             @Override
             public void onFailure() {
                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
-                callResponse.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
+                response.handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
             }
         };
 
@@ -657,47 +620,6 @@
         }
     }
 
-    /**
-     * Starts retrieval of details for an incoming call. Details are returned through the connection
-     * service adapter using the specified call ID. Upon failure, the specified error callback is
-     * invoked. Can be invoked even when the connection service is unbound. See
-     * {@link IConnectionService#createIncomingCall}.
-     *
-     * @param call The call used for the incoming call.
-     * @param errorCallback The callback to invoke upon failure.
-     */
-    void createIncomingCall(final Call call, final Runnable errorCallback) {
-        Log.d(this, "createIncomingCall(%s) via %s.", call, getComponentName());
-        BindCallback callback = new BindCallback() {
-            @Override
-            public void onSuccess() {
-                if (isServiceValid("createIncomingCall")) {
-                    mPendingIncomingCalls.add(call);
-                    String callId = mCallIdMapper.getCallId(call);
-                    logOutgoing("createIncomingCall %s", callId);
-                    ConnectionRequest request = new ConnectionRequest(
-                            call.getPhoneAccount(),
-                            callId,
-                            call.getHandle(),
-                            call.getHandlePresentation(),
-                            call.getExtras(),
-                            call.getVideoState());
-                    try {
-                        mServiceInterface.createIncomingCall(request);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-
-            @Override
-            public void onFailure() {
-                errorCallback.run();
-            }
-        };
-
-        mBinder.bind(callback);
-    }
-
     /** @see ConnectionService#disconnect(String) */
     void disconnect(Call call) {
         if (isServiceValid("disconnect")) {
@@ -768,12 +690,9 @@
     }
 
     void removeCall(Call call) {
-        mPendingIncomingCalls.remove(call);
-
-        OutgoingCallResponse outgoingResultCallback =
-                mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
-        if (outgoingResultCallback != null) {
-            outgoingResultCallback.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
+        CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
+        if (response != null) {
+            response.handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
         }
 
         mCallIdMapper.removeCall(call);
@@ -865,27 +784,14 @@
      * Called when the associated connection service dies.
      */
     private void handleConnectionServiceDeath() {
-        if (!mPendingOutgoingCalls.isEmpty()) {
-            for (OutgoingCallResponse callback : mPendingOutgoingCalls.values()) {
-                callback.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
-            }
-            mPendingOutgoingCalls.clear();
-        }
-
-        if (!mPendingIncomingCalls.isEmpty()) {
-            // Iterate through a copy because the code inside the loop will modify the original
-            // list.
-            for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
-                Preconditions.checkState(call.isIncoming());
-                mIncomingCallsManager.handleFailedIncomingCall(call);
-            }
-
-            if (!mPendingIncomingCalls.isEmpty()) {
-                Log.wtf(this, "Pending calls did not get cleared.");
-                mPendingIncomingCalls.clear();
+        if (!mPendingResponses.isEmpty()) {
+            CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
+                    new CreateConnectionResponse[mPendingResponses.values().size()]);
+            mPendingResponses.clear();
+            for (int i = 0; i < responses.length; i++) {
+                responses[i].handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
             }
         }
-
         mCallIdMapper.clear();
     }
 
@@ -900,15 +806,16 @@
     private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
         final List<IBinder> connectionServices = new ArrayList<>();
         final List<ComponentName> components = new ArrayList<>();
+        final List<ConnectionServiceWrapper> servciesAttempted = new ArrayList<>();
         final Collection<ConnectionServiceWrapper> services =
                 mConnectionServiceRepository.lookupServices();
 
+        Log.v(this, "queryRemoteConnectionServices, services: " + services.size());
+
         for (ConnectionServiceWrapper cs : services) {
             if (cs != this) {
                 final ConnectionServiceWrapper currentConnectionService = cs;
                 cs.mBinder.bind(new BindCallback() {
-                    private int mRemainingResponses = services.size() - 1;
-
                     @Override
                     public void onSuccess() {
                         Log.d(this, "Adding ***** %s", currentConnectionService.getComponentName());
@@ -920,13 +827,12 @@
 
                     @Override
                     public void onFailure() {
-                        // add null so that we always add up to totalExpected even if
-                        // some of the connection services fail to bind.
                         maybeComplete();
                     }
 
                     private void maybeComplete() {
-                        if (--mRemainingResponses == 0) {
+                        servciesAttempted.add(currentConnectionService);
+                        if (servciesAttempted.size() == services.size() - 1) {
                             try {
                                 callback.onResult(components, connectionServices);
                             } catch (RemoteException ignored) {
diff --git a/src/com/android/telecomm/CreateConnectionProcessor.java b/src/com/android/telecomm/CreateConnectionProcessor.java
new file mode 100644
index 0000000..631d6fe
--- /dev/null
+++ b/src/com/android/telecomm/CreateConnectionProcessor.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.telephony.DisconnectCause;
+import android.telephony.PhoneNumberUtils;
+import android.telecomm.ConnectionRequest;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class creates connections to place new outgoing calls to attached to an existing incoming
+ * call. In either case, this class cycles through a set of connection services until:
+ *   - a connection service returns a newly created connection in which case the call is displayed
+ *     to the user
+ *   - a connection service cancels the process, in which case the call is aborted
+ */
+final class CreateConnectionProcessor {
+    private final Call mCall;
+    private final ConnectionServiceRepository mRepository;
+    private List<ComponentName> mServiceComponentNames;
+    private Iterator<ComponentName> mServiceComponentNameIterator;
+    private CreateConnectionResponse mResponse;
+    private int mLastErrorCode = DisconnectCause.ERROR_UNSPECIFIED;
+    private String mLastErrorMsg;
+
+    CreateConnectionProcessor(
+            Call call, ConnectionServiceRepository repository, CreateConnectionResponse response) {
+        mCall = call;
+        mRepository = repository;
+        mResponse = response;
+    }
+
+    void process() {
+        Log.v(this, "process");
+
+        mServiceComponentNames = new ArrayList<>();
+
+        // TODO(sail): Remove once there's a way to pick the service.
+        ArrayList<ComponentName> priorityComponents = new ArrayList<>();
+        priorityComponents.add(new ComponentName("com.android.phone",
+                "com.android.services.telephony.sip.SipConnectionService"));
+        priorityComponents.add(new ComponentName("com.google.android.talk",
+                "com.google.android.apps.babel.telephony.TeleConnectionService"));
+        priorityComponents.add(new ComponentName("com.android.telecomm.tests",
+                "com.android.telecomm.testapps.TestConnectionService"));
+
+        for (ConnectionServiceWrapper service : mRepository.lookupServices()) {
+            ComponentName serviceName = service.getComponentName();
+            if (priorityComponents.contains(serviceName)) {
+                Log.i(this, "Moving connection service %s to top of list", serviceName);
+                mServiceComponentNames .add(0, serviceName);
+            } else {
+                mServiceComponentNames.add(serviceName);
+            }
+        }
+
+        adjustComponentNamesForEmergency();
+        mServiceComponentNameIterator = mServiceComponentNames.iterator();
+        attemptNextConnectionService();
+    }
+
+    void abort() {
+        Log.v(this, "abort");
+
+        // Clear the response first to prevent attemptNextConnectionService from attempting any
+        // more services.
+        CreateConnectionResponse response = mResponse;
+        mResponse = null;
+
+        ConnectionServiceWrapper service = mCall.getConnectionService();
+        if (service != null) {
+            service.abort(mCall);
+            mCall.clearConnectionService();
+        }
+        if (response != null) {
+            response.handleCreateConnectionCancelled();
+        }
+    }
+
+    private void attemptNextConnectionService() {
+        Log.v(this, "attemptNextConnectionService");
+
+        if (mResponse != null && mServiceComponentNameIterator.hasNext()) {
+            ComponentName component = mServiceComponentNameIterator.next();
+            ConnectionServiceWrapper service = mRepository.getService(component);
+            if (service == null) {
+                attemptNextConnectionService();
+            } else {
+                mCall.setConnectionService(service);
+                Log.i(this, "Attempting to call from %s", service.getComponentName());
+                service.createConnection(mCall, new Response(service));
+            }
+        } else {
+            Log.v(this, "attemptNextConnectionService, no more services, failing");
+            if (mResponse != null) {
+                mResponse.handleCreateConnectionFailed(mLastErrorCode, mLastErrorMsg);
+                mResponse = null;
+                mCall.clearConnectionService();
+            }
+        }
+    }
+
+    // If we are possibly attempting to call a local emergency number, ensure that the
+    // plain PSTN connection service, if it exists, is attempted first.
+    private void adjustComponentNamesForEmergency()  {
+        if (shouldProcessAsEmergency(mCall.getHandle())) {
+            for (int i = 0; i < mServiceComponentNames.size(); i++) {
+                if (TelephonyUtil.isPstnComponentName(mServiceComponentNames.get(i))) {
+                    mServiceComponentNames.add(0, mServiceComponentNames.remove(i));
+                    return;
+                }
+            }
+        }
+    }
+
+    private boolean shouldProcessAsEmergency(Uri handle) {
+        return handle != null && PhoneNumberUtils.isPotentialLocalEmergencyNumber(
+                TelecommApp.getInstance(), handle.getSchemeSpecificPart());
+    }
+
+    private class Response implements CreateConnectionResponse {
+        private final ConnectionServiceWrapper mService;
+
+        Response(ConnectionServiceWrapper service) {
+            mService = service;
+        }
+
+        @Override
+        public void handleCreateConnectionSuccessful(ConnectionRequest request) {
+            if (mResponse == null) {
+                mService.abort(mCall);
+            } else {
+                mResponse.handleCreateConnectionSuccessful(request);
+                mResponse= null;
+            }
+        }
+
+        @Override
+        public void handleCreateConnectionFailed(int code, String msg) {
+            mLastErrorCode = code;
+            mLastErrorMsg = msg;
+            attemptNextConnectionService();
+        }
+
+        @Override
+        public void handleCreateConnectionCancelled() {
+            if (mResponse != null) {
+                mResponse.handleCreateConnectionCancelled();
+                mResponse = null;
+            }
+        }
+    }
+}
diff --git a/src/com/android/telecomm/OutgoingCallResponse.java b/src/com/android/telecomm/CreateConnectionResponse.java
similarity index 67%
rename from src/com/android/telecomm/OutgoingCallResponse.java
rename to src/com/android/telecomm/CreateConnectionResponse.java
index 3d5c393..caf2c1a 100644
--- a/src/com/android/telecomm/OutgoingCallResponse.java
+++ b/src/com/android/telecomm/CreateConnectionResponse.java
@@ -16,11 +16,13 @@
 
 package com.android.telecomm;
 
+import android.telecomm.ConnectionRequest;
+
 /**
- * A callback for providing the result of placing an outgoing call.
+ * A callback for providing the result of creating a connection.
  */
-interface OutgoingCallResponse {
-    void onOutgoingCallSuccess();
-    void onOutgoingCallFailure(int code, String msg);
-    void onOutgoingCallCancel();
+interface CreateConnectionResponse {
+    void handleCreateConnectionSuccessful(ConnectionRequest request);
+    void handleCreateConnectionFailed(int code, String msg);
+    void handleCreateConnectionCancelled();
 }
diff --git a/src/com/android/telecomm/IncomingCallsManager.java b/src/com/android/telecomm/IncomingCallsManager.java
deleted file mode 100644
index 5fa80f5..0000000
--- a/src/com/android/telecomm/IncomingCallsManager.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.telecomm;
-
-import android.os.Bundle;
-import android.telecomm.ConnectionService;
-import android.telecomm.ConnectionRequest;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import java.util.Set;
-
-/**
- * Used to retrieve details about an incoming call. This is invoked after an incoming call intent.
- */
-final class IncomingCallsManager {
-
-    private final Set<Call> mPendingIncomingCalls = Sets.newLinkedHashSet();
-
-    /**
-     * Retrieves details of an incoming call through its associated connection service.
-     *
-     * @param call The call object.
-     */
-    void retrieveIncomingCall(final Call call) {
-        ThreadUtil.checkOnMainThread();
-        Log.d(this, "retrieveIncomingCall");
-
-        // Just to be safe, lets make sure we're not already processing this call.
-        Preconditions.checkState(!mPendingIncomingCalls.contains(call));
-
-        mPendingIncomingCalls.add(call);
-
-        Runnable errorCallback = new Runnable() {
-            @Override public void run() {
-                handleFailedIncomingCall(call);
-            }
-        };
-
-        call.getConnectionService().createIncomingCall(call, errorCallback);
-    }
-
-    /**
-     * Notifies the incoming call of success after removing it from the pending
-     * list.
-     *
-     * @param request The details of the call.
-     */
-    void handleSuccessfulIncomingCall(Call call, ConnectionRequest request) {
-        ThreadUtil.checkOnMainThread();
-
-        if (mPendingIncomingCalls.contains(call)) {
-            Log.d(this, "Incoming call %s found.", call);
-            mPendingIncomingCalls.remove(call);
-            call.handleVerifiedIncoming(request);
-        }
-    }
-
-    /**
-     * Notifies  incoming call of failure after removing it from the pending list.
-     */
-    void handleFailedIncomingCall(Call call) {
-        ThreadUtil.checkOnMainThread();
-
-        if (mPendingIncomingCalls.contains(call)) {
-            Log.i(this, "Failed to get details for incoming call %s", call);
-            mPendingIncomingCalls.remove(call);
-            // The call was found still waiting for details. Consider it failed.
-            call.handleFailedIncoming();
-        }
-    }
-}
diff --git a/src/com/android/telecomm/MissedCallNotifier.java b/src/com/android/telecomm/MissedCallNotifier.java
index bb2f092..ed79f5a 100644
--- a/src/com/android/telecomm/MissedCallNotifier.java
+++ b/src/com/android/telecomm/MissedCallNotifier.java
@@ -279,7 +279,7 @@
                         }
 
                         // Convert the data to a call object
-                        Call call = new Call(null, null, null, true, false);
+                        Call call = new Call(null, null, null, null, true, false);
                         call.setDisconnectCause(DisconnectCause.INCOMING_MISSED, "");
                         call.setState(CallState.DISCONNECTED);
 
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
deleted file mode 100644
index c24a83b..0000000
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.telecomm;
-
-import android.content.ComponentName;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.DisconnectCause;
-import android.telephony.PhoneNumberUtils;
-
-import com.google.android.collect.Sets;
-import com.google.common.collect.Maps;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Utility class to place a call using the specified set of connection services. Each of the
- * connection services is then attempted until either the outgoing call is placed, the attempted
- * call is aborted, or the list is exhausted -- whichever occurs first.
- *
- * Except for the abort case, all other scenarios should terminate with the call notified
- * of the result.
- */
-final class OutgoingCallProcessor {
-
-    /**
-     * The outgoing call this processor is tasked with placing.
-     */
-    private final Call mCall;
-
-    /**
-     * The set of attempted connection services, used to ensure services are attempted at most once
-     * per outgoing-call attempt.
-     */
-    private final Set<ConnectionServiceWrapper> mAttemptedServices = Sets.newHashSet();
-
-    private final ConnectionServiceRepository mConnectionServiceRepository;
-
-    /**
-     * The duplicate-free list of currently-available connection service component names.
-     */
-    private List<ComponentName> mServiceComponentNames;
-
-    /**
-     * The iterator over the currently-selected ordered list of connection service component names.
-     */
-    private Iterator<ComponentName> mServiceComponentNameIterator;
-
-    private OutgoingCallResponse mResultCallback;
-
-    private boolean mIsAborted = false;
-
-    private int mLastErrorCode = 0;
-
-    private String mLastErrorMsg = null;
-
-    /**
-     * Persists the specified parameters and iterates through the prioritized list of call
-     * services. Stops once a matching connection service is found. Calls with no matching
-     * connection service will eventually be killed by the cleanup/monitor switchboard handler.
-     *
-     * @param call The call to place.
-     * @param resultCallback The callback on which to return the result.
-     */
-    OutgoingCallProcessor(
-            Call call,
-            ConnectionServiceRepository connectionServiceRepository,
-            OutgoingCallResponse resultCallback) {
-
-        ThreadUtil.checkOnMainThread();
-
-        mCall = call;
-        mResultCallback = resultCallback;
-        mConnectionServiceRepository = connectionServiceRepository;
-    }
-
-    /**
-     * Initiates the attempt to place the call.  No-op beyond the first invocation.
-     */
-    void process() {
-        Log.v(this, "process, mIsAborted: %b", mIsAborted);
-        if (!mIsAborted) {
-            setConnectionServices(mConnectionServiceRepository.lookupServices());
-        }
-    }
-
-    /**
-     * Aborts the attempt to place the relevant call.  Intended to be invoked by
-     * switchboard through the outgoing-calls manager.
-     */
-    void abort() {
-        Log.v(this, "abort");
-        ThreadUtil.checkOnMainThread();
-        if (!mIsAborted && mResultCallback != null) {
-            mIsAborted = true;
-
-            // On an abort, we need to check if we already told the connection service to place the
-            // call. If so, we need to tell it to abort.
-            // TODO(santoscordon): The conneciton service is saved with the call and so we have to
-            // query the call to get it, which is a bit backwards.  Ideally, the connection service
-            // would be saved inside this class until the whole thing is complete and then set on
-            // the call.
-            ConnectionServiceWrapper service = mCall.getConnectionService();
-            if (service != null) {
-                service.abort(mCall);
-            }
-
-            // We consider a deliberate abort to be a "normal" disconnect, not
-            // requiring special reporting.
-            sendResult(false, DisconnectCause.LOCAL, null);
-        }
-    }
-
-    boolean isAborted() {
-        return mIsAborted;
-    }
-
-    /**
-     * Completes the outgoing call sequence by setting the connection service on the call object.
-     * This is invoked when the connection service adapter receives positive confirmation that the
-     * connection service placed the call.
-     */
-    void handleSuccessfulCallAttempt(ConnectionServiceWrapper service) {
-        Log.v(this, "handleSuccessfulCallAttempt");
-        ThreadUtil.checkOnMainThread();
-
-        if (mIsAborted) {
-            service.abort(mCall);
-            return;
-        }
-
-        sendResult(true, DisconnectCause.NOT_DISCONNECTED, null);
-    }
-
-    /**
-     * Attempts the next connection service if the specified connection service is the one currently
-    * being attempted.
-     *
-     * @param errorCode The reason for the failure, one of {@link DisconnectCause}.
-     * @param errorMsg Optional text reason for the failure.
-     */
-    void handleFailedCallAttempt(int errorCode, String errorMsg) {
-        Log.v(this, "handleFailedCallAttempt %s %s", DisconnectCause.toString(errorCode), errorMsg);
-        // Store latest error code and message. If this is our last available attempt at placing
-        // a call, these error details will be considered "the" cause of the failure.
-        mLastErrorCode = errorCode;
-        mLastErrorMsg = errorMsg;
-        if (!mIsAborted) {
-            ThreadUtil.checkOnMainThread();
-            attemptNextConnectionService();
-        }
-    }
-
-    /**
-     * Sets the connection services to attempt for this outgoing call.
-     *
-     * @param services The connection services.
-     */
-    private void setConnectionServices(Collection<ConnectionServiceWrapper> services) {
-        mServiceComponentNames = new ArrayList<>();
-
-        // TODO(sail): Remove once there's a way to pick the service.
-        ArrayList<ComponentName> priorityComponents = new ArrayList<>();
-        priorityComponents.add(new ComponentName("com.android.phone",
-                "com.android.services.telephony.sip.SipConnectionService"));
-        priorityComponents.add(new ComponentName("com.google.android.talk",
-                "com.google.android.apps.babel.telephony.TeleConnectionService"));
-        priorityComponents.add(new ComponentName("com.android.telecomm.tests",
-                "com.android.telecomm.testapps.TestConnectionService"));
-
-        for (ConnectionServiceWrapper service : services) {
-            ComponentName serviceName = service.getComponentName();
-            if (priorityComponents.contains(serviceName)) {
-                Log.i(this, "Moving connection service %s to top of list", serviceName);
-                mServiceComponentNames .add(0, serviceName);
-            } else {
-                mServiceComponentNames.add(serviceName);
-            }
-        }
-
-        adjustComponentNamesForEmergency();
-
-        mServiceComponentNameIterator = mServiceComponentNames.iterator();
-        attemptNextConnectionService();
-    }
-
-    /**
-     * Attempts to place the call using the connection service specified by the next connection
-     * service component name of mServiceComponentNameIterator.
-     */
-    private void attemptNextConnectionService() {
-        Log.v(this, "attemptNextConnectionService, mIsAborted: %b", mIsAborted);
-        if (mIsAborted) {
-            return;
-        }
-
-        if (mServiceComponentNameIterator != null && mServiceComponentNameIterator.hasNext()) {
-            ComponentName component = mServiceComponentNameIterator.next();
-            final ConnectionServiceWrapper service =
-                    mConnectionServiceRepository.getService(component);
-
-            if (service == null || mAttemptedServices.contains(service)) {
-                // The next connection service is either null or has already been attempted, fast
-                // forward to the next.
-                attemptNextConnectionService();
-            } else {
-                mAttemptedServices.add(service);
-                mCall.setConnectionService(service);
-
-                // Increment the associated call count until we get a result. This prevents the call
-                // service from unbinding while we are using it.
-                service.incrementAssociatedCallCount();
-
-                Log.i(this, "Attempting to call from %s", service.getComponentName());
-                service.call(mCall, new OutgoingCallResponse() {
-                    @Override
-                    public void onOutgoingCallSuccess() {
-                        handleSuccessfulCallAttempt(service);
-                        service.decrementAssociatedCallCount();
-                    }
-
-                    @Override
-                    public void onOutgoingCallFailure(int code, String msg) {
-                        handleFailedCallAttempt(code, msg);
-                        service.decrementAssociatedCallCount();
-                    }
-
-                    @Override
-                    public void onOutgoingCallCancel() {
-                        abort();
-                        service.decrementAssociatedCallCount();
-                    }
-                });
-            }
-        } else {
-            Log.v(this, "attemptNextConnectionService, no more services, failing");
-            mServiceComponentNameIterator = null;
-            mCall.clearConnectionService();
-            sendResult(false, mLastErrorCode, mLastErrorMsg);
-        }
-    }
-
-    private void sendResult(boolean wasCallPlaced, int errorCode, String errorMsg) {
-        if (mResultCallback != null) {
-            if (mIsAborted) {
-                mResultCallback.onOutgoingCallCancel();
-            } else if (wasCallPlaced) {
-                mResultCallback.onOutgoingCallSuccess();
-            } else {
-                mResultCallback.onOutgoingCallFailure(errorCode, errorMsg);
-            }
-            mResultCallback = null;
-        } else {
-            Log.wtf(this, "Attempting to return outgoing result twice for call %s", mCall);
-        }
-    }
-
-    // If we are possibly attempting to call a local emergency number, ensure that the
-    // plain PSTN connection service, if it exists, is attempted first.
-    private void adjustComponentNamesForEmergency()  {
-        for (int i = 0; i < mServiceComponentNames.size(); i++) {
-            if (shouldProcessAsEmergency(mCall.getHandle())) {
-                if (TelephonyUtil.isPstnComponentName(mServiceComponentNames.get(i))) {
-                    mServiceComponentNames.add(0, mServiceComponentNames.remove(i));
-                    return;
-                }
-            }
-        }
-    }
-
-    private boolean shouldProcessAsEmergency(Uri handle) {
-        return PhoneNumberUtils.isPotentialLocalEmergencyNumber(
-                TelecommApp.getInstance(), handle.getSchemeSpecificPart());
-    }
-}
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
deleted file mode 100644
index 2dfcfea..0000000
--- a/src/com/android/telecomm/Switchboard.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.telecomm;
-
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.telecomm.TelecommConstants;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Switchboard is responsible for gathering the {@link ConnectionServiceWrapper}s through
- *       which to place outgoing calls
- */
-final class Switchboard {
-    private final static Switchboard sInstance = new Switchboard();
-
-    /** Used to retrieve incoming call details. */
-    private final IncomingCallsManager mIncomingCallsManager;
-
-    private final ConnectionServiceRepository mConnectionServiceRepository;
-
-    /** Singleton accessor. */
-    static Switchboard getInstance() {
-        return sInstance;
-    }
-
-    /**
-     * Persists the specified parameters and initializes Switchboard.
-     */
-    private Switchboard() {
-        ThreadUtil.checkOnMainThread();
-
-        mIncomingCallsManager = new IncomingCallsManager();
-        mConnectionServiceRepository =
-                new ConnectionServiceRepository(mIncomingCallsManager);
-    }
-
-    ConnectionServiceRepository getConnectionServiceRepository() {
-        return mConnectionServiceRepository;
-    }
-
-    /**
-     * Retrieves details about the incoming call through the incoming call manager.
-     *
-     * @param call The call object.
-     */
-    void retrieveIncomingCall(Call call) {
-        Log.d(this, "retrieveIncomingCall");
-        ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
-                call.getPhoneAccount().getComponentName());
-        call.setConnectionService(service);
-        mIncomingCallsManager.retrieveIncomingCall(call);
-    }
-}