Updating the outgoing call sequence.

- Removes OutgoingCallsManager
- Call now invokes it's own OutgoingCallProcessor (OCP) directly.
- Results for select(), call(), and OCP.process() now happen through
  new AsyncResultCallback closure objects specified at the time
  the methods are invoked.
- Move remaining outgoing call responsibilities in Switchboard to
  OCP including expiration and CS/selector discovery.
- Remove isCompatibleWith sequence

Change-Id: Ie63c7900dc7a324d0c56e0767ed19b2ef0868528
diff --git a/src/com/android/telecomm/CallServiceWrapper.java b/src/com/android/telecomm/CallServiceWrapper.java
index a4e3d24..4040e33 100644
--- a/src/com/android/telecomm/CallServiceWrapper.java
+++ b/src/com/android/telecomm/CallServiceWrapper.java
@@ -36,6 +36,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -46,7 +48,6 @@
 final class CallServiceWrapper extends ServiceBinder<ICallService> {
 
     private final class Adapter extends ICallServiceAdapter.Stub {
-        private static final int MSG_SET_IS_COMPATIBLE_WITH = 0;
         private static final int MSG_NOTIFY_INCOMING_CALL = 1;
         private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
         private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
@@ -61,20 +62,11 @@
             public void handleMessage(Message msg) {
                 Call call;
                 switch (msg.what) {
-                    case MSG_SET_IS_COMPATIBLE_WITH:
-                        call = mCallIdMapper.getCall(msg.obj);
-                        if (call != null && !call.isIncoming()) {
-                            mOutgoingCallsManager.setIsCompatibleWith(call,
-                                    msg.arg1 == 1 ? true : false);
-                        } else {
-                            Log.w(this, "setIsCompatibleWith, unknown call: %s, id: %s", call,
-                                    msg.obj);
-                        }
-                        break;
                     case MSG_NOTIFY_INCOMING_CALL:
                         CallInfo clientCallInfo = (CallInfo) msg.obj;
                         call = mCallIdMapper.getCall(clientCallInfo.getId());
-                        if (call != null && mPendingCalls.remove(call) && call.isIncoming()) {
+                        if (call != null && mPendingIncomingCalls.remove(call) &&
+                                call.isIncoming()) {
                             CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
                                     clientCallInfo.getHandle());
                             mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
@@ -84,28 +76,27 @@
                                     clientCallInfo.getId());
                         }
                         break;
-                    case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL:
-                        call = mCallIdMapper.getCall(msg.obj);
-                        if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
-                            mOutgoingCallsManager.handleSuccessfulCallAttempt(call);
+                    case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
+                        String callId = (String) msg.obj;
+                        if (mPendingOutgoingCalls.containsKey(callId)) {
+                            mPendingOutgoingCalls.remove(callId).onResult(true);
                         } else {
-                            // TODO(gilad): Figure out how to wire up the callService.abort() call.
-                            Log.w(this,
-                                    "handleSuccessfulOutgoingCall, unknown call: %s, id: %s",
-                                    call, msg.obj);
+                            Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
                         }
                         break;
+                    }
                     case MSG_HANDLE_FAILED_OUTGOING_CALL: {
                         SomeArgs args = (SomeArgs) msg.obj;
                         try {
-                            call = mCallIdMapper.getCall(args.arg1);
+                            String callId = (String) args.arg1;
                             String reason = (String) args.arg2;
-                            if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
-                                mOutgoingCallsManager.handleFailedCallAttempt(call, reason);
+                            // TODO(santoscordon): Do something with 'reason' or get rid of it.
+
+                            if (mPendingOutgoingCalls.containsKey(callId)) {
+                                mPendingOutgoingCalls.remove(callId).onResult(false);
+                                mCallIdMapper.removeCall(callId);
                             } else {
-                                Log.w(this,
-                                        "handleFailedOutgoingCall, unknown call: %s, id: %s",
-                                        call, args.arg1);
+                                Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
                             }
                         } finally {
                             args.recycle();
@@ -168,10 +159,7 @@
         /** {@inheritDoc} */
         @Override
         public void setIsCompatibleWith(String callId, boolean isCompatible) {
-            Log.v(this, "setIsCompatibleWith id: %s, isCompatible: %b", callId, isCompatible);
-            mCallIdMapper.checkValidCallId(callId);
-            mHandler.obtainMessage(MSG_SET_IS_COMPATIBLE_WITH, isCompatible ? 1 : 0, 0, callId).
-                    sendToTarget();
+            Log.wtf(this, "Not expected.");
         }
 
         /** {@inheritDoc} */
@@ -184,6 +172,7 @@
         /** {@inheritDoc} */
         @Override
         public void handleSuccessfulOutgoingCall(String callId) {
+            Log.d(this, "handleSuccessfulOutgoingCall %s", callId);
             mCallIdMapper.checkValidCallId(callId);
             mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
         }
@@ -192,6 +181,7 @@
         @Override
         public void handleFailedOutgoingCall(String callId, String reason) {
             mCallIdMapper.checkValidCallId(callId);
+            Log.d(this, "handleFailedOutgoingCall %d", callId);
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = callId;
             args.arg2 = reason;
@@ -241,11 +231,11 @@
 
     private final Adapter mAdapter = new Adapter();
     private final CallsManager mCallsManager = CallsManager.getInstance();
-    private final Set<Call> mPendingCalls = Sets.newHashSet();
+    private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
     private final CallServiceDescriptor mDescriptor;
     private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
-    private final OutgoingCallsManager mOutgoingCallsManager;
     private final IncomingCallsManager mIncomingCallsManager;
+    private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
 
     private Binder mBinder = new Binder();
     private ICallService mServiceInterface;
@@ -255,17 +245,14 @@
      *
      * @param descriptor The call-service descriptor from
      *            {@link ICallServiceProvider#lookupCallServices}.
-     * @param outgoingCallsManager Manages the placing of outgoing calls.
      * @param incomingCallsManager Manages the incoming call initialization flow.
      */
     CallServiceWrapper(
             CallServiceDescriptor descriptor,
-            OutgoingCallsManager outgoingCallsManager,
             IncomingCallsManager incomingCallsManager) {
         super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
         mDescriptor = descriptor;
         mIncomingCallsManager = incomingCallsManager;
-        mOutgoingCallsManager = outgoingCallsManager;
     }
 
     CallServiceDescriptor getDescriptor() {
@@ -283,60 +270,48 @@
     }
 
     /**
-     * Checks whether or not the specified call is compatible with this call-service implementation,
-     * see {@link ICallService#isCompatibleWith}. Upon failure, the specified error callback is
-     * invoked. Can be invoked even when the call service is unbound.
-     *
-     * @param errorCallback The callback to invoke upon failure.
+     * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
+     * asynchronously through the specified callback.
      */
-    void isCompatibleWith(final Call call, final Runnable errorCallback) {
-        Log.d(this, "isCompatibleWith(%s) via %s.", call, getComponentName());
+    void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
+        Log.d(this, "call(%s) via %s.", call, getComponentName());
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
-                if (isServiceValid("isCompatibleWith")) {
-                    try {
-                        mPendingCalls.add(call);
-                        CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
-                        mServiceInterface.isCompatibleWith(callInfo);
-                    } catch (RemoteException e) {
-                    }
+                String callId = mCallIdMapper.getCallId(call);
+                mPendingOutgoingCalls.put(callId, resultCallback);
+
+                try {
+                    CallInfo callInfo = call.toCallInfo(callId);
+                    mServiceInterface.call(callInfo);
+                } catch (RemoteException e) {
+                    mPendingOutgoingCalls.remove(callId).onResult(false);
                 }
             }
 
             @Override
             public void onFailure() {
-                errorCallback.run();
+                resultCallback.onResult(false);
             }
         };
 
         mBinder.bind(callback);
     }
 
-    /**
-     * Attempts to place the specified call, see {@link ICallService#call}. Upon failure, the
-     * specified error callback is invoked. Can be invoked even when the call service is unbound.
-     */
-    void call(Call call) {
-        Log.d(this, "call(%s) via %s.", call, getComponentName());
-        if (isServiceValid("call")) {
-            try {
-                CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
-                mServiceInterface.call(callInfo);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
     /** @see CallService#abort(String) */
     void abort(Call call) {
-        mPendingCalls.remove(call);
+        // Clear out any pending outgoing call data
+        String callId = mCallIdMapper.getCallId(call);
+
+        // If still bound, tell the call service to abort.
         if (isServiceValid("abort")) {
             try {
-                mServiceInterface.abort(mCallIdMapper.getCallId(call));
+                mServiceInterface.abort(callId);
             } catch (RemoteException e) {
             }
         }
+
+        removeCall(call);
     }
 
     /** @see CallService#hold(String) */
@@ -386,7 +361,7 @@
             @Override
             public void onSuccess() {
                 if (isServiceValid("setIncomingCallId")) {
-                    mPendingCalls.add(call);
+                    mPendingIncomingCalls.add(call);
                     try {
                         mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
                                 extras);
@@ -467,7 +442,14 @@
     }
 
     void removeCall(Call call) {
-        mPendingCalls.remove(call);
+        mPendingIncomingCalls.remove(call);
+
+        AsyncResultCallback<Boolean> outgoingResultCallback =
+            mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
+        if (outgoingResultCallback != null) {
+            outgoingResultCallback.onResult(false);
+        }
+
         mCallIdMapper.removeCall(call);
     }
 
@@ -492,22 +474,27 @@
      * Called when the associated call service dies.
      */
     private void handleCallServiceDeath() {
-        if (!mPendingCalls.isEmpty()) {
+        if (!mPendingOutgoingCalls.isEmpty()) {
+            for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
+                callback.onResult(false);
+            }
+            mPendingOutgoingCalls.clear();
+        }
+
+        if (!mPendingIncomingCalls.isEmpty()) {
             // Iterate through a copy because the code inside the loop will modify the original
             // list.
-            for (Call call : ImmutableList.copyOf(mPendingCalls)) {
-                if (call.isIncoming()) {
-                    mIncomingCallsManager.handleFailedIncomingCall(call);
-                } else {
-                    mOutgoingCallsManager.handleFailedCallAttempt(call,
-                            "Call service disconnected.");
-                }
+            for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
+                Preconditions.checkState(call.isIncoming());
+                mIncomingCallsManager.handleFailedIncomingCall(call);
             }
 
-            if (!mPendingCalls.isEmpty()) {
+            if (!mPendingIncomingCalls.isEmpty()) {
                 Log.wtf(this, "Pending calls did not get cleared.");
-                mPendingCalls.clear();
+                mPendingIncomingCalls.clear();
             }
         }
+
+        mCallIdMapper.clear();
     }
 }