Combining Wrappers and adapters.

Cleaner code this way with fewer indirections. Also maps better to new
Telecomm design.

Change-Id: I88cf8f6382f307a7a2ce86e8acdd614283d1d0a5
diff --git a/src/com/android/telecomm/CallIdMapper.java b/src/com/android/telecomm/CallIdMapper.java
index a5d70bf..7044d89 100644
--- a/src/com/android/telecomm/CallIdMapper.java
+++ b/src/com/android/telecomm/CallIdMapper.java
@@ -65,7 +65,7 @@
         if (objId instanceof String) {
             callId = (String) objId;
         }
-        Preconditions.checkArgument(isValidCallId(callId));
+        checkValidCallId(callId);
 
         return mCalls.get(callId);
     }
@@ -73,8 +73,8 @@
     void checkValidCallId(String callId) {
         // Note, no need for thread check, this method is thread safe.
         if (!isValidCallId(callId)) {
-            Log.wtf(this, "%s is not a valid call ID", callId);
-            throw new IllegalArgumentException("Invalid call ID.");
+            throw new IllegalArgumentException(
+                    "Invalid call ID for " + mCallIdPrefix + ": " + callId);
         }
     }
 
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
deleted file mode 100644
index 39de235..0000000
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * 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;
-
-import android.os.Handler;
-import android.os.Message;
-import android.telecomm.CallInfo;
-
-import com.android.internal.telecomm.ICallServiceAdapter;
-import com.google.android.collect.Sets;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.android.internal.os.SomeArgs;
-
-import java.util.Set;
-
-/**
- * Used by call services to communicate with Telecomm. Each call service is given its own instance
- * of the adapter for the lifetmie of the binding.
- * TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity() in the service methods?
- */
-public final class CallServiceAdapter 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;
-    private static final int MSG_SET_ACTIVE = 4;
-    private static final int MSG_SET_RINGING = 5;
-    private static final int MSG_SET_DIALING = 6;
-    private static final int MSG_SET_DISCONNECTED = 7;
-    private static final int MSG_SET_ON_HOLD = 8;
-
-    private final class CallServiceAdapterHandler extends Handler {
-        @Override
-        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()) {
-                        CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
-                                clientCallInfo.getHandle());
-                        mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
-                    } else {
-                        Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s", call,
-                                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);
-                    } else {
-                        // TODO(gilad): Figure out how to wire up the callService.abort() call.
-                        Log.w(this,
-                                "handleSuccessfulOutgoingCall, unknown outgoing call: %s, id: %s",
-                                call, msg.obj);
-                    }
-                    break;
-                case MSG_HANDLE_FAILED_OUTGOING_CALL: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        call = mCallIdMapper.getCall(args.arg1);
-                        String reason = (String) args.arg2;
-                        if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
-                            mOutgoingCallsManager.handleFailedCallAttempt(call, reason);
-                        } else {
-                            Log.w(this,
-                                    "handleFailedOutgoingCall, unknown outgoing call: %s, id: %s",
-                                    call, args.arg1);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_SET_ACTIVE:
-                    call = mCallIdMapper.getCall(msg.obj);
-                    if (call != null) {
-                        mCallsManager.markCallAsActive(call);
-                    } else {
-                        Log.w(this, "setActive, unknown call id: %s", msg.obj);
-                    }
-                    break;
-                case MSG_SET_RINGING:
-                    call = mCallIdMapper.getCall(msg.obj);
-                    if (call != null) {
-                        mCallsManager.markCallAsRinging(call);
-                    } else {
-                        Log.w(this, "setRinging, unknown call id: %s", msg.obj);
-                    }
-                    break;
-                case MSG_SET_DIALING:
-                    call = mCallIdMapper.getCall(msg.obj);
-                    if (call != null) {
-                        mCallsManager.markCallAsDialing(call);
-                    } else {
-                        Log.w(this, "setDialing, unknown call id: %s", msg.obj);
-                    }
-                    break;
-                case MSG_SET_DISCONNECTED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        call = mCallIdMapper.getCall(args.arg1);
-                        String disconnectMessage = (String) args.arg2;
-                        int disconnectCause = args.argi1;
-                        if (call != null) {
-                            mCallsManager.markCallAsDisconnected(call, disconnectCause,
-                                    disconnectMessage);
-                        } else {
-                            Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_SET_ON_HOLD:
-                    call = mCallIdMapper.getCall(msg.obj);
-                    if (call != null) {
-                        mCallsManager.markCallAsOnHold(call);
-                    } else {
-                        Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
-                    }
-                    break;
-            }
-        }
-    }
-
-    private final CallsManager mCallsManager;
-    private final OutgoingCallsManager mOutgoingCallsManager;
-    private final IncomingCallsManager mIncomingCallsManager;
-    private final Handler mHandler = new CallServiceAdapterHandler();
-    private final CallIdMapper mCallIdMapper;
-    private final Set<Call> mPendingCalls = Sets.newHashSet();
-
-    /**
-     * Persists the specified parameters.
-     *
-     * @param outgoingCallsManager Manages the placing of outgoing calls.
-     * @param incomingCallsManager Manages the incoming call initialization flow.
-     */
-    CallServiceAdapter(
-            OutgoingCallsManager outgoingCallsManager,
-            IncomingCallsManager incomingCallsManager,
-            CallIdMapper callIdMapper) {
-        ThreadUtil.checkOnMainThread();
-        mCallsManager = CallsManager.getInstance();
-        mOutgoingCallsManager = outgoingCallsManager;
-        mIncomingCallsManager = incomingCallsManager;
-        mCallIdMapper = callIdMapper;
-    }
-
-    /** {@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();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void notifyIncomingCall(CallInfo callInfo) {
-        mCallIdMapper.checkValidCallId(callInfo.getId());
-        mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void handleSuccessfulOutgoingCall(String callId) {
-        mCallIdMapper.checkValidCallId(callId);
-        mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void handleFailedOutgoingCall(String callId, String reason) {
-        mCallIdMapper.checkValidCallId(callId);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = callId;
-        args.arg2 = reason;
-        mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setActive(String callId) {
-        mCallIdMapper.checkValidCallId(callId);
-        mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setRinging(String callId) {
-        mCallIdMapper.checkValidCallId(callId);
-        mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setDialing(String callId) {
-        mCallIdMapper.checkValidCallId(callId);
-        mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setDisconnected(
-            String callId, int disconnectCause, String disconnectMessage) {
-        mCallIdMapper.checkValidCallId(callId);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = callId;
-        args.arg2 = disconnectMessage;
-        args.argi1 = disconnectCause;
-        mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setOnHold(String callId) {
-        mCallIdMapper.checkValidCallId(callId);
-        mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
-    }
-
-    /**
-     * Adds the specified call to the list of pending calls. Only calls in this list which are
-     * outgoing will be handled by {@link #isCompatibleWith}, {@link handleSuccessfulOutgoingCall},
-     * and {@link handleFailedOutgoingCall}. Similarly, only calls in this list which are incoming
-     * will be handled by {@link notifyIncomingCall}.
-     */
-    void addPendingCall(Call call) {
-        mPendingCalls.add(call);
-    }
-
-    /**
-     * Removes the specified call from the list of pending calls.
-     */
-    void removePendingCall(Call call) {
-        mPendingCalls.remove(call);
-    }
-
-    /**
-     * Called when the associated call service dies.
-     */
-    void handleCallServiceDeath() {
-        if (!mPendingCalls.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.");
-                }
-            }
-
-            if (!mPendingCalls.isEmpty()) {
-                Log.wtf(this, "Pending calls did not get cleared.");
-                mPendingCalls.clear();
-            }
-        }
-    }
-}
diff --git a/src/com/android/telecomm/CallServiceSelectorAdapter.java b/src/com/android/telecomm/CallServiceSelectorAdapter.java
deleted file mode 100644
index 3fa3b10..0000000
--- a/src/com/android/telecomm/CallServiceSelectorAdapter.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.telecomm.CallServiceDescriptor;
-
-import com.android.internal.telecomm.ICallServiceSelectorAdapter;
-import com.android.internal.os.SomeArgs;
-
-import java.util.List;
-
-/**
- * Used by call service selector to communicate with Telecomm.
- */
-public final class CallServiceSelectorAdapter extends ICallServiceSelectorAdapter.Stub {
-    private static final int MSG_SET_SELECTED_CALL_SERVICES = 0;
-    private static final int MSG_CANCEL_OUTGOING_CALL = 1;
-    private static final int MSG_SET_HANDOFF_INFO = 2;
-
-    private final class CallServiceSelectorAdapterHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SET_SELECTED_CALL_SERVICES: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        Call call = mCallIdMapper.getCall(args.arg1);
-                        List<CallServiceDescriptor> descriptors =
-                                (List<CallServiceDescriptor>) args.arg2;
-                        if (call != null) {
-                            mOutgoingCallsManager.processSelectedCallServices(call, descriptors);
-                        } else {
-                            Log.w(this, "setSelectedCallServices: unknown call: %s, id: %s",
-                                    call, args.arg1);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_CANCEL_OUTGOING_CALL: {
-                    Call call = mCallIdMapper.getCall(msg.obj);
-                    if (call != null) {
-                        mOutgoingCallsManager.abort(call);
-                    } else {
-                        Log.w(this, "cancelOutgoingCall: unknown call: %s, id: %s", call, msg.obj);
-                    }
-                    break;
-                }
-                case MSG_SET_HANDOFF_INFO: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        Call call = mCallIdMapper.getCall(args.arg1);
-                        Uri handle = (Uri) args.arg2;
-                        Bundle extras = (Bundle) args.arg3;
-                        if (call != null) {
-                            mCallsManager.setHandoffInfo(call, handle, extras);
-                        } else {
-                            Log.w(this, "setHandoffInfo: unknown call: %s, id: %s",
-                                    call, args.arg1);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
-    private final Handler mHandler = new CallServiceSelectorAdapterHandler();
-    private final CallsManager mCallsManager;
-    private final OutgoingCallsManager mOutgoingCallsManager;
-    private final CallIdMapper mCallIdMapper;
-
-    CallServiceSelectorAdapter(
-            CallsManager callsManager,
-            OutgoingCallsManager outgoingCallsManager,
-            CallIdMapper callIdMapper) {
-        ThreadUtil.checkOnMainThread();
-        mCallsManager = callsManager;
-        mOutgoingCallsManager = outgoingCallsManager;
-        mCallIdMapper = callIdMapper;
-    }
-
-    /**
-     * @see CallServiceSelectorAdapter#setSelectedCallServices(String,List<CallServiceDescriptor>)
-     */
-    @Override
-    public void setSelectedCallServices(String callId, List<CallServiceDescriptor> descriptors) {
-        mCallIdMapper.checkValidCallId(callId);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = callId;
-        args.arg2 = descriptors;
-        mHandler.obtainMessage(MSG_SET_SELECTED_CALL_SERVICES, args).sendToTarget();
-    }
-
-    /** @see CallServiceSelectorAdapter#cancelOutgoingCall(String) */
-    @Override
-    public void cancelOutgoingCall(String callId) {
-        mCallIdMapper.checkValidCallId(callId);
-        mHandler.obtainMessage(MSG_CANCEL_OUTGOING_CALL, callId).sendToTarget();
-    }
-
-    /** @see CallServiceSelectorAdapter#setHandoffInfo(String,Uri,Bundle) */
-    @Override
-    public void setHandoffInfo(String callId, Uri handle, Bundle extras) {
-        mCallIdMapper.checkValidCallId(callId);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = callId;
-        args.arg2 = handle;
-        args.arg3 = extras;
-        mHandler.obtainMessage(MSG_SET_HANDOFF_INFO, args).sendToTarget();
-    }
-}
diff --git a/src/com/android/telecomm/CallServiceSelectorWrapper.java b/src/com/android/telecomm/CallServiceSelectorWrapper.java
index b5adda0..74073df 100644
--- a/src/com/android/telecomm/CallServiceSelectorWrapper.java
+++ b/src/com/android/telecomm/CallServiceSelectorWrapper.java
@@ -17,13 +17,18 @@
 package com.android.telecomm;
 
 import android.content.ComponentName;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.telecomm.CallInfo;
 import android.telecomm.CallServiceDescriptor;
-import android.telecomm.CallServiceSelector;
 import android.telecomm.TelecommConstants;
 
+import com.android.internal.os.SomeArgs;
 import com.android.internal.telecomm.ICallServiceSelector;
 import com.android.internal.telecomm.ICallServiceSelectorAdapter;
 
@@ -34,10 +39,100 @@
  * safely be unbound.
  */
 final class CallServiceSelectorWrapper extends ServiceBinder<ICallServiceSelector> {
-    private ICallServiceSelector mSelectorInterface;
+
+    private final class Adapter extends ICallServiceSelectorAdapter.Stub {
+        private static final int MSG_SET_SELECTED_CALL_SERVICES = 0;
+        private static final int MSG_CANCEL_OUTGOING_CALL = 1;
+        private static final int MSG_SET_HANDOFF_INFO = 2;
+
+        private final Handler mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_SET_SELECTED_CALL_SERVICES: {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        try {
+                            Call call = mCallIdMapper.getCall(args.arg1);
+                            List<CallServiceDescriptor> descriptors =
+                                    (List<CallServiceDescriptor>) args.arg2;
+                            if (call != null) {
+                                mOutgoingCallsManager.processSelectedCallServices(call,
+                                        descriptors);
+                            } else {
+                                Log.w(this, "setSelectedCallServices: unknown call: %s, id: %s",
+                                        call, args.arg1);
+                            }
+                        } finally {
+                            args.recycle();
+                        }
+                        break;
+                    }
+                    case MSG_CANCEL_OUTGOING_CALL: {
+                        Call call = mCallIdMapper.getCall(msg.obj);
+                        if (call != null) {
+                            mOutgoingCallsManager.abort(call);
+                        } else {
+                            Log.w(this, "cancelOutgoingCall: unknown call: %s, id: %s", call,
+                                    msg.obj);
+                        }
+                        break;
+                    }
+                    case MSG_SET_HANDOFF_INFO: {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        try {
+                            Call call = mCallIdMapper.getCall(args.arg1);
+                            Uri handle = (Uri) args.arg2;
+                            Bundle extras = (Bundle) args.arg3;
+                            if (call != null) {
+                                mCallsManager.setHandoffInfo(call, handle, extras);
+                            } else {
+                                Log.w(this, "setHandoffInfo: unknown call: %s, id: %s",
+                                        call, args.arg1);
+                            }
+                        } finally {
+                            args.recycle();
+                        }
+                        break;
+                    }
+                }
+            }
+        };
+
+        @Override
+        public void setSelectedCallServices(String callId,
+                List<CallServiceDescriptor> descriptors) {
+            mCallIdMapper.checkValidCallId(callId);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = descriptors;
+            mHandler.obtainMessage(MSG_SET_SELECTED_CALL_SERVICES, args).sendToTarget();
+        }
+
+        @Override
+        public void cancelOutgoingCall(String callId) {
+            mCallIdMapper.checkValidCallId(callId);
+            mHandler.obtainMessage(MSG_CANCEL_OUTGOING_CALL, callId).sendToTarget();
+        }
+
+        @Override
+        public void setHandoffInfo(String callId, Uri handle, Bundle extras) {
+            mCallIdMapper.checkValidCallId(callId);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = handle;
+            args.arg3 = extras;
+            mHandler.obtainMessage(MSG_SET_HANDOFF_INFO, args).sendToTarget();
+        }
+    }
+
     private final Binder mBinder = new Binder();
     private final CallIdMapper mCallIdMapper = new CallIdMapper("CallServiceSelector");
