Merge "Adding answer/reject API support to Telecomm." into master-nova
This commit is contained in:
commit
83a4aa7d71
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue