Merge "Adding answer/reject API support to Telecomm." into master-nova

This commit is contained in:
Santos Cordon 2014-02-25 02:37:14 +00:00 committed by Android (Google) Code Review
commit 83a4aa7d71
6 changed files with 188 additions and 93 deletions

View File

@ -21,7 +21,10 @@ import android.telecomm.CallInfo;
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 @@ import java.util.Date;
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 @@ final class Call {
*/
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 @@ final class Call {
* @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 @@ final class Call {
return mHandle;
}
void setHandle(String handle) {
mHandle = handle;
}
ContactInfo getContactInfo() {
return mContactInfo;
}
@ -146,6 +151,36 @@ final class Call {
}
}
/**
* 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.
*/
@ -156,6 +191,18 @@ final class Call {
return mCallInfo;
}
/**
* @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.
*/

View File

@ -47,13 +47,11 @@ public final class CallServiceAdapter extends ICallServiceAdapter.Stub {
/** 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 @@ public final class CallServiceAdapter extends ICallServiceAdapter.Stub {
}
/** {@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 @@ public final class CallServiceAdapter extends ICallServiceAdapter.Stub {
}
/**
* 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);
}
/**

View File

@ -55,7 +55,8 @@ public class CallServiceWrapper extends ServiceBinder<ICallService> {
/**
* 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 @@ public class CallServiceWrapper extends ServiceBinder<ICallService> {
/** 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);
}
}
}
/** 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);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to place call " + callInfo.getId() + ".", e);
}
}
/** 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);
}
}
}
/** 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);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to confirmIncomingCall for call " + callId, e);
mAdapter.removeUnconfirmedIncomingCallId(callId);
}
}
/**
* 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 @@ public class CallServiceWrapper extends ServiceBinder<ICallService> {
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;
}
}

View File

@ -93,6 +93,7 @@ public final class CallsManager {
* @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.
@ -144,6 +145,7 @@ public final class CallsManager {
* @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());
@ -172,7 +174,16 @@ public final class CallsManager {
* @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();
}
}
/**
@ -183,7 +194,12 @@ public final class CallsManager {
* @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();
}
}
/**

View File

@ -20,6 +20,7 @@ import android.os.Handler;
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 @@ import android.telecomm.IInCallAdapter;
* 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 @@ class InCallAdapter extends IInCallAdapter.Stub {
/** {@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 @@ class InCallAdapter extends IInCallAdapter.Stub {
/** {@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);

View File

@ -21,19 +21,15 @@ import com.google.common.base.Preconditions;
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;
/**
@ -43,11 +39,6 @@ import android.util.Log;
public class TestCallService extends CallService {
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 @@ public class TestCallService extends CallService {
*/
private MediaPlayer mMediaPlayer;
/** Persists the specified parameters. */
public TestCallService(Context context) {
mContext = context;
}
/** {@inheritDoc} */
@Override
public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
@ -78,7 +64,7 @@ public class TestCallService extends CallService {
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 @@ public class TestCallService extends CallService {
/** {@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);
}
}