OutgoingCalls(1/6) - Filling in lifecycle of outgoing call.

This CL adds the following:
1. CallServiceAdapter implementation
2. Setting call-service adapters when the finder receives call services.
3. CallServiceAdapter notifies OutgoingCallManager/OCP about a successful
    outgoing call
4. Switchboard notifies CallsManager of the successful outgoing call
5. CallsManager adds the call to it's call repository.

What remains after this CL:
CallsManager sending the call to the InCall app and listening to
commands from in-call.

depends on: I086443e3f17ae0233b7b0fd5629119e989d06a40

Change-Id: I86f3e7ab02a47485eb2a5fb3557819418f3c4adf
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 587214a..9f9a692 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -18,6 +18,7 @@
 
 import android.telecomm.CallInfo;
 import android.telecomm.CallState;
+import android.telecomm.ICallService;
 
 import java.util.Date;
 
@@ -53,6 +54,12 @@
     private final Date mCreationTime;
 
     /**
+     * The call service which is currently connecting this call, null as long as the call is not
+     * connected.
+     */
+    private ICallService mCallService;
+
+    /**
      * Read-only and parcelable version of this call.
      */
     private CallInfo mCallInfo;
@@ -107,6 +114,21 @@
         return new Date().getTime() - mCreationTime.getTime();
     }
 
+    ICallService getCallService() {
+        return mCallService;
+    }
+
+    void setCallService(ICallService callService) {
+        mCallService = callService;
+    }
+
+    /**
+     * Clears the associated call service.
+     */
+    void clearCallService() {
+        setCallService(null);
+    }
+
     /**
      * @return An object containing read-only information about this call.
      */
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
index 43aab4a..bfbdebd 100644
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ b/src/com/android/telecomm/CallServiceAdapter.java
@@ -1,16 +1,140 @@
+/*
+ * Copyright (C) 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.Handler;
+import android.os.Looper;
+import android.telecomm.CallInfo;
+import android.telecomm.ICallServiceAdapter;
+
+import com.google.common.base.Strings;
+
 /**
- * Only exposes the CallsManager APIs that CallService implementations should
- * have access to.
+ * Used by call services in order to update state and control calls while the call service is bound
+ * to Telecomm. Each call service is given its own instance for the lifetime of the binding between
+ * Telecomm and the call service.
+ * TODO(santoscordon): Whenever we get any method invocations from the call service, we need to
+ * check that the invocation is expected from that call service.
+ * TODO(santoscordon): Move away from Runnable objects and into messages so that we create fewer
+ * objects per IPC method call.
  */
-public class CallServiceAdapter {
-  private CallsManager callsManager;
+public final class CallServiceAdapter extends ICallServiceAdapter.Stub {
 
-  private CallService callService;
+    private final CallsManager mCallsManager;
 
-  /** Package private */
-  CallServiceAdapter(CallsManager callsManager) {
-    this.callsManager = callsManager;
-  }
+    private final OutgoingCallsManager mOutgoingCallsManager;
+
+    /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    /**
+     * Persists the specified parameters.
+     */
+    CallServiceAdapter(OutgoingCallsManager outgoingCallsManager) {
+        mCallsManager = CallsManager.getInstance();
+        mOutgoingCallsManager = outgoingCallsManager;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void getNextCallId() {
+        // TODO(santoscordon): needs response object.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setCompatibleWith(String callId, boolean isCompatible) {
+        // TODO(santoscordon): fill in.
+    }
+
+    /**
+     * TODO(santoscordon): Rename to handleIncomingCall.
+     * {@inheritDoc}
+     */
+    @Override public void newIncomingCall(CallInfo callInfo) {
+        // TODO(santoscordon): fill in.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void handleSuccessfulOutgoingCall(final String callId) {
+        checkValidCallId(callId);
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                mOutgoingCallsManager.handleSuccessfulCallAttempt(callId);
+            }
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Override public void handleFailedOutgoingCall(final String callId, final String reason) {
+        checkValidCallId(callId);
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                mOutgoingCallsManager.handleFailedCallAttempt(callId, reason);
+            }
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setActive(final String callId) {
+        checkValidCallId(callId);
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                mCallsManager.markCallAsActive(callId);
+            }
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setRinging(final String callId) {
+        checkValidCallId(callId);
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                mCallsManager.markCallAsRinging(callId);
+            }
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setDialing(final String callId) {
+        checkValidCallId(callId);
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                mCallsManager.markCallAsDialing(callId);
+            }
+        });
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setDisconnected(final String callId) {
+        checkValidCallId(callId);
+        mHandler.post(new Runnable() {
+            @Override public void run() {
+                mCallsManager.markCallAsDisconnected(callId);
+            }
+        });
+    }
+
+    /**
+     * Throws an IllegalArgumentException if the specified call ID is invalid.
+     *
+     * @param callId The call ID to check.
+     */
+    private void checkValidCallId(String callId) {
+        if (Strings.isNullOrEmpty(callId)) {
+            throw new IllegalArgumentException();
+        }
+    }
 }
diff --git a/src/com/android/telecomm/CallServiceFinder.java b/src/com/android/telecomm/CallServiceFinder.java
index 44ce75e..7fc187b 100644
--- a/src/com/android/telecomm/CallServiceFinder.java
+++ b/src/com/android/telecomm/CallServiceFinder.java
@@ -64,6 +64,8 @@
 
     private final Switchboard mSwitchboard;
 
+    private final OutgoingCallsManager mOutgoingCallsManager;
+
     /**
      * Determines whether or not a lookup cycle is already running.
      */
@@ -118,10 +120,12 @@
     /**
      * Persists the specified parameters.
      *
-     * @param switchboard The switchboard for this finer to work against.
+     * @param switchboard The switchboard for this finder to work against.
+     * @param outgoingCallsManager Manager in charge of placing outgoing calls.
      */
-    CallServiceFinder(Switchboard switchboard) {
+    CallServiceFinder(Switchboard switchboard, OutgoingCallsManager outgoingCallsManager) {
         mSwitchboard = switchboard;
+        mOutgoingCallsManager = outgoingCallsManager;
     }
 
     /**
@@ -288,11 +292,20 @@
         ThreadUtil.checkOnMainThread();
 
         // TODO(santoscordon): When saving the call services into this class, also add code to
-        // unregister (remove) the call services upon disconnect. Potentially use RemoteCallbackList.
+        // unregister (remove) the call services upon disconnect. Potentially use
+        // RemoteCallbackList.
 
         if (mUnregisteredProviders.remove(providerName)) {
             mProviderRegistry.add(provider);
-            mCallServiceRegistry.addAll(callServices);
+            for (ICallService callService : callServices) {
+                try {
+                    CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
+                    callService.setCallServiceAdapter(adapter);
+                    mCallServiceRegistry.add(callService);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to set call-service adapter.");
+                }
+            }
 
             // TODO(gilad): Introduce a map to retain the association between call services
             // and the corresponding provider such that mCallServiceRegistry can be updated
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 0712f03..332eb0a 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -17,11 +17,18 @@
 package com.android.telecomm;
 
 import android.content.Context;
+import android.telecomm.CallState;
+import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.telecomm.exceptions.RestrictedCallException;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Singleton.
@@ -31,6 +38,7 @@
  * beyond the com.android.telecomm package boundary.
  */
 public final class CallsManager {
+    private static final String TAG = CallsManager.class.getSimpleName();
 
     private static final CallsManager INSTANCE = new CallsManager();
 
@@ -40,6 +48,14 @@
     private final InCallController mInCallController;
 
     /**
+     * The main call repository. Keeps an instance of all live calls keyed by call ID. New incoming
+     * and outgoing calls are added to the map and removed when the calls move to the disconnected
+     * state.
+     * TODO(santoscordon): Add new CallId class and use it in place of String.
+     */
+    private final Map<String, Call> mCalls = Maps.newHashMap();
+
+    /**
      * May be unnecessary per off-line discussions (between santoscordon and gilad) since the set
      * of CallsManager APIs that need to be exposed to the dialer (or any application firing call
      * intents) may be empty.
@@ -65,7 +81,7 @@
      */
     private CallsManager() {
         mSwitchboard = new Switchboard();
-        mInCallController = new InCallController(this);
+        mInCallController = new InCallController();
     }
 
     /**
@@ -90,6 +106,21 @@
     }
 
     /**
+     * Adds a new outgoing call to the list of live calls and notifies the in-call app.
+     *
+     * @param call The new outgoing call.
+     */
+    void handleSuccessfulOutgoingCall(Call call) {
+        // OutgoingCallProcessor sets the call state to DIALING when it receives confirmation of the
+        // placed call from the call service so there is no need to set it here. Instead, check that
+        // the state is appropriate.
+        Preconditions.checkState(call.getState() == CallState.DIALING);
+
+        addCall(call);
+        // TODO(santoscordon): Notify in-call UI.
+    }
+
+    /**
      * Instructs Telecomm to answer the specified call. Intended to be invoked by the in-call
      * app through {@link InCallAdapter} after Telecomm notifies it of an incoming call followed by
      * the user opting to answer said call.
@@ -121,4 +152,57 @@
     void disconnectCall(String callId) {
         // TODO(santoscordon): fill in and check that the call is in the active state.
     }
+
+    void markCallAsRinging(String callId) {
+        setCallState(callId, CallState.RINGING);
+    }
+
+    void markCallAsDialing(String callId) {
+        setCallState(callId, CallState.DIALING);
+    }
+
+    void markCallAsActive(String callId) {
+        setCallState(callId, CallState.ACTIVE);
+    }
+
+    void markCallAsDisconnected(String callId) {
+        setCallState(callId, CallState.DISCONNECTED);
+        mCalls.remove(callId);
+    }
+
+    /**
+     * Sets the specified state on the specified call.
+     *
+     * @param callId The ID of the call to update.
+     * @param state The new state of the call.
+     */
+    private void setCallState(String callId, CallState state) {
+        Preconditions.checkState(!Strings.isNullOrEmpty(callId));
+        Preconditions.checkNotNull(state);
+
+        Call call = mCalls.get(callId);
+        if (call == null) {
+            Log.e(TAG, "Call " + callId + " was not found while attempting to upda the state to " +
+                    state + ".");
+        } else {
+            // Unfortunately, in the telephony world, the radio is king. So if the call notifies us
+            // that the call is in a particular state, we allow it even if it doesn't make sense
+            // (e.g., ACTIVE -> RINGING).
+            // TODO(santoscordon): Consider putting a stop to the above and turning CallState into
+            // a well-defined state machine.
+            // TODO(santoscordon): Define expected state transitions here, and log when an
+            // unexpected transition occurs.
+            call.setState(state);
+            // TODO(santoscordon): Notify the in-call app whenever a call changes state.
+        }
+    }
+
+    /**
+     * Adds the specified call to the main list of live calls.
+     *
+     * @param call The call to add.
+     */
+    private void addCall(Call call) {
+        mCalls.put(call.getId(), call);
+    }
 }
diff --git a/src/com/android/telecomm/InCallController.java b/src/com/android/telecomm/InCallController.java
index 7b48d95..f597030 100644
--- a/src/com/android/telecomm/InCallController.java
+++ b/src/com/android/telecomm/InCallController.java
@@ -76,11 +76,9 @@
 
     /**
      * Persists the specified parameters.
-     *
-     * @param callsManager The singleton calls manager instance.
      */
-    InCallController(CallsManager callsManager) {
-        mCallsManager = callsManager;
+    InCallController() {
+        mCallsManager = CallsManager.getInstance();
     }
 
     // TODO(santoscordon): May be better to expose the IInCallService methods directly from this
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index e7b0ff3..5135326 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -24,9 +24,11 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.telecomm.CallInfo;
+import android.telecomm.CallState;
 import android.telecomm.ICallService;
 import android.telecomm.ICallServiceSelectionResponse;
 import android.telecomm.ICallServiceSelector;
+import android.util.Log;
 
 import java.util.Iterator;
 import java.util.List;
@@ -40,7 +42,8 @@
  * call services is then attempted until either the outgoing call is placed, the attempted call
  * is aborted (by the switchboard), or the list is exhausted -- whichever occurs first.
  *
- * Except for the abort case, all other scenarios should terminate with the switchboard notified.
+ * Except for the abort case, all other scenarios should terminate with the switchboard notified
+ * of the result.
  *
  * NOTE(gilad): Currently operating under the assumption that we'll have one timeout per (outgoing)
  * call attempt.  If we (also) like to timeout individual selectors and/or call services, the code
@@ -49,11 +52,6 @@
 final class OutgoingCallProcessor {
 
     /**
-     * The (singleton) Telecomm switchboard instance.
-     */
-    private final Switchboard mSwitchboard;
-
-    /**
      * The outgoing call this processor is tasked with placing.
      */
     private final Call mCall;
@@ -76,6 +74,11 @@
     /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
+    /** Manages all outgoing call processors. */
+    private final OutgoingCallsManager mOutgoingCallsManager;
+
+    private final Switchboard mSwitchboard;
+
     /**
      * The iterator over the currently-selected ordered list of call-service IDs.
      */
@@ -83,6 +86,12 @@
 
     private Iterator<ICallServiceSelector> mSelectorIterator;
 
+    /**
+     * The last call service which we asked to place the call. If null, it indicates that there
+     * exists no call service that we expect to place this call.
+     */
+    private ICallService mCallService;
+
     private boolean mIsAborted = false;
 
     /**
@@ -90,17 +99,19 @@
      * passing to each selector (read-only versions of) the call object and all available call-
      * service IDs.  Stops once a matching selector is found.  Calls with no matching selectors
      * will eventually be killed by the cleanup/monitor switchboard handler, which will in turn
-     * call the abort method of this processor.
+     * call the abort method of this processor via {@link OutgoingCallsManager}.
      *
      * @param call The call to place.
      * @param callServices The available call-service implementations.
      * @param selectors The available call-service selector implementations.
-     * @param switchboard The switchboard for this processor to work against.
+     * @param outgoingCallsManager Manager of all outgoing call processors.
+     * @param switchboard The switchboard.
      */
     OutgoingCallProcessor(
             Call call,
             Set<ICallService> callServices,
             List<ICallServiceSelector> selectors,
+            OutgoingCallsManager outgoingCallsManager,
             Switchboard switchboard) {
 
         ThreadUtil.checkOnMainThread();
@@ -110,6 +121,7 @@
 
         mCall = call;
         mSelectors = selectors;
+        mOutgoingCallsManager = outgoingCallsManager;
         mSwitchboard = switchboard;
 
         // Populate the list and map of call-service IDs.  The list is needed since
@@ -131,7 +143,7 @@
         if (mSelectors.isEmpty() || mCallServiceIds.isEmpty()) {
             // TODO(gilad): Consider adding a failure message/type to differentiate the various
             // cases, or potentially throw an exception in this case.
-            mSwitchboard.handleFailedOutgoingCall(mCall);
+            mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
         } else if (mSelectorIterator == null) {
             mSelectorIterator = mSelectors.iterator();
             attemptNextSelector();
@@ -140,14 +152,39 @@
 
     /**
      * Aborts the attempt to place the relevant call.  Intended to be invoked by
-     * switchboard.
+     * switchboard through the outgoing-calls manager.
      */
     void abort() {
         ThreadUtil.checkOnMainThread();
+        resetCallService();
         mIsAborted = true;
     }
 
     /**
+     * Completes the outgoing call sequence by setting the call service on the call object. This is
+     * invoked when the call service adapter receives positive confirmation that the call service
+     * placed the call.
+     */
+    void handleSuccessfulCallAttempt() {
+        abort();  // Technically not needed but playing it safe.
+        mCall.setCallService(mCallService);
+        mCall.setState(CallState.DIALING);
+        resetCallService();
+
+        mSwitchboard.handleSuccessfulOutgoingCall(mCall);
+    }
+
+    /**
+     * Attempts the next call service if the specified call service is the one currently being
+     * attempted.
+     *
+     * @param reason The call-service supplied reason for the failed call attempt.
+     */
+    void handleFailedCallAttempt(String reason) {
+        attemptNextCallService();
+    }
+
+    /**
      * Attempts to place the call using the next selector, no-op if no other selectors
      * are available.
      */
@@ -166,7 +203,7 @@
             }
 
         } else {
-            mSwitchboard.handleFailedOutgoingCall(mCall);
+            mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
         }
     }
 
@@ -206,7 +243,9 @@
     }
 
     /**
-     * TODO(gilad): Add comment.
+     * Attempts to place the call using the call service specified by the next call-service ID of
+     * mCallServiceIdIterator. If there are no more call services to attempt, the process continues
+     * to the next call-service selector via {@link #attemptNextSelector}.
      */
     private void attemptNextCallService() {
         if (mIsAborted) {
@@ -215,10 +254,12 @@
 
         if (mCallServiceIdIterator.hasNext()) {
             String id = mCallServiceIdIterator.next();
-            ICallService callService = mCallServicesById.get(id);
-            if (callService != null) {
+            mCallService = mCallServicesById.get(id);
+            if (mCallService == null) {
+                attemptNextCallService();
+            } else {
                 try {
-                    callService.call(mCall.toCallInfo());
+                    mCallService.call(mCall.toCallInfo());
                 } catch (RemoteException e) {
                     // TODO(gilad): Log etc.
                     attemptNextCallService();
@@ -226,25 +267,16 @@
             }
         } else {
             mCallServiceIdIterator = null;
+            resetCallService();
             attemptNextSelector();
         }
     }
 
     /**
-     * Handles the successful outgoing-call case.
+     * Nulls out the reference to the current call service. Invoked when the call service is no longer
+     * expected to place the outgoing call.
      */
-    private void handleSuccessfulOutgoingCall() {
-        // TODO(gilad): More here?
-
-        abort();  // Shouldn't be necessary but better safe than sorry.
-        mSwitchboard.handleSuccessfulOutgoingCall(mCall);
-    }
-
-    /**
-     * Handles the failed outgoing-call case.
-     */
-    private void handleFailedOutgoingCall() {
-        // TODO(gilad): Implement.
-        attemptNextCallService();
+    private void resetCallService() {
+        mCallService = null;
     }
 }
diff --git a/src/com/android/telecomm/OutgoingCallsManager.java b/src/com/android/telecomm/OutgoingCallsManager.java
new file mode 100644
index 0000000..27472f1
--- /dev/null
+++ b/src/com/android/telecomm/OutgoingCallsManager.java
@@ -0,0 +1,131 @@
+/*
+ * 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.RemoteException;
+import android.telecomm.ICallService;
+import android.telecomm.ICallServiceSelector;
+import android.util.Log;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Responsible for placing all outgoing calls. For each outgoing call, this class creates an
+ * instance of {@link OutgoingCallProcessor} which handles the details of connecting to the
+ * appropriate call service and placing the call. This class maintains a mapping from call ID
+ * to {@link OutgoingCallProcessor} so that other classes (Switchboard, CallServiceAdapter, etc),
+ * can simply call into this class instead of individual OutgoingCallProcessors.
+ */
+final class OutgoingCallsManager {
+    private static final String TAG = OutgoingCallsManager.class.getSimpleName();
+
+    private final Switchboard mSwitchboard;
+
+    /**
+     * Maps call IDs to {@link OutgoingCallProcessor}s.
+     */
+    private Map<String, OutgoingCallProcessor> mOutgoingCallProcessors = Maps.newHashMap();
+
+    /** Persists specified parameters. */
+    OutgoingCallsManager(Switchboard switchboard) {
+        mSwitchboard = switchboard;
+    }
+
+    /**
+     * Starts the process of placing a call by constructing an outgoing call processor and asking
+     * it to place the call. Upon success, execution will continue (via {@link CallServiceAdapter})
+     * to {@link #handleSuccessfulCall}. Upon failure, execution will return to
+     * {@link #handleFailedCall}.
+     *
+     * @param call The call to place.
+     * @param callServices The set of call services which can potentially place the call.
+     * @param selectors The ordered list of selectors used in placing the call.
+     */
+    void placeCall(
+            Call call, Set<ICallService> callServices, List<ICallServiceSelector> selectors) {
+
+        Log.i(TAG, "Placing an outgoing call (" + call.getId() + ")");
+
+        // Create the processor for this (outgoing) call and store it in a map such that call
+        // attempts can be aborted etc.
+        // TODO(gilad): Consider passing mSelector as an immutable set.
+        OutgoingCallProcessor processor =
+                new OutgoingCallProcessor(call, callServices, selectors, this, mSwitchboard);
+
+        mOutgoingCallProcessors.put(call.getId(), processor);
+        processor.process();
+    }
+
+    /**
+     * Removes the outgoing call processor mapping for the successful call and returns execution to
+     * the switchboard. This method is invoked from {@link CallServiceAdapter} after a call service
+     * has notified Telecomm that it successfully placed the call.
+     *
+     * @param callId The ID of the call.
+     */
+    void handleSuccessfulCallAttempt(String callId) {
+        OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(callId);
+
+        if (processor == null) {
+            // Shouldn't happen, so log a wtf if it does.
+            Log.wtf(TAG, "Received an unexpected placed-call notification.");
+        } else {
+            processor.handleSuccessfulCallAttempt();
+        }
+    }
+
+    /**
+     * Notifies the appropriate outgoing call processor that a call attempt to place the call has
+     * failed and the processor should continue attempting to place the call with the next call
+     * service. This method is called from {@link CallServiceAdapter} after a call service has
+     * notified Telecomm that it could not place the call.
+     *
+     * @param callId The ID of the failed outgoing call.
+     * @param reason The call-service supplied reason for the failed call attempt.
+     */
+    void handleFailedCallAttempt(String callId, String reason) {
+        OutgoingCallProcessor processor = mOutgoingCallProcessors.get(callId);
+
+        // TODO(santoscordon): Consider combining the check here and in handleSuccessfulCallAttempt.
+        if (processor == null) {
+            // Shouldn't happen, so log a wtf if it does.
+            Log.wtf(TAG, "Received an unexpected failed-call notification.");
+        } else {
+            processor.handleFailedCallAttempt(reason);
+        }
+    }
+
+    /**
+     * Removes the outgoing call processor mapping for the failed call and returns execution to the
+     * switchboard. In contrast to handleFailedCallAttempt which comes from the call-service and
+     * goes to the outgoing-call processor indicating a single failed call attempt, this method is
+     * invoked by the outgoing-call processor to indicate that the entire process has failed and we
+     * should cleanup and notify Switchboard.
+     *
+     * @param call The failed outgoing call.
+     */
+    void handleFailedOutgoingCall(Call call) {
+        mOutgoingCallProcessors.remove(call.getId());
+        mSwitchboard.handleFailedOutgoingCall(call);
+    }
+}
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index 68ffc73..466f97f 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -45,9 +45,14 @@
      */
     private final static int TICK_FREQUENCY = 250;
 
-    private final CallServiceFinder mCallServiceFinder = new CallServiceFinder(this);
+    private final CallsManager mCallsManager;
 
-    private final CallServiceSelectorFinder mSelectorFinder = new CallServiceSelectorFinder(this);
+    /** Used to place outgoing calls. */
+    private final OutgoingCallsManager mOutgoingCallsManager;
+
+    private CallServiceFinder mCallServiceFinder;
+
+    private CallServiceSelectorFinder mSelectorFinder;
 
     /** Used to schedule tasks on the main (UI) thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -84,7 +89,15 @@
      */
     private Set<ICallServiceSelector> mSelectors;
 
-    private Map<Call, OutgoingCallProcessor> outgoingCallProcessors = Maps.newHashMap();
+    /**
+     * Persists the specified parameters and initializes Switchboard.
+     */
+    Switchboard() {
+        mCallsManager = CallsManager.getInstance();
+        mOutgoingCallsManager = new OutgoingCallsManager(this);
+        mCallServiceFinder = new CallServiceFinder(this, mOutgoingCallsManager);
+        mSelectorFinder = new CallServiceSelectorFinder(this);
+    }
 
     /**
      * Attempts to place an outgoing call to the specified handle.
@@ -157,7 +170,7 @@
      * see {@link OutgoingCallProcessor}.
      */
     void handleSuccessfulOutgoingCall(Call call) {
-        // TODO(gilad): More here.
+        mCallsManager.handleSuccessfulOutgoingCall(call);
 
         // Process additional (new) calls, if any.
         processNewOutgoingCalls();
@@ -236,14 +249,7 @@
         List<ICallServiceSelector> selectors = Lists.newArrayList();
         selectors.addAll(mSelectors);
 
-        // Create the processor for this (outgoing) call and store it in a map such that call
-        // attempts can be aborted etc.
-        // TODO(gilad): Consider passing mSelector as an immutable set.
-        OutgoingCallProcessor processor =
-                new OutgoingCallProcessor(call, mCallServices, selectors, this);
-
-        outgoingCallProcessors.put(call, processor);
-        processor.process();
+        mOutgoingCallsManager.placeCall(call, mCallServices, selectors);
     }
 
     /**