Telecomm implementation of CallServiceSelectorAdapter

Bug: 13643568
Change-Id: Ida5859a3b5b15c9fa1c533f27a3e14fd0d7c36af
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
index 4bd7743..2db6255 100644
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ b/src/com/android/telecomm/CallServiceAdapter.java
@@ -66,7 +66,8 @@
                                 clientCallInfo.getHandle());
                         mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
                     } else {
-                        Log.w(this, "Unknown incoming call: %s, id: %s", call, msg.obj);
+                        Log.w(this, "Unknown incoming call: %s, id: %s", call,
+                                clientCallInfo.getId());
                     }
                     break;
                 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL:
@@ -86,7 +87,7 @@
                         if (call != null && mPendingCalls.remove(call) && !call.isIncoming()) {
                             mOutgoingCallsManager.handleFailedCallAttempt(call, reason);
                         } else {
-                            Log.w(this, "Unknown outgoing call: %s, id: %s", call, msg.obj);
+                            Log.w(this, "Unknown outgoing call: %s, id: %s", call, args.arg1);
                         }
                     } finally {
                         args.recycle();
@@ -127,7 +128,7 @@
                             mCallsManager.markCallAsDisconnected(call, disconnectCause,
                                     disconnectMessage);
                         } else {
-                            Log.w(this, "Unknown call id: %s", msg.obj);
+                            Log.w(this, "Unknown call id: %s", args.arg1);
                         }
                     } finally {
                         args.recycle();
diff --git a/src/com/android/telecomm/CallServiceSelectorAdapter.java b/src/com/android/telecomm/CallServiceSelectorAdapter.java
new file mode 100644
index 0000000..5c805c5
--- /dev/null
+++ b/src/com/android/telecomm/CallServiceSelectorAdapter.java
@@ -0,0 +1,114 @@
+/*
+ * 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_SET_HANDOFF_INFO = 1;
+
+    private final class CallServiceSelectorAdapaterHandler 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, "Unknown call: %s, id: %s", call, args.arg1);
+                        }
+                    } finally {
+                        args.recycle();
+                    }
+                    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, "Unknown call: %s, id: %s", call, args.arg1);
+                        }
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private final Handler mHandler = new CallServiceSelectorAdapaterHandler();
+    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 CallServiceSelectorAdapater#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 CallServiceSelectorAdapater#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/CallServiceSelectorRepository.java b/src/com/android/telecomm/CallServiceSelectorRepository.java
index 0ec805f..50d26e8 100644
--- a/src/com/android/telecomm/CallServiceSelectorRepository.java
+++ b/src/com/android/telecomm/CallServiceSelectorRepository.java
@@ -39,9 +39,7 @@
 final class CallServiceSelectorRepository {
 
     private final Switchboard mSwitchboard;
-
-    /** The application context. */
-    private final Context mContext;
+    private final OutgoingCallsManager mOutgoingCallsManager;
 
     /**
      * The set of call-service selectors. Only populated via initiateLookup scenarios.
@@ -54,9 +52,11 @@
      *
      * @param switchboard The switchboard for this finer to work against.
      */
-    CallServiceSelectorRepository(Switchboard switchboard) {
+    CallServiceSelectorRepository(
+            Switchboard switchboard,
+            OutgoingCallsManager outgoingCallsManager) {
         mSwitchboard = switchboard;
-        mContext = TelecommApp.getInstance();
+        mOutgoingCallsManager = outgoingCallsManager;
     }
 
     /**
@@ -70,7 +70,8 @@
         List<ComponentName> selectorNames = getSelectorNames();
         for (ComponentName name : selectorNames) {
             if (!mCallServiceSelectors.containsKey(name)) {
-                mCallServiceSelectors.put(name, new CallServiceSelectorWrapper(name));
+                mCallServiceSelectors.put(name, new CallServiceSelectorWrapper(name,
+                        CallsManager.getInstance(), mOutgoingCallsManager));
             }
         }
 
@@ -86,7 +87,7 @@
         // The list of selector names to return to the caller, may be populated below.
         List<ComponentName> selectorNames = Lists.newArrayList();
 
-        PackageManager packageManager = mContext.getPackageManager();
+        PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
         Intent intent = new Intent(TelecommConstants.ACTION_CALL_SERVICE_SELECTOR);
         for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
             ServiceInfo serviceInfo = entry.serviceInfo;
diff --git a/src/com/android/telecomm/CallServiceSelectorWrapper.java b/src/com/android/telecomm/CallServiceSelectorWrapper.java
index 6db5c8f..f4ddf19 100644
--- a/src/com/android/telecomm/CallServiceSelectorWrapper.java
+++ b/src/com/android/telecomm/CallServiceSelectorWrapper.java
@@ -23,12 +23,11 @@
 import android.telecomm.CallInfo;
 import android.telecomm.CallServiceDescriptor;
 import android.telecomm.CallServiceSelector;
-import android.telecomm.CallServiceSelector.CallServiceSelectionResponse;
 import android.telecomm.TelecommConstants;
 
 import com.google.common.base.Preconditions;
-import com.android.internal.telecomm.ICallServiceSelectionResponse;
 import com.android.internal.telecomm.ICallServiceSelector;
+import com.android.internal.telecomm.ICallServiceSelectorAdapter;
 
 import java.util.List;
 
@@ -37,59 +36,53 @@
  * safely be unbound.
  */
 final class CallServiceSelectorWrapper extends ServiceBinder<ICallServiceSelector> {
-    class SelectionResponseImpl extends ICallServiceSelectionResponse.Stub {
-        private final CallServiceSelectionResponse mResponse;
-
-        SelectionResponseImpl(CallServiceSelectionResponse response) {
-            mResponse = response;
-        }
-
-        @Override
-        public void setSelectedCallServiceDescriptors(
-                final List<CallServiceDescriptor> descriptors) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mResponse.setSelectedCallServices(descriptors);
-                }
-            });
-        }
-    }
-
     private ICallServiceSelector mSelectorInterface;
     private final Binder mBinder = new Binder();
     private final Handler mHandler = new Handler();
     private final CallIdMapper mCallIdMapper = new CallIdMapper("CallServiceSelector");
