Use CallServiceWrappers in place of ICallService.

Change-Id: Icd8a6ae2401f68d04b359fb7539b6d4f5aafecd5
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 905fa6a..ea62da4 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -19,7 +19,6 @@
 import android.os.RemoteException;
 import android.telecomm.CallInfo;
 import android.telecomm.CallState;
-import android.telecomm.ICallService;
 import android.util.Log;
 
 import java.util.Date;
@@ -60,7 +59,7 @@
      * The call service which is currently connecting this call, null as long as the call is not
      * connected.
      */
-    private ICallService mCallService;
+    private CallServiceWrapper mCallService;
 
     /**
      * Read-only and parcelable version of this call.
@@ -117,11 +116,11 @@
         return new Date().getTime() - mCreationTime.getTime();
     }
 
-    ICallService getCallService() {
+    CallServiceWrapper getCallService() {
         return mCallService;
     }
 
-    void setCallService(ICallService callService) {
+    void setCallService(CallServiceWrapper callService) {
         mCallService = callService;
     }
 
@@ -132,19 +131,18 @@
         setCallService(null);
     }
 
-    /*
+    /**
      * Attempts to disconnect the call through the call service.
      */
     void disconnect() {
         if (mCallService == null) {
             Log.w(TAG, "disconnect() request on a call without a call service.");
         } else {
-            try {
-                Log.i(TAG, "Send disconnect to call service for call with id " + mId);
-                mCallService.disconnect(mId);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Disconnect attempt failed.", e);
-            }
+            Log.i(TAG, "Send disconnect to call service for call with id " + mId);
+            // The call isn't officially disconnected until the call service confirms that the call
+            // was actually disconnected. Only then is the association between call and call service
+            // severed, see {@link CallsManager#markCallAsDisconnected}.
+            mCallService.disconnect(mId);
         }
     }
 