-    private final CallServiceSelectorAdapter mAdapter;
+    private final Adapter mAdapter = new Adapter();
+
+    private final CallsManager mCallsManager;
+    private final OutgoingCallsManager mOutgoingCallsManager;
+
+    private ICallServiceSelector mSelectorInterface;
 
     /**
      * Creates a call-service selector for the specified component using the specified action to
@@ -55,8 +150,8 @@
             OutgoingCallsManager outgoingCallsManager) {
 
         super(action, componentName);
-        mAdapter =
-                new CallServiceSelectorAdapter(callsManager, outgoingCallsManager, mCallIdMapper);
+        mCallsManager = callsManager;
+        mOutgoingCallsManager = outgoingCallsManager;
     }
 
     /**
@@ -78,16 +173,6 @@
                 outgoingCallsManager);
     }
 
-    /** See {@link CallServiceSelector#setCallServiceSelectorAdapter}. */
-    private void setCallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) {
-        if (isServiceValid("setCallServiceSelectorAdapter")) {
-            try {
-                mSelectorInterface.setCallServiceSelectorAdapter(adapter);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
     /**
      * Retrieves the sorted set of call services that are preferred by this selector. Upon failure,
      * the error callback is invoked. Can be invoked even when the call service is unbound.
@@ -120,42 +205,6 @@
         mBinder.bind(callback);
     }
 
-    private void onCallUpdated(final CallInfo callInfo) {
-        BindCallback callback = new BindCallback() {
-            @Override
-            public void onSuccess() {
-                if (isServiceValid("onCallUpdated")) {
-                    try {
-                        mSelectorInterface.onCallUpdated(callInfo);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-            @Override
-            public void onFailure() {
-            }
-        };
-        mBinder.bind(callback);
-    }
-
-    private void onCallRemoved(final String callId) {
-        BindCallback callback = new BindCallback() {
-            @Override
-            public void onSuccess() {
-                if (isServiceValid("onCallRemoved")) {
-                    try {
-                        mSelectorInterface.onCallRemoved(callId);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-            @Override
-            public void onFailure() {
-            }
-        };
-        mBinder.bind(callback);
-    }
-
     void addCall(Call call) {
         mCallIdMapper.addCall(call);
         onCallUpdated(call.toCallInfo(mCallIdMapper.getCallId(call)));
@@ -177,4 +226,51 @@
             setCallServiceSelectorAdapter(mAdapter);
         }
     }
+
+    private void setCallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) {
+        if (isServiceValid("setCallServiceSelectorAdapter")) {
+            try {
+                mSelectorInterface.setCallServiceSelectorAdapter(adapter);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    private void onCallUpdated(final CallInfo callInfo) {
+        BindCallback callback = new BindCallback() {
+            @Override
+            public void onSuccess() {
+                if (isServiceValid("onCallUpdated")) {
+                    try {
+                        mSelectorInterface.onCallUpdated(callInfo);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+            @Override
+            public void onFailure() {
+            }
+        };
+        mBinder.bind(callback);
+    }
+
+    private void onCallRemoved(final String callId) {
+        BindCallback callback = new BindCallback() {
+            @Override
+            public void onSuccess() {
+                if (isServiceValid("onCallRemoved")) {
+                    try {
+                        mSelectorInterface.onCallRemoved(callId);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+            @Override
+            public void onFailure() {
+            }
+        };
+        mBinder.bind(callback);
+    }
 }
diff --git a/src/com/android/telecomm/CallServiceWrapper.java b/src/com/android/telecomm/CallServiceWrapper.java
index 70f0ec2..a4e3d24 100644
--- a/src/com/android/telecomm/CallServiceWrapper.java
+++ b/src/com/android/telecomm/CallServiceWrapper.java
@@ -17,7 +17,10 @@
 package com.android.telecomm;
 
 import android.os.Bundle;
+
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.telecomm.CallAudioState;
 import android.telecomm.CallInfo;
@@ -25,10 +28,15 @@
 import android.telecomm.CallServiceDescriptor;
 import android.telecomm.TelecommConstants;
 
+import com.android.internal.os.SomeArgs;
 import com.android.internal.telecomm.ICallService;
 import com.android.internal.telecomm.ICallServiceAdapter;
 import com.android.internal.telecomm.ICallServiceProvider;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
 
 /**
  * Wrapper for {@link ICallService}s, handles binding to {@link ICallService} and keeps track of
@@ -37,25 +45,216 @@
  */
 final class CallServiceWrapper extends ServiceBinder<ICallService> {
 
-    /** The descriptor of this call service as supplied by the call-service provider. */
+    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;
+        private static final int MSG_SET_ACTIVE = 4;
+        private static final int MSG_SET_RINGING = 5;
+        private static final int MSG_SET_DIALING = 6;
+        private static final int MSG_SET_DISCONNECTED = 7;
+        private static final int MSG_SET_ON_HOLD = 8;
+
+        private final Handler mHandler = new Handler() {
+            @Override
+            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()) {
+                            CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
+                                    clientCallInfo.getHandle());
+                            mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
+                        } else {
+                            Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
+                                    call,
+                                    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);
+                        } 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);
+                        }
+                        break;
+                    case MSG_HANDLE_FAILED_OUTGOING_CALL: {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        try {
+                            call = mCallIdMapper.getCall(args.arg1);
+                            String reason = (String) args.arg2;
+                            if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
+                                mOutgoingCallsManager.handleFailedCallAttempt(call, reason);
+                            } else {
+                                Log.w(this,
+                                        "handleFailedOutgoingCall, unknown call: %s, id: %s",
+                                        call, args.arg1);
+                            }
+                        } finally {
+                            args.recycle();
+                        }
+                        break;
+                    }
+                    case MSG_SET_ACTIVE:
+                        call = mCallIdMapper.getCall(msg.obj);
+                        if (call != null) {
+                            mCallsManager.markCallAsActive(call);
+                        } else {
+                            Log.w(this, "setActive, unknown call id: %s", msg.obj);
+                        }
+                        break;
+                    case MSG_SET_RINGING:
+                        call = mCallIdMapper.getCall(msg.obj);
+                        if (call != null) {
+                            mCallsManager.markCallAsRinging(call);
+                        } else {
+                            Log.w(this, "setRinging, unknown call id: %s", msg.obj);
+                        }
+                        break;
+                    case MSG_SET_DIALING:
+                        call = mCallIdMapper.getCall(msg.obj);
+                        if (call != null) {
+                            mCallsManager.markCallAsDialing(call);
+                        } else {
+                            Log.w(this, "setDialing, unknown call id: %s", msg.obj);
+                        }
+                        break;
+                    case MSG_SET_DISCONNECTED: {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        try {
+                            call = mCallIdMapper.getCall(args.arg1);
+                            String disconnectMessage = (String) args.arg2;
+                            int disconnectCause = args.argi1;
+                            if (call != null) {
+                                mCallsManager.markCallAsDisconnected(call, disconnectCause,
+                                        disconnectMessage);
+                            } else {
+                                Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
+                            }
+                        } finally {
+                            args.recycle();
+                        }
+                        break;
+                    }
+                    case MSG_SET_ON_HOLD:
+                        call = mCallIdMapper.getCall(msg.obj);
+                        if (call != null) {
+                            mCallsManager.markCallAsOnHold(call);
+                        } else {
+                            Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
+                        }
+                        break;
+                }
+            }
+        };
+
+        /** {@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();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void notifyIncomingCall(CallInfo callInfo) {
+            mCallIdMapper.checkValidCallId(callInfo.getId());
+            mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void handleSuccessfulOutgoingCall(String callId) {
+            mCallIdMapper.checkValidCallId(callId);
+            mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void handleFailedOutgoingCall(String callId, String reason) {
+            mCallIdMapper.checkValidCallId(callId);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = reason;
+            mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void setActive(String callId) {
+            mCallIdMapper.checkValidCallId(callId);
+            mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void setRinging(String callId) {
+            mCallIdMapper.checkValidCallId(callId);
+            mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void setDialing(String callId) {
+            mCallIdMapper.checkValidCallId(callId);
+            mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void setDisconnected(
+                String callId, int disconnectCause, String disconnectMessage) {
+            mCallIdMapper.checkValidCallId(callId);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = disconnectMessage;
+            args.argi1 = disconnectCause;
+            mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void setOnHold(String callId) {
+            mCallIdMapper.checkValidCallId(callId);
+            mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
+        }
+    }
+
+    private final Adapter mAdapter = new Adapter();
+    private final CallsManager mCallsManager = CallsManager.getInstance();
+    private final Set<Call> mPendingCalls = Sets.newHashSet();
     private final CallServiceDescriptor mDescriptor;
-
-    /**
-     * The adapter used by the underlying call-service implementation to communicate with Telecomm.
-     */
-    private final CallServiceAdapter mAdapter;
-
-    /** The actual service implementation. */
-    private ICallService mServiceInterface;
+    private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
+    private final OutgoingCallsManager mOutgoingCallsManager;
+    private final IncomingCallsManager mIncomingCallsManager;
 
     private Binder mBinder = new Binder();