+    private final CallServiceSelectorAdapter mAdapter;
 
     /**
      * Creates a call-service selector for the specified component.
      *
      * @param componentName The component name of the service.
      */
-    CallServiceSelectorWrapper(ComponentName componentName) {
+    CallServiceSelectorWrapper(ComponentName componentName, CallsManager callsManager,
+            OutgoingCallsManager outgoingCallsManager) {
         super(TelecommConstants.ACTION_CALL_SERVICE_SELECTOR, componentName);
+        mAdapter = new CallServiceSelectorAdapter(callsManager, outgoingCallsManager,
+                mCallIdMapper);
+    }
+
+    /** 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.
      *
-     * @param selectionResponse The selection response callback to invoke upon success.
+     * @param call The call being placed using the {@link CallService}s.
+     * @param descriptors The descriptors of the available {@link CallService}s with which to place
+     *            the call.
      * @param errorCallback The callback to invoke upon failure.
      */
-    void select(final Call call, final List<CallServiceDescriptor> callServiceDescriptors,
-            final CallServiceSelectionResponse selectionResponse, final Runnable errorCallback) {
+    void select(final Call call, final List<CallServiceDescriptor> descriptors,
+            final Runnable errorCallback) {
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
                 if (isServiceValid("select")) {
                     try {
                         CallInfo callInfo = call.toCallInfo(mCallIdMapper.getCallId(call));
-                        mSelectorInterface.select(callInfo, callServiceDescriptors,
-                                new SelectionResponseImpl(selectionResponse));
+                        mSelectorInterface.select(callInfo, descriptors);
                     } catch (RemoteException e) {
-                        Log.e(CallServiceSelectorWrapper.this, e,
-                                "Failed calling select for selector: %s.", getComponentName());
                     }
                 }
             }
@@ -114,6 +107,11 @@
     /** {@inheritDoc} */
     @Override
     protected void setServiceInterface(IBinder binder) {
-        mSelectorInterface = ICallServiceSelector.Stub.asInterface(binder);
+        if (binder == null) {
+            mSelectorInterface = null;
+        } else {
+            mSelectorInterface = ICallServiceSelector.Stub.asInterface(binder);
+            setCallServiceSelectorAdapter(mAdapter);
+        }
     }
 }
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 0387fab..3e8a42e 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -403,6 +403,10 @@
         removeCall(call);
     }
 
+    void setHandoffInfo(Call call, Uri handle, Bundle extras) {
+        // TODO(sail): Implement this.
+    }
+
     /**
      * Cleans up any calls currently associated with the specified call service when the
      * call-service binder disconnects unexpectedly.
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index ca4b33e..315a9bd 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -21,7 +21,6 @@
 import android.os.RemoteException;
 import android.telecomm.CallState;
 import android.telecomm.CallServiceDescriptor;
-import android.telecomm.CallServiceSelector.CallServiceSelectionResponse;
 
 import com.google.android.collect.Sets;
 import com.google.common.collect.Maps;
@@ -237,6 +236,23 @@
     }
 
     /**
+     * Persists the ordered-list of call-service descriptor as selected by the current selector and
+     * starts iterating through the corresponding call services continuing the attempt to place the
+     * call.
+     *
+     * @param descriptors The (ordered) list of call-service descriptor.
+     */
+    void processSelectedCallServices(List<CallServiceDescriptor> descriptors) {
+        Log.v(this, "processSelectedCallServices");
+        if (descriptors == null || descriptors.isEmpty()) {
+            attemptNextSelector();
+        } else if (mCallServiceDescriptorIterator == null) {
+            mCallServiceDescriptorIterator = descriptors.iterator();
+            attemptNextCallService();
+        }
+    }
+
+    /**
      * Attempts to place the call using the next selector, no-op if no other selectors
      * are available.
      */
