Merge "Use the new incoming call confirmation APIs." into master-nova-dev
diff --git a/src/com/android/telecomm/CallServiceRepository.java b/src/com/android/telecomm/CallServiceRepository.java
index eb31d87..23fffca 100644
--- a/src/com/android/telecomm/CallServiceRepository.java
+++ b/src/com/android/telecomm/CallServiceRepository.java
@@ -174,6 +174,31 @@
}
/**
+ * Creates and returns the call service for the specified {@link CallServiceInfo}. Inserts newly
+ * created entries into the cache, see {@link #mCallServiceCache}, or if a cached version
+ * already exists, returns that instead. All newly created instances will not yet be bound,
+ * however cached versions may or may not be bound.
+ *
+ * @param info The call service descriptor.
+ * @return The call service.
+ */
+ CallServiceWrapper getCallService(CallServiceInfo info) {
+ Preconditions.checkNotNull(info);
+
+ // TODO(santoscordon): Rename getServiceComponent to getComponentName.
+ ComponentName componentName = info.getServiceComponent();
+
+ CallServiceWrapper callService = mCallServiceCache.get(componentName);
+ if (callService == null) {
+ CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
+ callService = new CallServiceWrapper(info, adapter);
+ mCallServiceCache.put(componentName, callService);
+ }
+
+ return callService;
+ }
+
+ /**
* Attempts to bind to the specified provider before continuing to {@link #processProvider}.
*
* @param componentName The component name of the relevant provider.
@@ -347,29 +372,4 @@
return provider;
}
-
- /**
- * Creates and returns the call service for the specified call-service info object. Inserts
- * newly created entries into the cache, see {@link #mCallServiceCache}, or if a cached
- * version already exists, returns that instead. All newly created instances will not yet
- * be bound, however cached versions may or may not be bound.
- *
- * @param info The call service descriptor.
- * @return The call service.
- */
- private CallServiceWrapper getCallService(CallServiceInfo info) {
- Preconditions.checkNotNull(info);
-
- // TODO(santoscordon): Rename getServiceComponent to getComponentName.
- ComponentName componentName = info.getServiceComponent();
-
- CallServiceWrapper callService = mCallServiceCache.get(componentName);
- if (callService == null) {
- CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
- callService = new CallServiceWrapper(info, adapter);
- mCallServiceCache.put(componentName, callService);
- }
-
- return callService;
- }
}
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index d69af53..ff224e5 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -17,6 +17,7 @@
package com.android.telecomm;
import android.content.Context;
+import android.telecomm.CallServiceInfo;
import android.telecomm.CallState;
import android.text.TextUtils;
import android.util.Log;
@@ -86,6 +87,23 @@
}
/**
+ * Starts the incoming call sequence by having switchboard confirm with the specified call
+ * service that an incoming call actually exists for the specified call token. Upon success,
+ * execution returns to {@link #handleSuccessfulIncomingCall} to start the in-call UI.
+ *
+ * @param callServiceInfo The details of the call service to use for this incoming call.
+ * @param callToken The token used by the call service to identify the incoming call.
+ */
+ void processIncomingCallIntent(CallServiceInfo callServiceInfo, String callToken) {
+ // Create a call with no handle. Eventually, switchboard will update the call with
+ // additional information from the call service, but for now we just need one to pass around
+ // with a unique call ID.
+ Call call = new Call(null, null);
+
+ mSwitchboard.confirmIncomingCall(call, callServiceInfo, callToken);
+ }
+
+ /**
* Attempts to issue/connect the specified call. From an (arbitrary) application standpoint,
* all that is required to initiate this flow is to fire either of the CALL, CALL_PRIVILEGED,
* and CALL_EMERGENCY intents. These are listened to by CallActivity.java which then invokes
@@ -123,6 +141,17 @@
mInCallController.addCall(call.toCallInfo());
}
+ /**
+ * Adds a new incoming call to the list of live calls and notifies the in-call app.
+ *
+ * @param call The new incoming call.
+ */
+ void handleSuccessfulIncomingCall(Call call) {
+ Preconditions.checkState(call.getState() == CallState.RINGING);
+ addCall(call);
+ mInCallController.addCall(call.toCallInfo());
+ }
+
/*
* Sends all the live calls to the in-call app if any exist. If there are no live calls, then
* tells the in-call controller to unbind since it is not needed.
diff --git a/src/com/android/telecomm/IncomingCallsManager.java b/src/com/android/telecomm/IncomingCallsManager.java
new file mode 100644
index 0000000..941c248
--- /dev/null
+++ b/src/com/android/telecomm/IncomingCallsManager.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 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.Looper;
+
+import com.android.telecomm.ServiceBinder.BindCallback;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+
+import java.util.Map;
+
+/**
+ * Utility class to confirm the existence of an incoming call after receiving an incoming-call
+ * intent, see {@link TelecommReceiver}. Binds with the specified call services and requests
+ * confirmation of incoming calls using call tokens provided as part of the intent. Upon receipt of
+ * the confirmation, yields execution back to the switchboard to complete the incoming sequence. The
+ * entire process is timeboxed to protect against unresponsive call services.
+ */
+final class IncomingCallsManager {
+
+ /**
+ * The amount of time to wait for confirmation of an incoming call, in milliseconds.
+ * TODO(santoscordon): Likely needs adjustment.
+ */
+ private static final int INCOMING_CALL_TIMEOUT_MS = 1000;
+
+ private final Switchboard mSwitchboard;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ /** Maps incoming calls to their call services. */
+ private final Map<Call, CallServiceWrapper> mPendingIncomingCalls = Maps.newHashMap();
+
+ /**
+ * Persists the specified parameters.
+ *
+ * @param switchboard The switchboard.
+ */
+ IncomingCallsManager(Switchboard switchboard) {
+ mSwitchboard = switchboard;
+ }
+
+
+ /**
+ * Confirms the existence of an incoming call with the specified call service (asynchronously).
+ * Starts the timeout sequence in case the call service is unresponsive.
+ *
+ * @param call The call object.
+ * @param callService The call service.
+ * @param callToken The token used by the call service to identify the incoming call.
+ */
+ void confirmIncomingCall(
+ final Call call, final CallServiceWrapper callService, String callToken) {
+
+ ThreadUtil.checkOnMainThread();
+ // Just to be safe, lets make sure we're not already processing this call.
+ Preconditions.checkState(!mPendingIncomingCalls.containsKey(call));
+
+ mPendingIncomingCalls.put(call, callService);
+ startTimeoutForCall(call);
+
+ BindCallback callback = new BindCallback() {
+ @Override public void onSuccess() {
+ // TODO(santoscordon): ICallService needs to be updated with the following method.
+ // Confirmation won't work until this method is filled in.
+ // callService.confirmIncomingCall(call.toCallInfo(), callToken);
+ }
+ @Override public void onFailure() {
+ handleFailedIncomingCall(call);
+ }
+ };
+
+ callService.bind(callback);
+ }
+
+ /**
+ * Starts a timeout to timebox the confirmation of an incoming call. When the timeout expires,
+ * it will notify switchboard that the incoming call was not confirmed and thus does not exist
+ * as far as Telecomm is concerned.
+ *
+ * @param call The call.
+ */
+ void startTimeoutForCall(final Call call) {
+ Runnable timeoutCallback = new Runnable() {
+ @Override public void run() {
+ handleFailedIncomingCall(call);
+ }
+ };
+ mHandler.postDelayed(timeoutCallback, INCOMING_CALL_TIMEOUT_MS);
+ }
+
+ /**
+ * Notifies the switchboard of a successful incoming call after removing it from the pending
+ * list.
+ * TODO(santoscordon): Needs code in CallServiceAdapter to call this method.
+ *
+ * @param call The call.
+ */
+ void handleSuccessfulIncomingCall(Call call) {
+ ThreadUtil.checkOnMainThread();
+ if (mPendingIncomingCalls.remove(call) != null) {
+ mSwitchboard.handleSuccessfulIncomingCall(call);
+ }
+ }
+
+ /**
+ * Notifies switchboard of the failed incoming call after removing it from the pending list.
+ *
+ * @param call The call.
+ */
+ void handleFailedIncomingCall(Call call) {
+ ThreadUtil.checkOnMainThread();
+ if (mPendingIncomingCalls.remove(call) != null) {
+ // The call was found still waiting for confirmation. Consider it failed.
+ mSwitchboard.handleFailedIncomingCall(call);
+ }
+ }
+}
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index 0a2df04..34649f0 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -23,12 +23,12 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.telecomm.CallInfo;
import android.telecomm.CallState;
import android.telecomm.CallServiceInfo;
import android.telecomm.ICallServiceSelectionResponse;
import android.telecomm.ICallServiceSelector;
-import android.util.Log;
+
+import com.android.telecomm.ServiceBinder.BindCallback;
import java.util.Iterator;
import java.util.List;
@@ -62,9 +62,9 @@
private final List<CallServiceInfo> mCallServiceInfos = Lists.newArrayList();
/**
- * The map of currently-available call-service implementations keyed by call-service infos.
+ * The map of currently-available call-service implementations keyed by call-service ID.
*/
- private final Map<CallServiceInfo, CallServiceWrapper> mCallServicesByInfo = Maps.newHashMap();
+ private final Map<String, CallServiceWrapper> mCallServicesById = Maps.newHashMap();
/**
* The set of currently-available call-service selector implementations.
@@ -129,7 +129,7 @@
for (CallServiceWrapper callService : callServices) {
CallServiceInfo info = callService.getInfo();
mCallServiceInfos.add(info);
- mCallServicesByInfo.put(info, callService);
+ mCallServicesById.put(info.getCallServiceId(), callService);
}
}
@@ -253,11 +253,19 @@
if (mCallServiceInfoIterator.hasNext()) {
CallServiceInfo info = mCallServiceInfoIterator.next();
- mCallService = mCallServicesByInfo.get(info);
+ mCallService = mCallServicesById.get(info.getCallServiceId());
if (mCallService == null) {
attemptNextCallService();
} else {
- mCallService.call(mCall.toCallInfo());
+ BindCallback callback = new BindCallback() {
+ @Override public void onSuccess() {
+ mCallService.call(mCall.toCallInfo());
+ }
+ @Override public void onFailure() {
+ attemptNextSelector();
+ }
+ };
+ mCallService.bind(callback);
}
} else {
mCallServiceInfoIterator = null;
diff --git a/src/com/android/telecomm/ServiceBinder.java b/src/com/android/telecomm/ServiceBinder.java
index c58791c..101b940 100644
--- a/src/com/android/telecomm/ServiceBinder.java
+++ b/src/com/android/telecomm/ServiceBinder.java
@@ -59,7 +59,8 @@
mServiceConnection = this;
mBinder = binder;
- handleSuccessfulConnection(binder);
+ setServiceInterface(binder);
+ handleSuccessfulConnection();
}
@Override
@@ -140,7 +141,7 @@
}
} else {
Preconditions.checkNotNull(mBinder);
- handleSuccessfulConnection(mBinder);
+ handleSuccessfulConnection();
}
return true;
@@ -169,12 +170,8 @@
/**
* Notifies all the outstanding callbacks that the service is successfully bound. The list of
* outstanding callbacks is cleared afterwards.
- *
- * @param binder The actual bound service implementation.
*/
- private void handleSuccessfulConnection(IBinder binder) {
- setServiceInterface(binder);
-
+ private void handleSuccessfulConnection() {
for (BindCallback callback : mCallbacks) {
callback.onSuccess();
}
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index 32ef914..34398d2 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -20,9 +20,9 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.telecomm.CallServiceInfo;
import android.telecomm.ICallServiceSelector;
import java.util.Collection;
@@ -47,6 +47,9 @@
/** Used to place outgoing calls. */
private final OutgoingCallsManager mOutgoingCallsManager;
+ /** Used to confirm incoming calls. */
+ private final IncomingCallsManager mIncomingCallsManager;
+
private final CallServiceRepository mCallServiceRepository;
private final CallServiceSelectorRepository mSelectorRepository;
@@ -100,6 +103,7 @@
Switchboard(CallsManager callsManager) {
mCallsManager = callsManager;
mOutgoingCallsManager = new OutgoingCallsManager(this);
+ mIncomingCallsManager = new IncomingCallsManager(this);
mCallServiceRepository = new CallServiceRepository(this, mOutgoingCallsManager);
mSelectorRepository = new CallServiceSelectorRepository(this);
}
@@ -114,15 +118,31 @@
void placeOutgoingCall(Call call) {
ThreadUtil.checkOnMainThread();
- mLookupId++;
+ mNewOutgoingCalls.add(call);
// We initialize a lookup every time because between calls the set of available call
// services can change between calls.
+ mLookupId++;
mCallServiceRepository.initiateLookup(mLookupId);
mSelectorRepository.initiateLookup(mLookupId);
}
/**
+ * Confirms with incoming call manager that an incoming call exists for the specified call
+ * service and call token. The incoming call manager will invoke either
+ * {@link #handleSuccessfulIncomingCall} or {@link #handleFailedIncomingCall} depending
+ * on the result.
+ *
+ * @param call The call object.
+ * @param callServiceInfo The details of the call service.
+ * @param callToken The token used by the call service to identify the incoming call.
+ */
+ void confirmIncomingCall(Call call, CallServiceInfo callServiceInfo, String callToken) {
+ CallServiceWrapper callService = mCallServiceRepository.getCallService(callServiceInfo);
+ mIncomingCallsManager.confirmIncomingCall(call, callService, callToken);
+ }
+
+ /**
* Persists the specified set of call services and attempts to place any pending outgoing
* calls. Intended to be invoked by {@link CallServiceRepository} exclusively.
*
@@ -181,6 +201,29 @@
}
/**
+ * Handles the case where we received confirmation of an incoming call. Hands the resulting
+ * call to {@link CallsManager} as the final step in the incoming sequence. At that point,
+ * {@link CallsManager} should bring up the incoming-call UI.
+ */
+ void handleSuccessfulIncomingCall(Call call) {
+ mCallsManager.handleSuccessfulIncomingCall(call);
+ }
+
+ /**
+ * Handles the case where we failed to confirm an incoming call after receiving an incoming-call
+ * intent via {@link TelecommReceiver}.
+ *
+ * @param call The call.
+ */
+ void handleFailedIncomingCall(Call call) {
+ // At the moment there is nothing to do if an incoming call is not confirmed. We may at a
+ // future date bind to the in-call app optimistically during the incoming-call sequence and
+ // this method could tell {@link CallsManager} to unbind from the in-call app if the
+ // incoming call was not confirmed. It's worth keeping this method for parity with the
+ // outgoing call sequence.
+ }
+
+ /**
* @return True if ticking should continue (or be resumed) and false otherwise.
*/
private boolean isTicking() {
diff --git a/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java b/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
index afc82f7..a11a35b 100644
--- a/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
+++ b/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
@@ -16,15 +16,8 @@
package com.android.telecomm.testcallservice;
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.telecomm.CallInfo;
-import android.telecomm.CallServiceInfo;
-import android.telecomm.ICallServiceSelector;
-import android.telecomm.ICallServiceSelectionResponse;
-import android.telecomm.ICallSwitchabilityResponse;
+import android.telecomm.CallServiceSelector;
import java.util.List;
@@ -32,36 +25,14 @@
* Dummy call-service selector which returns the list of call services in the same order in which it
* was given. Also returns false for every request on switchability.
*/
-public class DummyCallServiceSelector extends Service {
-
- /**
- * Actual Binder implementation of ICallServiceSelector.
- */
- private final IBinder mBinder = new ICallServiceSelector.Stub() {
- /**
- * Returns the unaltered list of call services.
- *
- * {@inheritDoc}
- */
- @Override public void select(
- CallInfo callInfo,
- List<CallServiceInfo> callServiceInfos,
- ICallServiceSelectionResponse response) throws RemoteException {
-
- response.setSelectedCallServiceInfos(callServiceInfos);
- }
-
- /** {@inheritDoc} */
- @Override public void isSwitchable(CallInfo callInfo, ICallSwitchabilityResponse response)
- throws RemoteException {
-
- response.setIsSwitchable(false);
- }
- };
-
- /** {@inheritDoc} */
+public class DummyCallServiceSelector extends CallServiceSelector {
@Override
- public IBinder onBind(Intent intent) {
- return mBinder;
+ protected boolean isSwitchable(CallInfo callInfo) {
+ return false;
+ }
+
+ @Override
+ protected List<String> select(CallInfo callInfo, List<String> callServiceIds) {
+ return callServiceIds;
}
}