diff --git a/src/com/android/telecomm/CallServiceFinder.java b/src/com/android/telecomm/CallServiceFinder.java
deleted file mode 100644
index d24c612..0000000
--- a/src/com/android/telecomm/CallServiceFinder.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 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.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.telecomm.ICallService;
-import android.telecomm.ICallServiceLookupResponse;
-import android.telecomm.ICallServiceProvider;
-import android.util.Log;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Finds {@link ICallService} and {@link ICallServiceProvider} implementations on the device.
- * Uses binder APIs to find ICallServiceProviders and calls method on ICallServiceProvider to
- * find ICallService implementations.
- * TODO(santoscordon): Add performance timing to async calls.
- */
-final class CallServiceFinder {
-
-    private static final String TAG = CallServiceFinder.class.getSimpleName();
-
-    /**
-     * The longest period in milliseconds each lookup cycle is allowed to span over, see
-     * {@link #mLookupTerminator}.
-     * TODO(gilad): Likely requires tuning.
-     */
-    private static final int LOOKUP_TIMEOUT_MS = 100;
-
-    private final Switchboard mSwitchboard;
-
-    private final OutgoingCallsManager mOutgoingCallsManager;
-
-    /**
-     * Determines whether or not a lookup cycle is already running.
-     */
-    private boolean mIsLookupInProgress = false;
-
-    /**
-     * The current lookup-cycle ID. Incremented upon initiateLookup calls.
-     * TODO(gilad): If at all useful, consider porting the cycle ID concept to switchboard and
-     * have it centralized/shared between the two finders.
-     */
-    private int mLookupId = 0;
-
-    /**
-     * The set of bound call-services. Only populated via initiateLookup scenarios. Entries should
-     * only be removed upon unbinding.
-     * TODO(gilad): Add the necessary logic to keep this set up to date.
-     */
-    private Set<ICallService> mCallServiceRegistry = Sets.newHashSet();
-
-    /**
-     * The set of bound call-service providers.  Only populated via initiateLookup scenarios.
-     * Providers should only be removed upon unbinding.
-     * TODO(santoscordon): This can be removed once this class starts using CallServiceWrapper
-     * since we'll be able to unbind the providers within registerProvider().
-     */
-    private Set<CallServiceProviderWrapper> mProviderRegistry = Sets.newHashSet();
-
-    /**
-     * Map of {@link CallServiceProviderWrapper}s keyed by their ComponentName. Used as a long-lived
-     * cache in order to simplify management of service-wrapper construction/destruction.
-     */
-    private Map<ComponentName, CallServiceProviderWrapper> mProviderCache = Maps.newHashMap();
-
-    /**
-     * Stores the names of the providers to bind to in one lookup cycle.  The set size represents
-     * the number of call-service providers this finder expects to hear back from upon initiating
-     * call-service lookups, see initiateLookup. Whenever all providers respond before the lookup
-     * timeout occurs, the complete set of (available) call services is passed to the switchboard
-     * for further processing of outgoing calls etc.  When the timeout occurs before all responses
-     * are received, the partial (potentially empty) set gets passed (to the switchboard) instead.
-     * Entries are removed from this set as providers register.
-     */
-    private Set<ComponentName> mUnregisteredProviders;
-
-    /**
-     * Used to interrupt lookup cycles that didn't terminate naturally within the allowed
-     * period, see LOOKUP_TIMEOUT.
-     */
-    private final Runnable mLookupTerminator = new Runnable() {
-        @Override
-        public void run() {
-            terminateLookup();
-        }
-    };
-
-    /** 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.
-     *
-     * @param switchboard The switchboard for this finder to work against.
-     * @param outgoingCallsManager Manager in charge of placing outgoing calls.
-     */
-    CallServiceFinder(Switchboard switchboard, OutgoingCallsManager outgoingCallsManager) {
-        mSwitchboard = switchboard;
-        mOutgoingCallsManager = outgoingCallsManager;
-    }
-
-    /**
-     * Initiates a lookup cycle for call-service providers. Must be called from the UI thread.
-     * TODO(gilad): Expand this comment to describe the lookup flow in more detail.
-     */
-    void initiateLookup() {
-        ThreadUtil.checkOnMainThread();
-        if (mIsLookupInProgress) {
-            // At most one active lookup is allowed at any given time, bail out.
-            return;
-        }
-
-        List<ComponentName> providerNames = getProviderNames();
-        if (providerNames.isEmpty()) {
-            Log.i(TAG, "No ICallServiceProvider implementations found.");
-            updateSwitchboard();
-            return;
-        }
-
-        mLookupId++;
-        mIsLookupInProgress = true;
-        mUnregisteredProviders = Sets.newHashSet();
-
-        for (ComponentName name : providerNames) {
-            // Bind to each of the providers that were found. Some of the providers may already be
-            // bound, and in those cases the provider wrapper will still invoke registerProvider()
-            // allowing us to treat bound and unbound providers the same.
-            getProvider(name).bind();
-            mUnregisteredProviders.add(name);
-        }
-
-        int providerCount = providerNames.size();
-        int unregisteredProviderCount = mUnregisteredProviders.size();
-
-        Log.i(TAG, "Found " + providerCount + " implementations of ICallServiceProvider, "
-                + unregisteredProviderCount + " of which are not currently registered.");
-
-        if (unregisteredProviderCount == 0) {
-            // All known (provider) implementations are already registered, pass control
-            // back to the switchboard.
-            updateSwitchboard();
-        } else {
-            // Schedule a lookup terminator to run after LOOKUP_TIMEOUT_MS milliseconds.
-            mHandler.removeCallbacks(mLookupTerminator);
-            mHandler.postDelayed(mLookupTerminator, LOOKUP_TIMEOUT_MS);
-        }
-    }
-
-    /**
-     * Returns the all-inclusive list of call-service-provider names.
-     *
-     * @return The list containing the (component) names of all known ICallServiceProvider
-     *     implementations or the empty list upon no available providers.
-     */
-    private List<ComponentName> getProviderNames() {
-        // The list of provider names to return to the caller, may be populated below.
-        List<ComponentName> providerNames = Lists.newArrayList();
-
-        PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
-        Intent intent = new Intent(CallServiceProviderWrapper.CALL_SERVICE_PROVIDER_ACTION);
-        for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
-            ServiceInfo serviceInfo = entry.serviceInfo;
-            if (serviceInfo != null) {
-                // The entry resolves to a proper service, add it to the list of provider names.
-                providerNames.add(
-                        new ComponentName(serviceInfo.packageName, serviceInfo.name));
-            }
-        }
-
-        return providerNames;
-    }
-
-    /**
-     * Queries the supplied provider asynchronously for its CallServices and passes the list through
-     * to {@link #registerCallServices} which will relinquish control back to switchboard.
-     *
-     * @param providerName The component name of the relevant provider.
-     * @param provider The provider object to register.
-     */
-    void registerProvider(
-            final ComponentName providerName, final CallServiceProviderWrapper provider) {
-
-        // Query the provider for {@link ICallService} implementations.
-        provider.lookupCallServices(new ICallServiceLookupResponse.Stub() {
-            @Override
-            public void setCallServices(final List<IBinder> binderList) {
-                // TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity()?
-                mHandler.post(new Runnable() {
-                    @Override public void run() {
-                        Set<ICallService> callServices = Sets.newHashSet();
-                        for (IBinder binder : binderList) {
-                            callServices.add(ICallService.Stub.asInterface(binder));
-                        }
-                        registerCallServices(providerName, provider, callServices);
-                    }
-                });
-            }
-        });
-    }
-
-    /**
-     * Registers the {@link CallService}s for the specified provider and performs the necessary
-     * bookkeeping to potentially return control to the switchboard before the timeout for the
-     * current lookup cycle.
-     *
-     * @param providerName The component name of the relevant provider.
-     * @param provider The provider associated with callServices.
-     * @param callServices The {@link CallService}s to register.
-     */
-    private void registerCallServices(
-            ComponentName providerName,
-            CallServiceProviderWrapper provider,
-            Set<ICallService> callServices) {
-        ThreadUtil.checkOnMainThread();
-
-        if (mUnregisteredProviders.remove(providerName)) {
-            mProviderRegistry.add(provider);
-
-            // Add all the call services from this provider to the call-service registry.
-            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.");
-                }
-            }
-
-            if (mUnregisteredProviders.size() < 1) {
-                terminateLookup();  // No other providers to wait for.
-            }
-        } else {
-            Log.i(TAG, "Unexpected list of call services in lookup " + mLookupId + " from " +
-                    providerName);
-        }
-    }
-
-    /**
-     * Unregisters the specified provider.
-     *
-     * @param providerName The component name of the relevant provider.
-     */
-    void unregisterProvider(ComponentName providerName) {
-        ThreadUtil.checkOnMainThread();
-        mProviderRegistry.remove(providerName);
-    }
-
-    /**
-     * Timeouts the current lookup cycle, see LookupTerminator.
-     */
-    private void terminateLookup() {
-        mHandler.removeCallbacks(mLookupTerminator);
-        mUnregisteredProviders.clear();
-
-        updateSwitchboard();
-        mIsLookupInProgress = false;
-    }
-
-    /**
-     * Updates the switchboard passing the relevant call services (as opposed
-     * to call-service providers).
-     */
-    private void updateSwitchboard() {
-        ThreadUtil.checkOnMainThread();
-        mSwitchboard.setCallServices(mCallServiceRegistry);
-    }
-
-    /**
-     * Returns the call-service provider wrapper for the specified componentName. Creates a new one
-     * if none is found in the cache.
-     *
-     * @param ComponentName The component name of the provider.
-     */
-    private CallServiceProviderWrapper getProvider(ComponentName componentName) {
-        Preconditions.checkNotNull(componentName);
-
-        CallServiceProviderWrapper provider = mProviderCache.get(componentName);
-        if (provider == null) {
-            provider = new CallServiceProviderWrapper(componentName, this);
-            mProviderCache.put(componentName, provider);
-        }
-
-        return provider;
-    }
-}
diff --git a/src/com/android/telecomm/CallServiceProviderWrapper.java b/src/com/android/telecomm/CallServiceProviderWrapper.java
index 044d370..18cff35 100644
--- a/src/com/android/telecomm/CallServiceProviderWrapper.java
+++ b/src/com/android/telecomm/CallServiceProviderWrapper.java
@@ -45,28 +45,30 @@
     private ICallServiceProvider mServiceInterface;
 
     /**
-     * The class to notify when the binding succeeds or fails.
+     * The class to notify when binding succeeds or fails.
      */
