Work in preparation for wiring up the remaining de-allocation and
abort bits.
1) limit to one attempt per call service upon multiple selectors
returning the same call service
2) record the failing/incompatible ones such that these are avoided
when switching
3) addressing some related todos etc.

Change-Id: I62204e9947bb8557888df33ca70f4352d3e6decf
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index bcfda61..a242866 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -16,10 +16,6 @@
 
 package com.android.telecomm;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Lists;
-
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -28,7 +24,9 @@
 
 import com.android.internal.telecomm.ICallServiceSelectionResponse;
 import com.android.internal.telecomm.ICallServiceSelector;
-import com.android.telecomm.ServiceBinder.BindCallback;
+import com.google.android.collect.Sets;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Lists;
 
 import java.util.Iterator;
 import java.util.List;
@@ -68,6 +66,17 @@
     private final Map<String, CallServiceWrapper> mCallServicesById = Maps.newHashMap();
 
     /**
+     * The set of attempted call services, used to ensure services are attempted at most once per
+     * outgoing-call attempt.
+     */
+    private final Set<CallServiceWrapper> mAttemptedCallServices = Sets.newHashSet();
+
+    /**
+     * The set of incompatible call services, used to suppress unnecessary call switching attempts.
+     */
+    private final Set<CallServiceWrapper> mIncompatibleCallServices = Sets.newHashSet();
+
+    /**
      * The list of currently-available call-service selector implementations.
      */
     private final List<ICallServiceSelector> mSelectors;
@@ -80,6 +89,12 @@
 
     private final Switchboard mSwitchboard;
 
+    private final Runnable mNextCallServiceCallback = new Runnable() {
+        @Override public void run() {
+            attemptNextCallService();
+        }
+    };
+
     /**
      * The iterator over the currently-selected ordered list of call-service descriptors.
      */
@@ -141,6 +156,34 @@
     }
 
     /**
+     * Handles the specified compatibility status from the call-service implementation.
+     * TODO(gilad): Consider making this class stateful, potentially rejecting out-of-order/
+     * unexpected invocations (i.e. beyond checking for unexpected call IDs).
+     *
+     * @param callId The ID of the call.
+     * @param isCompatible True if the call-service is compatible with the corresponding call and
+     *     false otherwise.
+     */
+    void setIsCompatibleWith(String callId, boolean isCompatible) {
+      if (callId != mCall.getId()) {
+          Log.wtf(this, "setIsCompatibleWith invoked with unexpected call ID: %s", callId);
+          return;
+      }
+
+      if (!mIsAborted) {
+          CallServiceWrapper callService = mCall.getCallService();
+          if (callService != null) {
+              if (isCompatible) {
+                  callService.call(mCall.toCallInfo(), mNextCallServiceCallback);
+                  return;
+              }
+              mIncompatibleCallServices.add(callService);
+          }
+          attemptNextCallService();
+      }
+    }
+
+    /**
      * Aborts the attempt to place the relevant call.  Intended to be invoked by
      * switchboard through the outgoing-calls manager.
      */
@@ -166,6 +209,9 @@
         }
 
         mCall.setState(CallState.DIALING);
+        for (CallServiceWrapper callService : mIncompatibleCallServices) {
+            mCall.addIncompatibleCallService(callService);
+        }
         mSwitchboard.handleSuccessfulOutgoingCall(mCall);
     }
 
@@ -263,23 +309,14 @@
             final CallServiceWrapper callService =
                     mCallServicesById.get(descriptor.getCallServiceId());
 
-            if (callService == null) {
+            if (callService == null || mAttemptedCallServices.contains(callService)) {
+                // The next call service is either null or has already been attempted, fast forward
+                // to the next.
                 attemptNextCallService();
             } else {
-                BindCallback callback = new BindCallback() {
-                    @Override public void onSuccess() {
-                        callService.call(mCall.toCallInfo());
-                    }
-                    @Override public void onFailure() {
-                        attemptNextCallService();
-                    }
-                };
-
+                mAttemptedCallServices.add(callService);
                 mCall.setCallService(callService);
-
-                // TODO(santoscordon): Consider making bind private to CallServiceWrapper and having
-                // CSWrapper.call() do the bind automatically.
-                callService.bind(callback);
+                callService.isCompatibleWith(mCall.toCallInfo(), mNextCallServiceCallback);
             }
         } else {
             mCallServiceDescriptorIterator = null;