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/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();
+ }
+ }
+ }
}