-    private final CallServiceFinder mFinder;
+    private final CallServiceRepository mRepository;
 
     /**
      * Creates a call-service provider for the specified component.
+     *
+     * @param componentName The component name of the service to bind to.
+     * @param repository The call-service repository.
      */
-    public CallServiceProviderWrapper(ComponentName componentName, CallServiceFinder finder) {
+    public CallServiceProviderWrapper(ComponentName componentName, CallServiceRepository repository) {
         super(CALL_SERVICE_PROVIDER_ACTION, componentName);
-        mFinder = finder;
+        mRepository = repository;
     }
 
     /** {@inheritDoc} */
     @Override public void handleSuccessfulConnection(IBinder binder) {
         mServiceInterface = ICallServiceProvider.Stub.asInterface(binder);
-        mFinder.registerProvider(getComponentName(), this);
+        mRepository.processProvider(getComponentName(), this);
     }
 
     /** {@inheritDoc} */
     @Override public void handleFailedConnection() {
-        mFinder.unregisterProvider(getComponentName());
-        // TODO(santoscordon): Notify CallServiceFinder.
+        mRepository.abortProvider(getComponentName());
     }
 
     /** {@inheritDoc} */
@@ -75,7 +77,9 @@
         // TODO(santoscordon): fill in.
     }
 