@@ -249,15 +265,7 @@
         if (mSelectorIterator.hasNext()) {
             CallServiceSelectorWrapper selector = mSelectorIterator.next();
             mCall.setCallServiceSelector(selector);
-
-            CallServiceSelectionResponse responseCallback = new CallServiceSelectionResponse() {
-                    @Override
-                    public void setSelectedCallServices(List<CallServiceDescriptor> callServices) {
-                        processSelectedCallServiceDescriptors(callServices);
-                    }
-                };
-            selector.select(mCall, mCallServiceDescriptors, responseCallback,
-                    mNextSelectorCallback);
+            selector.select(mCall, mCallServiceDescriptors, mNextSelectorCallback);
         } else {
             Log.v(this, "attemptNextSelector, no more selectors, failing");
             mCall.clearCallServiceSelector();
@@ -266,25 +274,6 @@
     }
 
     /**
-     * Persists the ordered-list of call-service descriptor as selected by the current selector and
-     * starts iterating through the corresponding call services continuing the attempt to place the
-     * call.
-     *
-     * @selectedCallServiceDescriptors The (ordered) list of call-service descriptor.
-     */
-    private void processSelectedCallServiceDescriptors(
-            List<CallServiceDescriptor> selectedCallServiceDescriptors) {
-
-        Log.v(this, "processSelectedCallServiceDescriptors");
-        if (selectedCallServiceDescriptors == null || selectedCallServiceDescriptors.isEmpty()) {
-            attemptNextSelector();
-        } else if (mCallServiceDescriptorIterator == null) {
-            mCallServiceDescriptorIterator = selectedCallServiceDescriptors.iterator();
-            attemptNextCallService();
-        }
-    }
-
-    /**
      * Attempts to place the call using the call service specified by the next call-service
      * descriptor of mCallServiceDescriptorIterator.  If there are no more call services to
      * attempt, the process continues to the next call-service selector via
diff --git a/src/com/android/telecomm/OutgoingCallsManager.java b/src/com/android/telecomm/OutgoingCallsManager.java
index 1a955ce..def5211 100644
--- a/src/com/android/telecomm/OutgoingCallsManager.java
+++ b/src/com/android/telecomm/OutgoingCallsManager.java
@@ -16,10 +16,13 @@
 
 package com.android.telecomm;
 
+import android.telecomm.CallServiceDescriptor;
+
 import com.android.internal.telecomm.ICallServiceSelector;
 import com.google.common.collect.Maps;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -141,6 +144,21 @@
     }
 
     /**
+     * Forwards the selected call service from the selector to the corresponding outgoing-call
+     * processor.
+     */
+    void processSelectedCallServices(Call call, List<CallServiceDescriptor> descriptors) {
+        Log.v(this, "processSelectedCallServices, call %s,  descriptors: %s", call, descriptors);
+        OutgoingCallProcessor processor = mOutgoingCallProcessors.get(call);
+        if (processor == null) {
+            // Shouldn't happen, so log a wtf if it does.
+            Log.wtf(this, "Received unexpected setSelectedCallServices notification.");
+        } else {
+            processor.processSelectedCallServices(descriptors);
+        }
+    }
+
+    /**
      * Aborts any ongoing attempts to connect the specified (outgoing) call.
      *
      * @param call The call to be aborted.
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index 5a23f88..77c3c16 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -107,7 +107,7 @@
         mCallsManager = callsManager;
         mOutgoingCallsManager = new OutgoingCallsManager(this);
         mIncomingCallsManager = new IncomingCallsManager(this);
-        mSelectorRepository = new CallServiceSelectorRepository(this);
+        mSelectorRepository = new CallServiceSelectorRepository(this, mOutgoingCallsManager);
         mCallServiceRepository =
                 new CallServiceRepository(this, mOutgoingCallsManager, mIncomingCallsManager);
 
@@ -258,8 +258,7 @@
      */
     private boolean isTicking() {
         // TODO(gilad): return true every time at least one outgoing call is pending (i.e. waiting
-        // to be connected by a call service) and also when at least one active call is switch-able
-        // between call services, see {@link ICallServiceSelector#isSwitchable}.
+        // to be connected by a call service).
         return false;
     }