-    private final CallIdMapper mCallIdMapper;
+    private ICallService mServiceInterface;
 
     /**
      * Creates a call-service for the specified descriptor.
      *
      * @param descriptor The call-service descriptor from
-     *         {@link ICallServiceProvider#lookupCallServices}.
+     *            {@link ICallServiceProvider#lookupCallServices}.
      * @param outgoingCallsManager Manages the placing of outgoing calls.
      * @param incomingCallsManager Manages the incoming call initialization flow.
      */
@@ -65,9 +264,8 @@
             IncomingCallsManager incomingCallsManager) {
         super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
         mDescriptor = descriptor;
-        mCallIdMapper = new CallIdMapper("CallService");
-        mAdapter = new CallServiceAdapter(outgoingCallsManager, incomingCallsManager,
-                mCallIdMapper);
+        mIncomingCallsManager = incomingCallsManager;
+        mOutgoingCallsManager = outgoingCallsManager;
     }
 
     CallServiceDescriptor getDescriptor() {
@@ -86,7 +284,7 @@
 
     /**
      * 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
+     * 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.
@@ -94,17 +292,20 @@
     void isCompatibleWith(final Call call, final Runnable errorCallback) {
         Log.d(this, "isCompatibleWith(%s) via %s.", call, getComponentName());
         BindCallback callback = new BindCallback() {
-            @Override public void onSuccess() {
+            @Override
+            public void onSuccess() {
                 if (isServiceValid("isCompatibleWith")) {
                     try {
-                        mAdapter.addPendingCall(call);
+                        mPendingCalls.add(call);
                         CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
                         mServiceInterface.isCompatibleWith(callInfo);
                     } catch (RemoteException e) {
                     }
                 }
             }
-            @Override public void onFailure() {
+
+            @Override
+            public void onFailure() {
                 errorCallback.run();
             }
         };
@@ -113,7 +314,7 @@
     }
 
     /**
-     * Attempts to place the specified call, see {@link ICallService#call}.  Upon failure, the
+     * 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) {
@@ -129,7 +330,7 @@
 
     /** @see CallService#abort(String) */
     void abort(Call call) {
-        mAdapter.removePendingCall(call);
+        mPendingCalls.remove(call);
         if (isServiceValid("abort")) {
             try {
                 mServiceInterface.abort(mCallIdMapper.getCallId(call));
@@ -172,8 +373,8 @@
     /**
      * Starts retrieval of details for an incoming call. Details are returned through the
      * call-service adapter using the specified call ID. Upon failure, the specified error callback
-     * is invoked. Can be invoked even when the call service is unbound.
-     * See {@link ICallService#setIncomingCallId}.
+     * is invoked. Can be invoked even when the call service is unbound. See
+     * {@link ICallService#setIncomingCallId}.
      *
      * @param call The call used for the incoming call.
      * @param extras The {@link CallService}-provided extras which need to be sent back.
@@ -182,9 +383,10 @@
     void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
         Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
         BindCallback callback = new BindCallback() {
-            @Override public void onSuccess() {
+            @Override
+            public void onSuccess() {
                 if (isServiceValid("setIncomingCallId")) {
-                    mAdapter.addPendingCall(call);
+                    mPendingCalls.add(call);
                     try {
                         mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
                                 extras);
@@ -192,7 +394,9 @@
                     }
                 }
             }
-            @Override public void onFailure() {
+
+            @Override
+            public void onFailure() {
                 errorCallback.run();
             }
         };
@@ -263,18 +467,19 @@
     }
 
     void removeCall(Call call) {
-        mAdapter.removePendingCall(call);
+        mPendingCalls.remove(call);
         mCallIdMapper.removeCall(call);
     }
 
     /** {@inheritDoc} */
-    @Override protected void setServiceInterface(IBinder binder) {
+    @Override
+    protected void setServiceInterface(IBinder binder) {
         if (binder == null) {
             // We have lost our service connection. Notify the world that this call service is done.
             // We must notify the adapter before CallsManager. The adapter will force any pending
             // outgoing calls to try the next call service. This needs to happen before CallsManager
             // tries to clean up any calls still associated with this call service.
-            mAdapter.handleCallServiceDeath();
+            handleCallServiceDeath();
             CallsManager.getInstance().handleCallServiceDeath(this);
             mServiceInterface = null;
         } else {
@@ -282,4 +487,27 @@
             setCallServiceAdapter(mAdapter);
         }
     }
+
+    /**
+     * Called when the associated call service dies.
+     */
+    private void handleCallServiceDeath() {
+        if (!mPendingCalls.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.");
+                }
+            }
+
+            if (!mPendingCalls.isEmpty()) {
+                Log.wtf(this, "Pending calls did not get cleared.");
+                mPendingCalls.clear();
+            }
+        }
+    }
 }