-    /** See {@link ICallServiceProvider#lookupCallServices}. */
+    /**
+     * See {@link ICallServiceProvider#lookupCallServices}.
+     */
     public void lookupCallServices(ICallServiceLookupResponse response) {
         try {
             if (mServiceInterface == null) {
diff --git a/src/com/android/telecomm/CallServiceRepository.java b/src/com/android/telecomm/CallServiceRepository.java
new file mode 100644
index 0000000..28cac6b
--- /dev/null
+++ b/src/com/android/telecomm/CallServiceRepository.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 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.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telecomm.CallServiceInfo;
+import android.telecomm.ICallService;
+import android.telecomm.ICallServiceLookupResponse;
+import android.telecomm.ICallServiceProvider;
+import android.util.Log;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Uses package manager to find all implementations of {@link ICallServiceProvider} and uses them to
+ * get a list of bindable {@link ICallServices}. Ultimately returns a list of call services as
+ * {@link CallServiceWrapper}s. The resulting call services may or may not be bound at the end of the
+ * lookup.
+ * TODO(santoscordon): Add performance timing to async calls.
+ * TODO(santoscordon): Need to unbind/remove unused call services stored in the cache.
+ */
+final class CallServiceRepository {
+
+    private static final String TAG = CallServiceRepository.class.getSimpleName();
+
+    /**
+     * The longest period in milliseconds each lookup cycle is allowed to span over, see
+     * {@link #mLookupTerminator}.
+     * TODO(gilad): Likely requires tuning.
+     */
+    private static final int LOOKUP_TIMEOUT_MS = 100;
+
+    private final Switchboard mSwitchboard;
+
+    private final OutgoingCallsManager mOutgoingCallsManager;
+
+    /**
+     * Determines whether or not a lookup cycle is already running.
+     */
+    private boolean mIsLookupInProgress = false;
+
+    /**
+     * The current lookup-cycle ID, unique per invocation of {@link #initiateLookup}.
+     */
+    private int mLookupId = 0;
+
+    /**
+     * Map of {@link CallServiceProviderWrapper}s keyed by their ComponentName. Used as a cache for
+     * call services. Entries are added to the cache as part of the lookup sequence. Every call
+     * service found will have an entry in the cache. The cache is cleaned up periodically to
+     * remove any call services (bound or unbound) which are no longer needed because they have no
+     * associated calls. After a cleanup, the only entries in the cache should be call services
+     * with existing calls. During the lookup, we will always use a cached version of a call service
+     * if one exists.
+     */
+    private Map<ComponentName, CallServiceProviderWrapper> mProviderCache = Maps.newHashMap();
+
+    /**
+     * Map of {@link CallServiceWrapper}s keyed by their ComponentName. Used as a long-lived cache
+     * in order to simplify management of service-wrapper construction/destruction.
+     */
+    private Map<ComponentName, CallServiceWrapper> mCallServiceCache = Maps.newHashMap();
+
+    /**
+     * Stores the names of the providers to bind to in one lookup cycle.  The set size represents
+     * the number of call-service providers this repository expects to hear back from upon
+     * initiating call-service lookups, see initiateLookup. Whenever all providers respond before
+     * the lookup timeout occurs, the complete set of (available) call services is passed to the
+     * switchboard for further processing of outgoing calls etc.  When the timeout occurs before all
+     * responses are received, the partial (potentially empty) set gets passed (to the switchboard)
+     * instead. Entries are removed from this set as providers are processed.
+     */
+    private Set<ComponentName> mOutstandingProviders;
+
+    /**
+     * Used to interrupt lookup cycles that didn't terminate naturally within the allowed
+     * period, see LOOKUP_TIMEOUT.
+     */
+    private final Runnable mLookupTerminator = new Runnable() {
+        @Override
+        public void run() {
+            terminateLookup();
+        }
+    };
+
+    /** 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.
+     *
+     * @param switchboard The switchboard.
+     * @param outgoingCallsManager Manager in charge of placing outgoing calls.
+     */
+    CallServiceRepository(Switchboard switchboard, OutgoingCallsManager outgoingCallsManager) {
+        mSwitchboard = switchboard;
+        mOutgoingCallsManager = outgoingCallsManager;
+    }
+
+    /**
+     * Initiates a lookup cycle for call-service providers. Must be called from the UI thread.
+     * TODO(gilad): Expand this comment to describe the lookup flow in more detail.
+     *
+     * @param lookupId The switchboard-supplied lookup ID.
+     */
+    void initiateLookup(int lookupId) {
+        ThreadUtil.checkOnMainThread();
+        if (mIsLookupInProgress) {
+            // At most one active lookup is allowed at any given time, bail out.
+            return;
+        }
+
+        List<ComponentName> providerNames = getProviderNames();
+        if (providerNames.isEmpty()) {
+            Log.i(TAG, "No ICallServiceProvider implementations found.");
+            updateSwitchboard();
+            return;
+        }
+
+        mLookupId = lookupId;
+        mIsLookupInProgress = true;
+        mOutstandingProviders = Sets.newHashSet();
+
+        for (ComponentName name : providerNames) {
+            // Bind to each of the providers that were found. Some of the providers may already be
+            // bound, and in those cases the provider wrapper will still invoke processProvider()
+            // allowing us to treat bound and unbound providers the same.
+            getProvider(name).bind();
+            mOutstandingProviders.add(name);
+        }
+
+        int providerCount = providerNames.size();
+        int outstandingProviderCount = mOutstandingProviders.size();
+
+        Log.i(TAG, "Found " + providerCount + " implementations of ICallServiceProvider, "
+                + outstandingProviderCount + " of which are not currently processed.");
+
+        if (outstandingProviderCount == 0) {
+            // All known (provider) implementations are already processed, pass control
+            // back to the switchboard.
+            updateSwitchboard();
+        } else {
+            // Schedule a lookup terminator to run after LOOKUP_TIMEOUT_MS milliseconds.
+            mHandler.removeCallbacks(mLookupTerminator);
+            mHandler.postDelayed(mLookupTerminator, LOOKUP_TIMEOUT_MS);
+        }
+    }
+
+    /**
+     * Returns the all-inclusive list of call-service-provider names.
+     *
+     * @return The list containing the (component) names of all known ICallServiceProvider
+     *     implementations or the empty list upon no available providers.
+     */
+    private List<ComponentName> getProviderNames() {
+        // The list of provider names to return to the caller, may be populated below.
+        List<ComponentName> providerNames = Lists.newArrayList();
+
+        PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
+        Intent intent = new Intent(CallServiceProviderWrapper.CALL_SERVICE_PROVIDER_ACTION);
+        for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
+            ServiceInfo serviceInfo = entry.serviceInfo;
+            if (serviceInfo != null) {
+                // The entry resolves to a proper service, add it to the list of provider names.
+                providerNames.add(
+                        new ComponentName(serviceInfo.packageName, serviceInfo.name));
+            }
+        }
+
+        return providerNames;
+    }
+
+    /**
+     * Queries the supplied provider asynchronously for its CallServices and passes the list through
+     * to {@link #processCallServices} which will relinquish control back to switchboard.
+     *
+     * @param providerName The component name of the relevant provider.
+     * @param provider The provider object to process.
+     */
+    void processProvider(
+            final ComponentName providerName, final CallServiceProviderWrapper provider) {
+        Preconditions.checkNotNull(providerName);
+        Preconditions.checkNotNull(provider);
+
+        // Query the provider for {@link ICallService} implementations.
+        provider.lookupCallServices(new ICallServiceLookupResponse.Stub() {
+            // TODO(santoscordon): Rename CallServiceInfo to CallServiceDescriptor and update
+            // this method name to setCallServiceDescriptors.
+            @Override
+            public void setCallServices(final List<CallServiceInfo> callServiceInfos) {
+                // TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity()?
+                mHandler.post(new Runnable() {
+                    @Override public void run() {
+                        processCallServices(
+                                providerName, provider, Sets.newHashSet(callServiceInfos));
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Skips the processing of a provider. Called in cases where the provider was not found or
+     * not connected.
+     *
+     * @param providerName The component name of the relevant provider.
+     */
+    void abortProvider(ComponentName providerName) {
+        Preconditions.checkNotNull(providerName);
+        removeOutstandingProvider(providerName);
+    }
+
+    /**
+     * Processes the {@link CallServiceInfo}s for the specified provider and performs the necessary
+     * bookkeeping to potentially return control to the switchboard before the timeout for the
+     * current lookup cycle.
+     *
+     * @param providerName The component name of the relevant provider.
+     * @param provider The provider associated with callServices.
+     * @param callServiceInfos The set of call service infos to process.
+     */
+    private void processCallServices(
+            ComponentName providerName,
+            CallServiceProviderWrapper provider,
+            Set<CallServiceInfo> callServiceInfos) {
+
+        Preconditions.checkNotNull(provider);
+        Preconditions.checkNotNull(callServiceInfos);
+        ThreadUtil.checkOnMainThread();
+
+        // We only need the provider for retrieving the call-service info set, so unbind here.
+        provider.unbind();
+
+        if (mOutstandingProviders.contains(providerName)) {
+            // Add all the call services from this provider to the call-service cache.
+            for (CallServiceInfo info : callServiceInfos) {
+                getCallService(info);
+            }
+
+            removeOutstandingProvider(providerName);
+        } else {
+            Log.i(TAG, "Unexpected list of call services in lookup " + mLookupId + " from " +
+                    providerName);
+        }
+    }
+
+    /**
+     * Removes an entry from the set of outstanding providers. When the final outstanding
+     * provider is removed, terminates the lookup.
+     *
+     * @param providerName The component name of the relevant provider.
+     */
+    private void removeOutstandingProvider(ComponentName providerName) {
+        ThreadUtil.checkOnMainThread();
+
+        mOutstandingProviders.remove(providerName);
+        if (mOutstandingProviders.size() < 1) {
+            terminateLookup();  // No other providers to wait for.
+        }
+    }
+
+    /**
+     * Timeouts the current lookup cycle, see LookupTerminator.
+     */
+    private void terminateLookup() {
+        mHandler.removeCallbacks(mLookupTerminator);
+        mOutstandingProviders.clear();
+
+        updateSwitchboard();
+        mIsLookupInProgress = false;
+    }
+
+    /**
+     * Updates the switchboard with the call services from the latest lookup.
+     */
+    private void updateSwitchboard() {
+        ThreadUtil.checkOnMainThread();
+
+        Set<CallServiceWrapper> callServices = Sets.newHashSet(mCallServiceCache.values());
+        mSwitchboard.setCallServices(callServices);
+    }
+
+    /**
+     * Returns the call-service provider wrapper for the specified componentName. Creates a new one
+     * if none is found in the cache.
+     *
+     * @param componentName The component name of the provider.
+     */
+    private CallServiceProviderWrapper getProvider(ComponentName componentName) {
+        Preconditions.checkNotNull(componentName);
+
+        CallServiceProviderWrapper provider = mProviderCache.get(componentName);
+        if (provider == null) {
+            provider = new CallServiceProviderWrapper(componentName, this);
+            mProviderCache.put(componentName, provider);
+        }
+
+        return provider;
+    }
+
+    /**
+     * Creates and returns the call service for the specified call-service info object. Inserts
+     * newly created entries into the cache, see {@link #mCallServiceCache}, or if a cached
+     * version already exists, returns that instead. All newly created instances will not yet
+     * be bound, however cached versions may or may not be bound.
+     *
+     * @param info The call service descriptor.
+     * @return The call service.
+     */
+    private CallServiceWrapper getCallService(CallServiceInfo info) {
+        Preconditions.checkNotNull(info);
+
+        // TODO(santoscordon): Rename getServiceComponent to getComponentName.
+        ComponentName componentName = info.getServiceComponent();
+
+        CallServiceWrapper callService = mCallServiceCache.get(componentName);
+        if (callService == null) {
+            CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
+            callService = new CallServiceWrapper(info, adapter);
+            mCallServiceCache.put(componentName, callService);
+        }
+
+        return callService;
+    }
+}
diff --git a/src/com/android/telecomm/CallServiceSelectorFinder.java b/src/com/android/telecomm/CallServiceSelectorRepository.java
similarity index 81%
rename from src/com/android/telecomm/CallServiceSelectorFinder.java
rename to src/com/android/telecomm/CallServiceSelectorRepository.java
index 0270e3d..13249c5 100644
--- a/src/com/android/telecomm/CallServiceSelectorFinder.java
+++ b/src/com/android/telecomm/CallServiceSelectorRepository.java
@@ -41,9 +41,9 @@
  * asynchronously bind to them.  Each lookup cycle is time-boxed, hence selectors too slow
  * to bind are effectively omitted from the set that is passed back to {@link Switchboard}.
  */
-final class CallServiceSelectorFinder {
+final class CallServiceSelectorRepository {
 
-    private static final String TAG = CallServiceSelectorFinder.class.getSimpleName();
+    private static final String TAG = CallServiceSelectorRepository.class.getSimpleName();
 
     /**
      * The longest period in milliseconds each lookup cycle is allowed to span over, see
@@ -74,17 +74,18 @@
 
     private final Switchboard mSwitchboard;
 
+    /** The application context. */
+    private final Context mContext;
+
     /**
      * Determines whether or not a lookup cycle is already running.
      */
     private boolean mIsLookupInProgress = false;
 
     /**
-     * Used to generate unique lookup-cycle identifiers. Incremented upon initiateLookup calls.
-     * TODO(gilad): If at all useful, consider porting the cycle ID concept to switchboard and
-     * have it centralized/shared between the two finders.
+     * The current lookup-cycle ID, unique per invocation of {@link #initiateLookup}.
      */
-    private int mNextLookupId = 0;
+    private int mLookupId = 0;
 
     /**
      * The set of bound call-service selectors.  Only populated via initiateLookup scenarios.
@@ -94,54 +95,55 @@
 
     /**
      * Stores the names of the selectors to bind to in one lookup cycle.  The set size represents
-     * the number of call-service selectors this finder expects to hear back from upon initiating
-     * lookups, see initiateLookup. Whenever all selectors respond before the timeout occurs, the
-     * complete set of available selectors is passed to the switchboard for further processing of
-     * outgoing calls etc.  When the timeout occurs before all responds are received, the partial
-     * (potentially empty) set gets passed to the switchboard instead. Note that cached selectors
-     * do not require finding and hence are excluded from this set.  Also note that selectors are
-     * removed from this set as they register.
+     * the number of call-service selectors this repositories expects to hear back from upon
+     * initiating lookups, see initiateLookup. Whenever all selectors respond before the timeout
+     * occurs, the complete set of available selectors is passed to the switchboard for further
+     * processing of outgoing calls etc.  When the timeout occurs before all responds are received,
+     * the partial (potentially empty) set gets passed to the switchboard instead. Note that cached
+     * selectors do not require finding and hence are excluded from this set.  Also note that
+     * selectors are removed from this set as they register.
      */
     private Set<ComponentName> mUnregisteredSelectors;
 
     /**
-     * Persists the specified parameters.
+     * Persists the specified parameters and initializes the new instance.
      *
      * @param switchboard The switchboard for this finer to work against.
      */
-    CallServiceSelectorFinder(Switchboard switchboard) {
+    CallServiceSelectorRepository(Switchboard switchboard) {
         mSwitchboard = switchboard;
+        mContext = TelecommApp.getInstance();
     }
 
     /**
      * Initiates a lookup cycle for call-service selectors. Must be called from the UI thread.
      *
-     * @param context The relevant application context.
+     * @param lookupId The switchboard-supplied lookup ID.
      */
-    void initiateLookup(Context context) {
+    void initiateLookup(int lookupId) {
         ThreadUtil.checkOnMainThread();
         if (mIsLookupInProgress) {
             // At most one active lookup is allowed at any given time, bail out.
             return;
         }
 
-        List<ComponentName> selectorNames = getSelectorNames(context);
+        List<ComponentName> selectorNames = getSelectorNames();
         if (selectorNames.isEmpty()) {
             Log.i(TAG, "No ICallServiceSelector implementations found.");
             updateSwitchboard();
             return;
         }
 
+        mLookupId = lookupId;
         mIsLookupInProgress = true;
         mUnregisteredSelectors = Sets.newHashSet();
 
-        int lookupId = mNextLookupId++;
         for (ComponentName name : selectorNames) {
             if (!mSelectorRegistry.contains(name)) {
                 // The selector is either not yet registered or has been unregistered
                 // due to unbinding etc.
                 mUnregisteredSelectors.add(name);
-                bindSelector(name, lookupId, context);
+                bindSelector(name, lookupId);
             }
         }
 
@@ -163,17 +165,14 @@
     }
 
     /**
-     * Returns the all-inclusive list of call-service selector names.
-     *
-     * @param context The relevant/application context to query against.
      * @return The list containing the (component) names of all known ICallServiceSelector
      *     implementations or the empty list upon no available selectors.
      */
-    private List<ComponentName> getSelectorNames(Context context) {
+    private List<ComponentName> getSelectorNames() {
         // The list of selector names to return to the caller, may be populated below.
         List<ComponentName> selectorNames = Lists.newArrayList();
 
-        PackageManager packageManager = context.getPackageManager();
+        PackageManager packageManager = mContext.getPackageManager();
         Intent intent = new Intent(CALL_SERVICE_SELECTOR_CLASS_NAME);
         for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
             ServiceInfo serviceInfo = entry.serviceInfo;
@@ -193,13 +192,11 @@
      *
      * @param selectorName The component name of the relevant selector.
      * @param lookupId The lookup-cycle ID.
-     * @param context The relevant application context.
      */
     private void bindSelector(
-            final ComponentName selectorName, final int lookupId, Context context) {
+            final ComponentName selectorName, final int lookupId) {
 
         Preconditions.checkNotNull(selectorName);
-        Preconditions.checkNotNull(context);
 
         Intent serviceIntent =
                 new Intent(CALL_SERVICE_SELECTOR_CLASS_NAME).setComponent(selectorName);
@@ -219,7 +216,7 @@
             }
         };
 
-        if (!context.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
+        if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
             // TODO(gilad): Handle error.
         }
     }
@@ -270,4 +267,4 @@
         mSwitchboard.setSelectors(mSelectorRegistry);
         mIsLookupInProgress = false;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/telecomm/CallServiceWrapper.java b/src/com/android/telecomm/CallServiceWrapper.java
index c7b494f..2dc5170 100644
--- a/src/com/android/telecomm/CallServiceWrapper.java
+++ b/src/com/android/telecomm/CallServiceWrapper.java
@@ -20,6 +20,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telecomm.CallInfo;
+import android.telecomm.CallServiceInfo;
 import android.telecomm.ICallService;
 import android.telecomm.ICallServiceAdapter;
 import android.util.Log;
@@ -32,10 +33,6 @@
  * TODO(santoscordon): Look into combining with android.telecomm.CallService.
  */
 public class CallServiceWrapper extends ServiceBinder<ICallService> {
-    private static final String TAG = CallServiceWrapper.class.getSimpleName();
-
-    /** The actual service implementation. */
-    private ICallService mServiceInterface;
 
     /**
      * The service action used to bind to ICallService implementations.
@@ -43,22 +40,44 @@
      */
     static final String CALL_SERVICE_ACTION = ICallService.class.getName();
 
+    private static final String TAG = CallServiceWrapper.class.getSimpleName();
+
+    /** The descriptor of this call service as supplied by the call-service provider. */
+    private final CallServiceInfo mCallServiceInfo;
+
+    /**
+     * The adapter used by the underlying call-service implementation to communicate with Telecomm.
+     */
+    private final CallServiceAdapter mAdapter;
+
+    /** The actual service implementation. */
+    private ICallService mServiceInterface;
+
     /**
      * Creates a call-service provider for the specified component.
+     *
+     * @param info The call-service descriptor from {@link ICallServiceProvider#lookupCallServices}.
+     * @param adapter The call-service adapter.
      */
-    public CallServiceWrapper(ComponentName componentName) {
-        super(CALL_SERVICE_ACTION, componentName);
+    public CallServiceWrapper(CallServiceInfo info, CallServiceAdapter adapter) {
+        super(CALL_SERVICE_ACTION, info.getServiceComponent());
+        mCallServiceInfo = info;
+        mAdapter = adapter;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * Initializes the underlying call-service implementation upon successful binding.
+     *
+     * {@inheritDoc}
+     */
     @Override public void handleSuccessfulConnection(IBinder binder) {
         mServiceInterface = ICallService.Stub.asInterface(binder);
-        // TODO(santoscordon): Notify CallServiceFinder.
+        setCallServiceAdapter(mAdapter);
     }
 
     /** {@inheritDoc} */
     @Override public void handleFailedConnection() {
-        // TODO(santoscordon): Notify CallServiceFinder.
+        // TODO(santoscordon): fill in
     }
 
     /** {@inheritDoc} */
@@ -67,6 +86,10 @@
         // TODO(santoscordon): fill in.
     }
 
+    public CallServiceInfo getInfo() {
+        return mCallServiceInfo;
+    }
+
     /** See {@link ICallService#setCallServiceAdapter}. */
     public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
         try {
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index c96b8da..d69af53 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -104,7 +104,7 @@
 
         // No objection to issue the call, proceed with trying to put it through.
         Call call = new Call(handle, contactInfo);
-        mSwitchboard.placeOutgoingCall(call, context);
+        mSwitchboard.placeOutgoingCall(call);
     }
 
     /**
@@ -197,7 +197,11 @@
      */
     void markCallAsDisconnected(String callId) {
         setCallState(callId, CallState.DISCONNECTED);
-        mCalls.remove(callId);
+
+        Call call = mCalls.remove(callId);
+        // At this point the call service has confirmed that the call is disconnected to it is
+        // safe to disassociate the call from its call service.
+        call.clearCallService();
 
         // Notify the in-call UI
         mInCallController.markCallAsDisconnected(callId);
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index 5135326..0a2df04 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -25,7 +25,7 @@
 import android.os.RemoteException;
 import android.telecomm.CallInfo;
 import android.telecomm.CallState;
-import android.telecomm.ICallService;
+import android.telecomm.CallServiceInfo;
 import android.telecomm.ICallServiceSelectionResponse;
 import android.telecomm.ICallServiceSelector;
 import android.util.Log;
@@ -37,7 +37,7 @@
 
 /**
  * Utility class to place a call using the specified set of call-services and ordered selectors.
- * Iterates through the selectors and gets a sorted list of supported call service IDs for each
+ * Iterates through the selectors and gets a sorted list of supported call service infos for each
  * selector. Upon receiving each sorted list (one list per selector), each of the corresponding
  * 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.
@@ -57,14 +57,14 @@
     private final Call mCall;
 
     /**
-     * The duplicate-free list of currently-available call-service IDs.
+     * The duplicate-free list of currently-available call-service infos.
      */
-    private final List<String> mCallServiceIds = Lists.newArrayList();
+    private final List<CallServiceInfo> mCallServiceInfos = Lists.newArrayList();
 
     /**
-     * The map of currently-available call-service implementations keyed by call-service ID.
+     * The map of currently-available call-service implementations keyed by call-service infos.
      */
-    private final Map<String, ICallService> mCallServicesById = Maps.newHashMap();
+    private final Map<CallServiceInfo, CallServiceWrapper> mCallServicesByInfo = Maps.newHashMap();
 
     /**
      * The set of currently-available call-service selector implementations.
@@ -80,9 +80,9 @@
     private final Switchboard mSwitchboard;
 
     /**
-     * The iterator over the currently-selected ordered list of call-service IDs.
+     * The iterator over the currently-selected ordered list of call-service infos.
      */
-    private Iterator<String> mCallServiceIdIterator;
+    private Iterator<CallServiceInfo> mCallServiceInfoIterator;
 
     private Iterator<ICallServiceSelector> mSelectorIterator;
 
@@ -90,14 +90,14 @@
      * 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 CallServiceWrapper mCallService;
 
     private boolean mIsAborted = false;
 
     /**
      * Persists the specified parameters and iterates through the prioritized list of selectors
      * 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
+     * service infos.  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 via {@link OutgoingCallsManager}.
      *
@@ -109,7 +109,7 @@
      */
     OutgoingCallProcessor(
             Call call,
-            Set<ICallService> callServices,
+            Set<CallServiceWrapper> callServices,
             List<ICallServiceSelector> selectors,
             OutgoingCallsManager outgoingCallsManager,
             Switchboard switchboard) {
@@ -124,13 +124,12 @@
         mOutgoingCallsManager = outgoingCallsManager;
         mSwitchboard = switchboard;
 
-        // Populate the list and map of call-service IDs.  The list is needed since
+        // Populate the list and map of call-service infos.  The list is needed since
         // it's being passed down to selectors.
-        for (ICallService callService : callServices) {
-            // TOOD(gilad): Implement callService.getId() and use that instead.
-            String id = "xyz";
-            mCallServiceIds.add(id);
-            mCallServicesById.put(id, callService);
+        for (CallServiceWrapper callService : callServices) {
+            CallServiceInfo info = callService.getInfo();
+            mCallServiceInfos.add(info);
+            mCallServicesByInfo.put(info, callService);
         }
     }
 
@@ -140,7 +139,7 @@
     void process() {
         ThreadUtil.checkOnMainThread();
 
-        if (mSelectors.isEmpty() || mCallServiceIds.isEmpty()) {
+        if (mSelectors.isEmpty() || mCallServiceInfos.isEmpty()) {
             // TODO(gilad): Consider adding a failure message/type to differentiate the various
             // cases, or potentially throw an exception in this case.
             mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
@@ -197,7 +196,7 @@
             ICallServiceSelector selector = mSelectorIterator.next();
             ICallServiceSelectionResponse.Stub response = createSelectionResponse();
             try {
-                selector.select(mCall.toCallInfo(), mCallServiceIds, response);
+                selector.select(mCall.toCallInfo(), mCallServiceInfos, response);
             } catch (RemoteException e) {
                 attemptNextSelector();
             }
@@ -212,12 +211,12 @@
      */
     private ICallServiceSelectionResponse.Stub createSelectionResponse() {
         return new ICallServiceSelectionResponse.Stub() {
-            @Override public void setSelectedCallServiceIds(
-                    final List<String> selectedCallServiceIds) {
+            @Override public void setSelectedCallServiceInfos(
+                    final List<CallServiceInfo> selectedCallServiceInfos) {
 
                 Runnable runnable = new Runnable() {
                     @Override public void run() {
-                        processSelectedCallServiceIds(selectedCallServiceIds);
+                        processSelectedCallServiceInfos(selectedCallServiceInfos);
                     }
                 };
 
@@ -227,24 +226,24 @@
     }
 
     /**
-     * Persists the ordered-list of call service IDs as selected by the current selector and
+     * Persists the ordered-list of call-service infos as selected by the current selector and
      * starts iterating through the corresponding call services in the continuing attempt to
      * place the call.
      *
-     * @selectedCallServiceIds The (ordered) list of call service IDs.
+     * @selectedCallServiceInfos The (ordered) list of call service infos.
      */
-    private void processSelectedCallServiceIds(List<String> selectedCallServiceIds) {
-        if (selectedCallServiceIds == null || selectedCallServiceIds.isEmpty()) {
+    private void processSelectedCallServiceInfos(List<CallServiceInfo> selectedCallServiceInfos) {
+        if (selectedCallServiceInfos == null || selectedCallServiceInfos.isEmpty()) {
             attemptNextSelector();
-        } else if (mCallServiceIdIterator == null) {
-            mCallServiceIdIterator = selectedCallServiceIds.iterator();
+        } else if (mCallServiceInfoIterator == null) {
+            mCallServiceInfoIterator = selectedCallServiceInfos.iterator();
             attemptNextCallService();
         }
     }
 
     /**
-     * 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
+     * Attempts to place the call using the call service specified by the next call-service info of
+     * mCallServiceInfoIterator. If there are no more call services to attempt, the process continues
      * to the next call-service selector via {@link #attemptNextSelector}.
      */
     private void attemptNextCallService() {
@@ -252,21 +251,16 @@
             return;
         }
 
-        if (mCallServiceIdIterator.hasNext()) {
-            String id = mCallServiceIdIterator.next();
-            mCallService = mCallServicesById.get(id);
+        if (mCallServiceInfoIterator.hasNext()) {
+            CallServiceInfo info = mCallServiceInfoIterator.next();
+            mCallService = mCallServicesByInfo.get(info);
             if (mCallService == null) {
                 attemptNextCallService();
             } else {
-                try {
-                    mCallService.call(mCall.toCallInfo());
-                } catch (RemoteException e) {
-                    // TODO(gilad): Log etc.
-                    attemptNextCallService();
-                }
+                mCallService.call(mCall.toCallInfo());
             }
         } else {
-            mCallServiceIdIterator = null;
+            mCallServiceInfoIterator = null;
             resetCallService();
             attemptNextSelector();
         }
diff --git a/src/com/android/telecomm/OutgoingCallsManager.java b/src/com/android/telecomm/OutgoingCallsManager.java
index 7eae0fa..247af8c 100644
--- a/src/com/android/telecomm/OutgoingCallsManager.java
+++ b/src/com/android/telecomm/OutgoingCallsManager.java
@@ -16,7 +16,6 @@
 
 package com.android.telecomm;
 
-import android.telecomm.ICallService;
 import android.telecomm.ICallServiceSelector;
 import android.util.Log;
 
@@ -59,7 +58,7 @@
      * @param selectors The ordered list of selectors used in placing the call.
      */
     void placeCall(
-            Call call, Set<ICallService> callServices, List<ICallServiceSelector> selectors) {
+            Call call, Set<CallServiceWrapper> callServices, List<ICallServiceSelector> selectors) {
 
         Log.i(TAG, "Placing an outgoing call (" + call.getId() + ")");
 
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index d4afb76..32ef914 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
-import android.telecomm.ICallService;
 import android.telecomm.ICallServiceSelector;
 
 import java.util.Collection;
@@ -31,9 +30,9 @@
 import java.util.Set;
 
 /**
- * Switchboard is responsible for (1) selecting the {@link ICallService} through which to make
- * outgoing calls and (2) switching active calls between transports (each ICallService is
- * considered a different transport type).
+ * Switchboard is responsible for (1) gathering the {@link CallServiceWrapper}s and
+ * {@link ICallServiceSelector}s through which to place outgoing calls, (2) starting outgoing calls
+ * (via {@link OutgoingCallsManager} and (3) switching active calls between call services.
  */
 final class Switchboard {
 
@@ -48,9 +47,9 @@
     /** Used to place outgoing calls. */
     private final OutgoingCallsManager mOutgoingCallsManager;
 
-    private CallServiceFinder mCallServiceFinder;
+    private final CallServiceRepository mCallServiceRepository;
 
-    private CallServiceSelectorFinder mSelectorFinder;
+    private final CallServiceSelectorRepository mSelectorRepository;
 
     /** Used to schedule tasks on the main (UI) thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -75,66 +74,63 @@
     private final Set<Call> mPendingOutgoingCalls = Sets.newLinkedHashSet();
 
     /**
-     * The set of currently available call-service implementations, see {@link CallServiceFinder}.
-     * TODO(gilad): Null out once the active-call count goes to zero.
+     * The set of currently available call service implementations, see
+     * {@link CallServiceRepository}. Populated after a lookup for call services as part of
+     * {@link #placeCall}. It is cleared periodically when there are no more new or pending outgoing
+     * calls.
      */
-    private Set<ICallService> mCallServices;
+    private Set<CallServiceWrapper> mCallServices;
 
     /**
      * The set of currently available call-service-selector implementations,
-     * see {@link CallServiceSelectorFinder}.
+     * see {@link CallServiceSelectorRepository}.
      * TODO(gilad): Null out once the active-call count goes to zero.
      */
     private Set<ICallServiceSelector> mSelectors;
 
     /**
+     * The current lookup-cycle ID used with the repositories. Incremented with each invocation
+     * of {@link #placeCall} and passed to the repositories via initiateLookup().
+     */
+    private int mLookupId = 0;
+
+    /**
      * Persists the specified parameters and initializes Switchboard.
      */
     Switchboard(CallsManager callsManager) {
         mCallsManager = callsManager;
         mOutgoingCallsManager = new OutgoingCallsManager(this);
-        mCallServiceFinder = new CallServiceFinder(this, mOutgoingCallsManager);
-        mSelectorFinder = new CallServiceSelectorFinder(this);
+        mCallServiceRepository = new CallServiceRepository(this, mOutgoingCallsManager);
+        mSelectorRepository = new CallServiceSelectorRepository(this);
     }
 
     /**
-     * Attempts to place an outgoing call to the specified handle.
+     * Starts the process of placing an outgoing call by searching for available call services
+     * through which the call can be placed. After a lookup for those services completes, execution
+     * returns to {@link #setCallServices} where the process of placing the call continues.
      *
      * @param call The yet-to-be-connected outgoing-call object.
-     * @param context The application context.
      */
-    void placeOutgoingCall(Call call, Context context) {
+    void placeOutgoingCall(Call call) {
         ThreadUtil.checkOnMainThread();
 
-        boolean bailout = false;
-        if (isNullOrEmpty(mCallServices)) {
-            mCallServiceFinder.initiateLookup();
-            bailout = true;
-        }
-        if (isNullOrEmpty(mSelectors)) {
-            mSelectorFinder.initiateLookup(context);
-            bailout = true;
-        }
+        mLookupId++;
 
-        if (bailout) {
-            // Unable to process the call without either call service, selectors, or both.
-            // Store the call for deferred processing and bail out.
-            mNewOutgoingCalls.add(call);
-            return;
-        }
-
-        processNewOutgoingCall(call);
+        // We initialize a lookup every time because between calls the set of available call
+        // services can change between calls.
+        mCallServiceRepository.initiateLookup(mLookupId);
+        mSelectorRepository.initiateLookup(mLookupId);
     }
 
     /**
      * Persists the specified set of call services and attempts to place any pending outgoing
-     * calls.  Intended to be invoked by {@link CallServiceFinder} exclusively.
+     * calls.  Intended to be invoked by {@link CallServiceRepository} exclusively.
      *
      * @param callServices The potentially-partial set of call services.  Partial since the lookup
      *     process is time-boxed, such that some providers/call-services may be slow to respond and
      *     hence effectively omitted from the specified list.
      */
-    void setCallServices(Set<ICallService> callServices) {
+    void setCallServices(Set<CallServiceWrapper> callServices) {
         ThreadUtil.checkOnMainThread();
 
         mCallServices = callServices;
@@ -143,13 +139,15 @@
 
     /**
      * Persists the specified list of selectors and attempts to connect any pending outgoing
-     * calls.  Intended to be invoked by {@link CallServiceSelectorFinder} exclusively.
+     * calls.  Intended to be invoked by {@link CallServiceSelectorRepository} exclusively.
      *
      * @param selectors The potentially-partial set of selectors.  Partial since the lookup
      *     procedure is time-boxed such that some selectors may be slow to respond and hence
      *     effectively omitted from the specified set.
      */
     void setSelectors(Set<ICallServiceSelector> selectors) {
+        // TODO(santoscordon): This should take in CallServiceSelectorWrapper instead of the direct
+        // ICallServiceSelector implementation. Copy what we have for CallServiceWrapper.
         ThreadUtil.checkOnMainThread();
 
         // TODO(gilad): Add logic to include the built-in selectors (e.g. for dealing with
@@ -207,6 +205,8 @@
      */
     private void tick() {
         // TODO(gilad): More here.
+        // TODO(santoscordon): Clear mCallServices if there exist no more new or pending outgoing
+        // calls.
     }
 
     /**
@@ -235,7 +235,6 @@
      * @param call The call to place.
      */
     private void processNewOutgoingCall(Call call) {
-
         Preconditions.checkNotNull(mCallServices);
         Preconditions.checkNotNull(mSelectors);