Adding the lookup logic for call-service selectors.

Change-Id: If18628f9b4854b8055ec43d8e33c0c6f822454cd
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index e9f9033..4abebce 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -22,11 +22,13 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.telecomm.ICallService;
+import android.telecomm.ICallServiceSelector;
 import android.util.Log;
 
 import com.android.telecomm.exceptions.CallServiceUnavailableException;
 import com.android.telecomm.exceptions.OutgoingCallException;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
@@ -42,7 +44,17 @@
 
     private static final String TAG = Switchboard.class.getSimpleName();
 
-    private CallServiceFinder callServiceFinder = new CallServiceFinder(this);
+    private CallServiceFinder mCallServiceFinder = new CallServiceFinder(this);
+
+    private CallServiceSelectorFinder mSelectorFinder = new CallServiceSelectorFinder(this);
+
+    /** TODO(gilad): Add comment, may also want to use a set instead. */
+    /** TODO(gilad): Null out once the active-call count goes to zero. */
+    private List<ICallService> mCallServices;
+
+    /** TODO(gilad): Add comment, may also want to use a set instead. */
+    /** TODO(gilad): Null out once the active-call count goes to zero. */
+    private List<ICallServiceSelector> mSelectors;
 
     private Set<Call> mPendingOutgoingCalls = Sets.newHashSet();
 
@@ -50,6 +62,7 @@
      * Places an outgoing call to the handle passed in. Method asynchronously collects
      * {@link ICallService} implementations and passes them along with the handle and contactInfo
      * to {@link #placeOutgoingCallInternal} to actually place the call.
+     * TODO(gilad): Update.
      *
      * @param handle The handle to dial.
      * @param contactInfo Information about the entity being called.
@@ -57,41 +70,109 @@
      */
     void placeOutgoingCall(String handle, ContactInfo contactInfo, Context context) {
         ThreadUtil.checkOnMainThread();
-        mPendingOutgoingCalls.add(new Call(handle, contactInfo));
-        callServiceFinder.initiateLookup(context);
+
+        // TODO(gilad): Consider creating the call object even earlier, e.g. in CallsManager.
+        Call call = new Call(handle, contactInfo);
+        boolean bailout = false;
+        if (isNullOrEmpty(mCallServices)) {
+            mCallServiceFinder.initiateLookup(context);
+            bailout = true;
+        }
+        if (isNullOrEmpty(mSelectors)) {
+            mSelectorFinder.initiateLookup(context);
+            bailout = true;
+        }
+
+        if (bailout) {
+            // Unable to process the call without either call service, selectors, or both.
+            // Store the call for deferred processing and bail out.
+            mPendingOutgoingCalls.add(call);
+            return;
+        }
+
+        placeOutgoingCall(call);
     }
 
     /**
      * Persists the specified list of call services and attempts to connect any pending outgoing
-     * calls still waiting for a matching call-service to be initiated.
+     * calls still waiting for a matching call-service to be initiated. Intended to be called by
+     * {@link CallServiceFinder} exclusively.
      *
-     * @param callServices The potentially-partial list of call services the switchboard should
-     *     feel free to make use of.  Partial since the lookup procedure is time-boxed such that
-     *     some providers/call-services may be too slow to respond and hence effectively omitted
-     *     from the specified list.  If the switchboard has previous/reliable knowledge of other
-     *     call-services, it should be at liberty to use these just as well.
+     * @param callServices The potentially-partial list of call services.  Partial since the
+     *     lookup procedure is time-boxed, such that some providers/call-services may be slow
+     *     to respond and hence effectively omitted from the specified list.
      */
     void setCallServices(List<ICallService> callServices) {
         ThreadUtil.checkOnMainThread();
-        for (Call pendingCall : mPendingOutgoingCalls) {
-            // TODO(gilad): Iterate through the prioritized list of switchboard policies passing
-            // to each policy the call object as well as all known call services.  Break out of
-            // the inner/policy loop as soon as the first matching policy for the call is found.
-            // Calls for which no matching policy can be found will be killed by cleanup/monitor
-            // thread, see the "monitor" to-do at the top of the file.
 
-            // Psuedo code (assuming connect to be a future switchboard method):
-            //
-            //   FOR policy IN prioritizedPolicies:
-            //     IF policy.is_applicable_to(pendingCall, callServices):
-            //       TRY
-            //         connect(pendingCall, callServices, policy)
-            //         mPendingOutgoingCalls.remove(pendingCall)
-            //         BREAK
+        mCallServices = callServices;
+        processPendingOutgoingCalls();
+    }
+
+    /**
+     * Persists the specified list of selectors and attempts to connect any pending outgoing
+     * calls.  Intended to be called by {@link CallServiceSelectorFinder} exclusively.
+     *
+     * @param selectors The potentially-partial list 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 list.
+     */
+    void setSelectors(List<ICallServiceSelector> selectors) {
+        ThreadUtil.checkOnMainThread();
+
+        mSelectors = selectors;
+        processPendingOutgoingCalls();
+    }
+
+    /**
+     * Attempts to process any pending outgoing calls that have not yet been expired.
+     */
+    void processPendingOutgoingCalls() {
+        for (Call call : mPendingOutgoingCalls) {
+            placeOutgoingCall(call);
         }
     }
 
     /**
+     * Attempts to place the specified call.
+     *
+     * @param call The call to put through.
+     */
+    private void placeOutgoingCall(Call call) {
+        if (isNullOrEmpty(mCallServices) || isNullOrEmpty(mSelectors)) {
+            // At least one call service and one selector are required to process outgoing calls.
+            return;
+        }
+
+        // TODO(gilad): Iterate through the prioritized list of selectors passing to each selector
+        // (read-only versions of) the call object and all available call services.  Break out once
+        // a matching selector is found. Calls with no matching selectors will eventually be killed
+        // by the cleanup/monitor thread, see the "monitor" to-do at the top of the file.
+
+        // Psuedo code (assuming connect to be a future switchboard method):
+        //
+        //   FOR selector IN prioritizedSelectors:
+        //     prioritizedCallServices = selector.select(mCallServices, call)
+        //     IF notEmpty(prioritizedCallServices):
+        //       FOR callService IN prioritizedCallServices:
+        //         TRY
+        //           connect(call, callService, selector)
+        //           mPendingOutgoingCalls.remove(call)
+        //           BREAK
+    }
+
+    /**
+     * Determines whether or not the specified collection is either null or empty.
+     *
+     * @param collection Either null or the collection object to be evaluated.
+     * @return True if the collection is null or empty.
+     */
+    @SuppressWarnings("rawtypes")
+    private boolean isNullOrEmpty(Collection collection) {
+        return collection == null || collection.isEmpty();
+    }
+
+    /**
      * Places an outgoing call to the handle passed in. Given a list of {@link ICallServices},
      * select one and place a call to the handle.
      * TODO(santoscordon): How does the CallService selection process work?
@@ -156,16 +237,4 @@
 //            // RemoteExceptions also be converted to OutgoingCallExceptions thrown by call()?
 //        }
 //    }
-
-    /**
-     * Sorts a list of {@link ICallService} ordered by the preferred service for dialing the call.
-     *
-     * @param callServices The list to order.
-     */
-    private List<ICallService> sort(List<ICallService> callServices) {
-        // TODO(android-contacts): Sort by reliability, cost, and ultimately
-        // the desirability to issue a given call over each of the specified
-        // call services.
-        return callServices;
-    }
 }