Adding answer/reject API support to Telecomm.
Adds answer/reject overridden methods to CallServiceWrapper.
Adds direct answer/reject to Call.java
Fills in existing answer/reject in CallsManager.java.
Change-Id: Ifd3a65230661b94f9dd99aabb2d2083684e2fc7c
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index ea62da4..4576075 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -21,7 +21,10 @@
import android.telecomm.CallState;
import android.util.Log;
+import com.google.common.base.Preconditions;
+
import java.util.Date;
+import java.util.UUID;
/**
* Encapsulates all aspects of a given phone call throughout its lifecycle, starting
@@ -31,19 +34,9 @@
final class Call {
private static final String TAG = Call.class.getSimpleName();
- /**
- * Unique identifier for the call as a UUID string.
- */
+ /** Unique identifier for the call as a UUID string. */
private final String mId;
- /**
- * The state of the call.
- */
- private CallState mState;
-
- /** The handle with which to establish this call. */
- private final String mHandle;
-
/** Additional contact information beyond handle above, optional. */
private final ContactInfo mContactInfo;
@@ -55,15 +48,19 @@
*/
private final Date mCreationTime;
+ /** The state of the call. */
+ private CallState mState;
+
+ /** The handle with which to establish this call. */
+ private String mHandle;
+
/**
* The call service which is currently connecting this call, null as long as the call is not
* connected.
*/
private CallServiceWrapper mCallService;
- /**
- * Read-only and parcelable version of this call.
- */
+ /** Read-only and parcelable version of this call. */
private CallInfo mCallInfo;
/**
@@ -73,14 +70,18 @@
* @param contactInfo Information about the entity being called.
*/
Call(String handle, ContactInfo contactInfo) {
- // TODO(gilad): Pass this in etc.
- mId = "dummy";
+ mId = UUID.randomUUID().toString(); // UUIDs should provide sufficient uniqueness.
mState = CallState.NEW;
mHandle = handle;
mContactInfo = contactInfo;
mCreationTime = new Date();
}
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "[" + mId + ", " + mState + ", " + mCallService.getComponentName() + "]";
+ }
+
String getId() {
return mId;
}
@@ -104,6 +105,10 @@
return mHandle;
}
+ void setHandle(String handle) {
+ mHandle = handle;
+ }
+
ContactInfo getContactInfo() {
return mContactInfo;
}
@@ -147,6 +152,36 @@
}
/**
+ * Answers the call if it is ringing.
+ */
+ void answer() {
+ Preconditions.checkNotNull(mCallService);
+
+ // Check to verify that the call is still in the ringing state. A call can change states
+ // between the time the user hits 'answer' and Telecomm receives the command.
+ if (isRinging("answer")) {
+ // At this point, we are asking the call service to answer but we don't assume that
+ // it will work. Instead, we wait until confirmation from the call service that the
+ // call is in a non-RINGING state before changing the UI. See
+ // {@link CallServiceAdapter#setActive} and other set* methods.
+ mCallService.answer(mId);
+ }
+ }
+
+ /**
+ * Rejects the call if it is ringing.
+ */
+ void reject() {
+ Preconditions.checkNotNull(mCallService);
+
+ // Check to verify that the call is still in the ringing state. A call can change states
+ // between the time the user hits 'reject' and Telecomm receives the command.
+ if (isRinging("reject")) {
+ mCallService.reject(mId);
+ }
+ }
+
+ /**
* @return An object containing read-only information about this call.
*/
CallInfo toCallInfo() {
@@ -157,6 +192,18 @@
}
/**
+ * @return True if the call is ringing, else logs the action name.
+ */
+ private boolean isRinging(String actionName) {
+ if (mState == CallState.RINGING) {
+ return true;
+ }
+
+ Log.i(TAG, "Request to " + actionName + " a non-ringing call " + this);
+ return false;
+ }
+
+ /**
* Resets the cached read-only version of this call.
*/
private void clearCallInfo() {
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
index 9a9d0db..9e20d0c 100644
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ b/src/com/android/telecomm/CallServiceAdapter.java
@@ -47,13 +47,11 @@
/** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper());
- /** The list of unconfirmed incoming call IDs. Contains only IDs for incoming calls which are
- * pending confirmation from the call service. Entries are added by the call service when a
- * confirmation request is sent and removed when the confirmation is received or it times out.
- * See {@link IncomingCallsManager} for more information about the incoming sequence and its
- * timeouts.
+ /** The set of pending incoming call IDs. Contains the call IDs for which we are expecting
+ * details via {@link #handleIncomingCall}. If {@link #handleIncomingCall} is invoked for a call
+ * ID that is not in this set, it will be ignored.
*/
- private final Set<String> mUnconfirmedIncomingCallIds = Sets.newHashSet();
+ private final Set<String> mPendingIncomingCallIds = Sets.newHashSet();
/**
* Persists the specified parameters.
@@ -69,15 +67,15 @@
}
/** {@inheritDoc} */
- @Override public void handleConfirmedIncomingCall(final CallInfo callInfo) {
+ @Override public void handleIncomingCall(final CallInfo callInfo) {
checkValidCallId(callInfo.getId());
mHandler.post(new Runnable() {
@Override public void run() {
- if (mUnconfirmedIncomingCallIds.remove(callInfo.getId())) {
+ if (mPendingIncomingCallIds.remove(callInfo.getId())) {
// TODO(santoscordon): Uncomment when ready.
// mIncomingCallsManager.handleSuccessfulIncomingCall(callInfo);
} else {
- Log.wtf(TAG, "Call service confirming unknown incoming call " + callInfo);
+ Log.wtf(TAG, "Received details for an unknown incoming call " + callInfo);
}
}
});
@@ -144,22 +142,22 @@
}
/**
- * Adds a call ID to the list of unconfirmed incoming call IDs. Only calls with call IDs in the
- * list will be handled by {@link #handleConfirmedIncomingCall}.
+ * Adds a call ID to the list of pending incoming call IDs. Only calls with call IDs in the
+ * list will be handled by {@link #handleIncomingCall}.
*
* @param callId The ID of the call.
*/
- void addUnconfirmedIncomingCallId(String callId) {
- mUnconfirmedIncomingCallIds.add(callId);
+ void addPendingIncomingCallId(String callId) {
+ mPendingIncomingCallIds.add(callId);
}
/**
- * Removed a call ID from the list of unconfirmed incoming call IDs.
+ * Removed a call ID from the list of pending incoming call IDs.
*
* @param callId The ID of the call.
*/
- void removeUnconfirmedIncomingCallId(String callId) {
- mUnconfirmedIncomingCallIds.remove(callId);
+ void removePendingIncomingCallId(String callId) {
+ mPendingIncomingCallIds.remove(callId);
}
/**
diff --git a/src/com/android/telecomm/CallServiceWrapper.java b/src/com/android/telecomm/CallServiceWrapper.java
index 941c6ee..5d2162a 100644
--- a/src/com/android/telecomm/CallServiceWrapper.java
+++ b/src/com/android/telecomm/CallServiceWrapper.java
@@ -55,7 +55,8 @@
/**
* Creates a call-service provider for the specified component.
*
- * @param descriptor The call-service descriptor from {@link ICallServiceProvider#lookupCallServices}.
+ * @param descriptor The call-service descriptor from
+ * {@link ICallServiceProvider#lookupCallServices}.
* @param adapter The call-service adapter.
*/
public CallServiceWrapper(CallServiceDescriptor descriptor, CallServiceAdapter adapter) {
@@ -70,80 +71,92 @@
/** See {@link ICallService#setCallServiceAdapter}. */
public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
- try {
- if (mServiceInterface == null) {
- Log.wtf(TAG, "setCallServiceAdapter() invoked while the service is unbound.");
- } else {
+ if (isServiceValid("setCallServiceAdapter")) {
+ try {
mServiceInterface.setCallServiceAdapter(callServiceAdapter);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to setCallServiceAdapter.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to setCallServiceAdapter.", e);
}
}
/** See {@link ICallService#isCompatibleWith}. */
public void isCompatibleWith(CallInfo callInfo) {
- try {
- if (mServiceInterface == null) {
- Log.wtf(TAG, "isCompatibleWith() invoked while the service is unbound.");
- } else {
+ if (isServiceValid("isCompatibleWith")) {
+ try {
mServiceInterface.isCompatibleWith(callInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed checking isCompatibleWith.", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Failed checking isCompatibleWith.", e);
}
}
/** See {@link ICallService#call}. */
public void call(CallInfo callInfo) {
- try {
- if (mServiceInterface == null) {
- Log.wtf(TAG, "call() invoked while the service is unbound.");
- } else {
+ if (isServiceValid("call")) {
+ try {
mServiceInterface.call(callInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to place call " + callInfo.getId() + ".", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to place call " + callInfo.getId() + ".", e);
+ }
+ }
+
+ /** See {@link ICallService#setIncomingCallId}. */
+ public void setIncomingCallId(String callId) {
+ if (isServiceValid("setIncomingCallId")) {
+ mAdapter.addPendingIncomingCallId(callId);
+ try {
+ mServiceInterface.setIncomingCallId(callId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to setIncomingCallId for call " + callId, e);
+ mAdapter.removePendingIncomingCallId(callId);
+ }
}
}
/** See {@link ICallService#disconnect}. */
public void disconnect(String callId) {
- try {
- if (mServiceInterface == null) {
- Log.wtf(TAG, "disconnect() invoked while the service is unbound.");
- } else {
+ if (isServiceValid("disconnect")) {
+ try {
mServiceInterface.disconnect(callId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to disconnect call " + callId + ".", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to disconnect call " + callId + ".", e);
}
}
- /** See {@link ICallService#confirmIncomingCall}. */
- public void confirmIncomingCall(String callId, String callToken) {
- try {
- if (mServiceInterface == null) {
- Log.wtf(TAG, "confirmIncomingCall() invoked while service in unbound.");
- } else {
- mAdapter.addUnconfirmedIncomingCallId(callId);
- mServiceInterface.confirmIncomingCall(callId, callToken);
+ /** See {@link ICallService#answer}. */
+ public void answer(String callId) {
+ if (isServiceValid("answer")) {
+ try {
+ mServiceInterface.answer(callId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to answer call " + callId, e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to confirmIncomingCall for call " + callId, e);
- mAdapter.removeUnconfirmedIncomingCallId(callId);
+ }
+ }
+
+ /** See {@link ICallService#reject}. */
+ public void reject(String callId) {
+ if (isServiceValid("reject")) {
+ try {
+ mServiceInterface.reject(callId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to reject call " + callId, e);
+ }
}
}
/**
- * Cancels the an incoming call confirmation for the specified call ID.
- * TODO(santoscordon): This method should be called by IncomingCallManager when the incoming
- * call confirmation has failed.
+ * Cancels the incoming call for the specified call ID.
+ * TODO(santoscordon): This method should be called by IncomingCallsManager when the incoming
+ * call has failed.
*
* @param callId The ID of the call.
*/
void cancelIncomingCall(String callId) {
- mAdapter.removeUnconfirmedIncomingCallId(callId);
+ mAdapter.removePendingIncomingCallId(callId);
}
/** {@inheritDoc} */
@@ -151,4 +164,13 @@
mServiceInterface = ICallService.Stub.asInterface(binder);
setCallServiceAdapter(mAdapter);
}
+
+ private boolean isServiceValid(String actionName) {
+ if (mServiceInterface != null) {
+ return true;
+ }
+
+ Log.wtf(TAG, actionName + " invoked while service is unbound");
+ return false;
+ }
}
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index aebee13..d79669a 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -94,6 +94,7 @@
* @param callToken The token used by the call service to identify the incoming call.
*/
void processIncomingCallIntent(CallServiceDescriptor descriptor, String callToken) {
+ Log.d(TAG, "processIncomingCallIntent");
// 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.
@@ -146,6 +147,7 @@
* @param call The new incoming call.
*/
void handleSuccessfulIncomingCall(Call call) {
+ Log.d(TAG, "handleSuccessfulIncomingCall");
Preconditions.checkState(call.getState() == CallState.RINGING);
addCall(call);
mInCallController.addCall(call.toCallInfo());
@@ -174,7 +176,16 @@
* @param callId The ID of the call.
*/
void answerCall(String callId) {
- // TODO(santoscordon): fill in and check that it is in the ringing state.
+ Call call = mCalls.get(callId);
+ if (call == null) {
+ Log.i(TAG, "Request to answer a non-existent call " + callId);
+ } else {
+ // We do not update the UI until we get confirmation of the answer() through
+ // {@link #markCallAsActive}. However, if we ever change that to look more responsive,
+ // then we need to make sure we add a timeout for the answer() in case the call never
+ // comes out of RINGING.
+ call.answer();
+ }
}
/**
@@ -185,7 +196,12 @@
* @param callId The ID of the call.
*/
void rejectCall(String callId) {
- // TODO(santoscordon): fill in and check that it is in the ringing state.
+ Call call = mCalls.get(callId);
+ if (call == null) {
+ Log.i(TAG, "Request to reject a non-existent call " + callId);
+ } else {
+ call.reject();
+ }
}
/**
diff --git a/src/com/android/telecomm/InCallAdapter.java b/src/com/android/telecomm/InCallAdapter.java
index eb10318..9faa472 100644
--- a/src/com/android/telecomm/InCallAdapter.java
+++ b/src/com/android/telecomm/InCallAdapter.java
@@ -20,6 +20,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.telecomm.IInCallAdapter;
+import android.util.Log;
/**
* Receives call commands and updates from in-call app and passes them through to CallsManager.
@@ -27,6 +28,9 @@
* binding to it. This adapter can receive commands and updates until the in-call app is unbound.
*/
class InCallAdapter extends IInCallAdapter.Stub {
+
+ private static final String TAG = InCallAdapter.class.getSimpleName();
+
private final CallsManager mCallsManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -39,6 +43,7 @@
/** {@inheritDoc} */
@Override
public void answerCall(final String callId) throws RemoteException {
+ Log.d(TAG, "answerCall(" + callId + ")");
mHandler.post(new Runnable() {
@Override public void run() {
mCallsManager.answerCall(callId);
@@ -49,6 +54,7 @@
/** {@inheritDoc} */
@Override
public void rejectCall(final String callId) throws RemoteException {
+ Log.d(TAG, "rejectCall(" + callId + ")");
mHandler.post(new Runnable() {
@Override public void run() {
mCallsManager.rejectCall(callId);
diff --git a/tests/src/com/android/telecomm/testcallservice/TestCallService.java b/tests/src/com/android/telecomm/testcallservice/TestCallService.java
index d392a79..7c0d5c0 100644
--- a/tests/src/com/android/telecomm/testcallservice/TestCallService.java
+++ b/tests/src/com/android/telecomm/testcallservice/TestCallService.java
@@ -21,19 +21,15 @@
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
-import java.util.Date;
import java.util.Set;
-import android.content.Context;
import android.content.Intent;
-import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.RemoteException;
import android.telecomm.CallInfo;
import android.telecomm.CallService;
import android.telecomm.CallState;
import android.telecomm.ICallServiceAdapter;
-import android.text.TextUtils;
import android.util.Log;
/**
@@ -44,11 +40,6 @@
private static final String TAG = TestCallService.class.getSimpleName();
/**
- * The application context.
- */
- private final Context mContext;
-
- /**
* Set of call IDs for live (active, ringing, dialing) calls.
* TODO(santoscordon): Reference CallState javadoc when available for the different call states.
*/
@@ -64,11 +55,6 @@
*/
private MediaPlayer mMediaPlayer;
- /** Persists the specified parameters. */
- public TestCallService(Context context) {
- mContext = context;
- }
-
/** {@inheritDoc} */
@Override
public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
@@ -78,7 +64,7 @@
mLiveCallIds = Sets.newHashSet();
// Prepare the media player to play a tone when there is a call.
- mMediaPlayer = MediaPlayer.create(mContext, R.raw.beep_boop);
+ mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.beep_boop);
mMediaPlayer.setLooping(true);
// TODO(santoscordon): Re-enable audio through voice-call stream.
@@ -144,17 +130,37 @@
/** {@inheritDoc} */
@Override
- public void confirmIncomingCall(String callId, String callToken) {
- Log.i(TAG, "confirmIncomingCall(" + callId + ", " + callToken + ")");
+ public void setIncomingCallId(String callId) {
+ Log.i(TAG, "setIncomingCallId(" + callId + ")");
// Use dummy number for testing incoming calls.
String handle = "5551234";
CallInfo callInfo = new CallInfo(callId, CallState.RINGING, handle);
try {
- mCallsManagerAdapter.handleConfirmedIncomingCall(callInfo);
+ mCallsManagerAdapter.handleIncomingCall(callInfo);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to handleConfirmedIncomingCall().", e);
+ Log.e(TAG, "Failed to handleIncomingCall().", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void answer(String callId) {
+ try {
+ mCallsManagerAdapter.setActive(callId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to setActive the call " + callId);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void reject(String callId) {
+ try {
+ mCallsManagerAdapter.setDisconnected(callId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to setDisconnected the call " + callId);
}
}