Continuing the outgoing call flow -- passing call services from the finder back to the switchboard.

Change-Id: I613e8e77af02b645c7bffc6661c5b3c3e07278dc
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 4f1028d..3511b7e 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -1,4 +1,57 @@
+/*
+ * 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;
 
-public class Call {
+import java.util.Date;
+
+final class Call {
+
+    /** The handle with which to establish this call. */
+    private String mHandle;
+
+    /** Additional contact information beyond handle above, optional. */
+    private ContactInfo mContactInfo;
+
+    /**
+     * The time this call was created, typically also the time this call was added to the set
+     * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
+     * Beyond logging and such, may also be used for bookkeeping and specifically for marking
+     * certain call attempts as failed attempts.
+     */
+    private final Date mCreationTime;
+
+    /**
+     * Persists the specified parameters and initializes the new instance.
+     *
+     * @param handle The handle to dial.
+     * @param contactInfo Information about the entity being called.
+     */
+    public Call(String handle, ContactInfo contactInfo) {
+        mHandle = handle;
+        mContactInfo = contactInfo;
+
+        mCreationTime = new Date();
+    }
+
+    /**
+     * @return The "age" of this call object in milliseconds, which typically also represents the
+     *     period since this call was added to the set pending outgoing calls, see mCreationTime.
+     */
+    public long getAgeInMilliseconds() {
+        return new Date().getTime() - mCreationTime.getTime();
+    }
 }
diff --git a/src/com/android/telecomm/CallServiceFinder.java b/src/com/android/telecomm/CallServiceFinder.java
index 360c503..20c7ee8 100644
--- a/src/com/android/telecomm/CallServiceFinder.java
+++ b/src/com/android/telecomm/CallServiceFinder.java
@@ -159,6 +159,8 @@
      */
     static final String CALL_SERVICE_PROVIDER_CLASS_NAME = ICallServiceProvider.class.getName();
 
+    private final Switchboard mSwitchboard;
+
     /**
      * Determines whether or not a lookup cycle is already running.
      */
@@ -194,12 +196,21 @@
     private Timer mTimer;
 
     /**
+     * Persists the specified parameters.
+     *
+     * @param switchboard The switchboard for this finer to work against.
+     */
+    CallServiceFinder(Switchboard switchboard) {
+        mSwitchboard = switchboard;
+    }
+
+    /**
      * Initiates a lookup cycle for call-service providers.
      * TODO(gilad): Expand this comment to describe the lookup flow in more detail.
      *
      * @param context The relevant application context.
      */
-    public synchronized void initiateLookup(Context context) {
+    synchronized void initiateLookup(Context context) {
         if (mIsLookupInProgress) {
             // At most one active lookup is allowed at any given time, bail out.
             return;
@@ -320,6 +331,7 @@
     private void updateSwitchboard() {
         synchronized (mProviderRegistry) {
             // TODO(gilad): More here.
+            mSwitchboard.setCallServices(null);
         }
     }
 }
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index b33df50..060b5c1 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -17,6 +17,7 @@
 package com.android.telecomm;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 import android.content.Context;
 import android.os.RemoteException;
@@ -27,34 +28,69 @@
 import com.android.telecomm.exceptions.OutgoingCallException;
 
 import java.util.List;
+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).
  * TODO(santoscordon): Need to add comments on the switchboard optimizer once that it is place.
+ * TODO(gilad): Add a monitor thread to wake up periodically and check for stale pending calls
+ *     that may need to be terminated, see mPendingOutgoingCalls.
  */
 final class Switchboard {
     /** Used to identify log entries by this class */
     private static final String TAG = Switchboard.class.getSimpleName();
 
-    private CallServiceFinder callServiceFinder = new CallServiceFinder();
+    private CallServiceFinder callServiceFinder = new CallServiceFinder(this);
+
+    private Set<Call> mPendingOutgoingCalls = Sets.newHashSet();
 
     /**
      * 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.
      *
-     * @param handle The handle to dial.  Marked as final so it can be used in the inner class.
-     * @param contactInfo Information about the entity being called. Marked as final so it can
-     *     be used in the inner class.
+     * @param handle The handle to dial.
+     * @param contactInfo Information about the entity being called.
      * @param context The application context.
      */
     void placeOutgoingCall(String handle, ContactInfo contactInfo, Context context) {
+        synchronized (mPendingOutgoingCalls) {
+            mPendingOutgoingCalls.add(new Call(handle, contactInfo));
+        }
         callServiceFinder.initiateLookup(context);
+    }
 
-        // TODO(gilad): Persist the necessary parameters to attempt putting the call through
-        // once the call services become available (likely using some sort of closure).
+    /**
+     * 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.
+     *
+     * @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.
+     */
+    void setCallServices(List<ICallService> callServices) {
+        synchronized (mPendingOutgoingCalls) {
+            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
+            }
+        }
     }
 
     /**