Add support for remote incoming calls: impl
This CL changes how incoming calls are routed. We now
treat incoming calls the same as outgoing calls.
This allows a ConnectionService to attach to a incoming call
using a remote connection.
Change-Id: I5232d062ad3b559f4fe7c8224e7234b2c6bf8431
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index e6a3dbc..5f28df8 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -57,7 +57,7 @@
* from the time the call intent was received by Telecomm (vs. the time the call was
* connected etc).
*/
-final class Call implements OutgoingCallResponse {
+final class Call implements CreateConnectionResponse {
/**
* Listener for events on the call.
*/
@@ -147,14 +147,19 @@
}
};
+ private final Runnable mDirectToVoicemailRunnable = new Runnable() {
+ @Override
+ public void run() {
+ processDirectToVoicemail();
+ }
+ };
+
/** True if this is an incoming call. */
private final boolean mIsIncoming;
/**
- * The time this call was created, typically also the time this call was added to the set
- * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
- * Beyond logging and such, may also be used for bookkeeping and specifically for marking
- * certain call attempts as failed attempts.
+ * The time this call was created. Beyond logging and such, may also be used for bookkeeping
+ * and specifically for marking certain call attempts as failed attempts.
*/
private final long mCreationTimeMillis = System.currentTimeMillis();
@@ -213,7 +218,7 @@
/** Set of listeners on this call. */
private Set<Listener> mListeners = Sets.newHashSet();
- private OutgoingCallProcessor mOutgoingCallProcessor;
+ private CreateConnectionProcessor mCreateConnectionProcessor;
/** Caller information retrieved from the latest contact query. */
private CallerInfo mCallerInfo;
@@ -245,6 +250,7 @@
private boolean mAudioModeIsVoip;
private StatusHints mStatusHints;
+ private final ConnectionServiceRepository mRepository;
/**
* Persists the specified parameters and initializes the new instance.
@@ -254,9 +260,15 @@
* @param account Account information to use for the call.
* @param isIncoming True if this is an incoming call.
*/
- Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
- boolean isIncoming, boolean isConference) {
+ Call(
+ ConnectionServiceRepository repository,
+ Uri handle,
+ GatewayInfo gatewayInfo,
+ PhoneAccount account,
+ boolean isIncoming,
+ boolean isConference) {
mState = isConference ? CallState.ACTIVE : CallState.NEW;
+ mRepository = repository;
setHandle(handle, CallPropertyPresentation.ALLOWED);
mGatewayInfo = gatewayInfo;
mPhoneAccount = account;
@@ -499,43 +511,7 @@
}
}
- /**
- * Starts the incoming call flow through the switchboard. When switchboard completes, it will
- * invoke handle[Un]SuccessfulIncomingCall.
- */
- void startIncoming() {
- Switchboard.getInstance().retrieveIncomingCall(this);
- }
-
- /**
- * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
- * from the contacts provider. The call is not yet exposed to the user at this point and
- * the result of the query will determine if the call is rejected or passed through to the
- * in-call UI.
- */
- void handleVerifiedIncoming(ConnectionRequest request) {
- mPhoneAccount = request.getAccount();
-
- // We do not handle incoming calls immediately when they are verified by the connection
- // service. We allow the caller-info-query code to execute first so that we can read the
- // direct-to-voicemail property before deciding if we want to show the incoming call to the
- // user or if we want to reject the call.
- mDirectToVoicemailQueryPending = true;
-
- // Setting the handle triggers the caller info lookup code.
- setHandle(request.getHandle(), CallPropertyPresentation.ALLOWED);
-
- // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
- // showing the user the incoming call screen.
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- processDirectToVoicemail();
- }
- }, Timeouts.getDirectToVoicemailMillis());
- }
-
- void processDirectToVoicemail() {
+ private void processDirectToVoicemail() {
if (mDirectToVoicemailQueryPending) {
if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
Log.i(this, "Directing call to voicemail: %s.", this);
@@ -557,60 +533,81 @@
}
}
- void handleFailedIncoming() {
- clearConnectionService();
-
- // TODO: Needs more specific disconnect error for this case.
- setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
- setState(CallState.DISCONNECTED);
-
- // TODO(santoscordon): Replace this with state transitions related to "connecting".
- for (Listener l : mListeners) {
- l.onFailedIncomingCall(this);
- }
- }
-
/**
- * Starts the outgoing call sequence. Upon completion, there should exist an active connection
- * through a connection service (or the call will have failed).
+ * Starts the create connection sequence. Upon completion, there should exist an active
+ * connection through a connection service (or the call will have failed).
*/
- void startOutgoing() {
- Preconditions.checkState(mOutgoingCallProcessor == null);
-
- mOutgoingCallProcessor = new OutgoingCallProcessor(
- this, Switchboard.getInstance().getConnectionServiceRepository(), this);
- mOutgoingCallProcessor.process();
+ void startCreateConnection() {
+ Preconditions.checkState(mCreateConnectionProcessor == null);
+ mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
+ mCreateConnectionProcessor.process();
}
@Override
- public void onOutgoingCallSuccess() {
- // TODO(santoscordon): Replace this with state transitions related to "connecting".
- for (Listener l : mListeners) {
- l.onSuccessfulOutgoingCall(this);
+ public void handleCreateConnectionSuccessful(ConnectionRequest request) {
+ mCreateConnectionProcessor = null;
+ mPhoneAccount = request.getAccount();
+
+ if (mIsIncoming) {
+ // We do not handle incoming calls immediately when they are verified by the connection
+ // service. We allow the caller-info-query code to execute first so that we can read the
+ // direct-to-voicemail property before deciding if we want to show the incoming call to
+ // the user or if we want to reject the call.
+ mDirectToVoicemailQueryPending = true;
+
+ // Setting the handle triggers the caller info lookup code.
+ setHandle(request.getHandle(), request.getHandlePresentation());
+
+ // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
+ // showing the user the incoming call screen.
+ mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
+ } else {
+ for (Listener l : mListeners) {
+ l.onSuccessfulOutgoingCall(this);
+ }
}
- mOutgoingCallProcessor = null;
}
@Override
- public void onOutgoingCallFailure(int code, String msg) {
- // TODO(santoscordon): Replace this with state transitions related to "connecting".
- for (Listener l : mListeners) {
- l.onFailedOutgoingCall(this, code, msg);
- }
+ public void handleCreateConnectionFailed(int code, String msg) {
+ mCreateConnectionProcessor = null;
+ if (mIsIncoming) {
+ clearConnectionService();
+ setDisconnectCause(code, null);
+ setState(CallState.DISCONNECTED);
- clearConnectionService();
- mOutgoingCallProcessor = null;
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onFailedIncomingCall(this);
+ }
+ } else {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onFailedOutgoingCall(this, code, msg);
+ }
+ clearConnectionService();
+ }
}
@Override
- public void onOutgoingCallCancel() {
- // TODO(santoscordon): Replace this with state transitions related to "connecting".
- for (Listener l : mListeners) {
- l.onCancelledOutgoingCall(this);
- }
+ public void handleCreateConnectionCancelled() {
+ mCreateConnectionProcessor = null;
+ if (mIsIncoming) {
+ clearConnectionService();
+ setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
+ setState(CallState.DISCONNECTED);
- clearConnectionService();
- mOutgoingCallProcessor = null;
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onFailedIncomingCall(this);
+ }
+ } else {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onCancelledOutgoingCall(this);
+ }
+ clearConnectionService();
+ }
}
/**
@@ -656,8 +653,8 @@
}
void abort() {
- if (mOutgoingCallProcessor != null) {
- mOutgoingCallProcessor.abort();
+ if (mCreateConnectionProcessor != null) {
+ mCreateConnectionProcessor.abort();
}
}
diff --git a/src/com/android/telecomm/CallActivity.java b/src/com/android/telecomm/CallActivity.java
index 0086c99..0f625c7 100644
--- a/src/com/android/telecomm/CallActivity.java
+++ b/src/com/android/telecomm/CallActivity.java
@@ -113,10 +113,13 @@
return;
}
- Bundle clientExtras = Bundle.EMPTY;
+ Bundle clientExtras = null;
if (intent.hasExtra(TelecommConstants.EXTRA_INCOMING_CALL_EXTRAS)) {
clientExtras = intent.getBundleExtra(TelecommConstants.EXTRA_INCOMING_CALL_EXTRAS);
}
+ if (clientExtras == null) {
+ clientExtras = Bundle.EMPTY;
+ }
Log.d(this, "Processing incoming call from connection service [%s]",
phoneAccount.getComponentName());
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 2d5d5c0..5d07176 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -67,6 +67,8 @@
*/
private final Set<Call> mCalls = new CopyOnWriteArraySet<Call>();
+ private final ConnectionServiceRepository mConnectionServiceRepository =
+ new ConnectionServiceRepository();
private final DtmfLocalTonePlayer mDtmfLocalTonePlayer = new DtmfLocalTonePlayer();
private final InCallController mInCallController = new InCallController();
private final CallAudioManager mCallAudioManager;
@@ -230,9 +232,7 @@
}
/**
- * Starts the incoming call sequence by having switchboard gather more information about the
- * specified call; using the specified connection service component name. Upon success,
- * execution returns to {@link #onSuccessfulIncomingCall} to start the in-call UI.
+ * Starts the process to attach the call to a connection service.
*
* @param phoneAccount The phone account which contains the component name of the connection
* serivce to use for this call.
@@ -240,16 +240,20 @@
*/
void processIncomingCallIntent(PhoneAccount phoneAccount, Bundle extras) {
Log.d(this, "processIncomingCallIntent");
- // Create a call with no handle. Eventually, switchboard will update the call with
- // additional information from the connection service, but for now we just need one to pass
- // around.
+ // Create a call with no handle. The handle is eventually set when the call is attached
+ // to a connection service.
Call call = new Call(
- null, null, phoneAccount, true /* isIncoming */, false /* isConference */);
+ mConnectionServiceRepository,
+ null /* handle */,
+ null /* gatewayInfo */,
+ phoneAccount,
+ true /* isIncoming */,
+ false /* isConference */);
+
call.setExtras(extras);
// TODO(santoscordon): Move this to be a part of addCall()
call.addListener(this);
-
- call.startIncoming();
+ call.startCreateConnection();
}
/**
@@ -275,15 +279,19 @@
}
Call call = new Call(
- uriHandle, gatewayInfo, account, false /* isIncoming */, false /* isConference */);
+ mConnectionServiceRepository,
+ uriHandle,
+ gatewayInfo,
+ account,
+ false /* isIncoming */,
+ false /* isConference */);
call.setStartWithSpeakerphoneOn(speakerphoneOn);
call.setVideoState(videoState);
// TODO(santoscordon): Move this to be a part of addCall()
call.addListener(this);
addCall(call);
-
- call.startOutgoing();
+ call.startCreateConnection();
}
/**
@@ -293,7 +301,12 @@
*/
void conference(Call call) {
Call conferenceCall = new Call(
- null, null, null, false /* isIncoming */, true /* isConference */);
+ mConnectionServiceRepository,
+ null /* handle */,
+ null /* gatewayInfo */,
+ null /* phoneAccount */,
+ false /* isIncoming */,
+ true /* isConference */);
conferenceCall.addListener(this);
call.conferenceInto(conferenceCall);
}
diff --git a/src/com/android/telecomm/ConnectionServiceRepository.java b/src/com/android/telecomm/ConnectionServiceRepository.java
index 7672e30..598bdd1 100644
--- a/src/com/android/telecomm/ConnectionServiceRepository.java
+++ b/src/com/android/telecomm/ConnectionServiceRepository.java
@@ -32,12 +32,10 @@
*/
final class ConnectionServiceRepository
implements ServiceBinder.Listener<ConnectionServiceWrapper> {
- private final IncomingCallsManager mIncomingCallsManager;
private final HashMap<ComponentName, ConnectionServiceWrapper> mServiceCache =
new HashMap<ComponentName, ConnectionServiceWrapper>();
- ConnectionServiceRepository(IncomingCallsManager incomingCallsManager) {
- mIncomingCallsManager = incomingCallsManager;
+ ConnectionServiceRepository() {
}
Collection<ConnectionServiceWrapper> lookupServices() {
@@ -58,7 +56,7 @@
ConnectionServiceWrapper getService(ComponentName componentName) {
ConnectionServiceWrapper service = mServiceCache.get(componentName);
if (service == null) {
- service = new ConnectionServiceWrapper(componentName, mIncomingCallsManager, this);
+ service = new ConnectionServiceWrapper(componentName, this);
service.addListener(this);
mServiceCache.put(componentName, service);
}
diff --git a/src/com/android/telecomm/ConnectionServiceWrapper.java b/src/com/android/telecomm/ConnectionServiceWrapper.java
index 1b0dcdf..870d61c 100644
--- a/src/com/android/telecomm/ConnectionServiceWrapper.java
+++ b/src/com/android/telecomm/ConnectionServiceWrapper.java
@@ -56,87 +56,67 @@
* {@link IConnectionService}.
*/
final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {
- 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_CANCEL_OUTGOING_CALL = 4;
- private static final int MSG_SET_ACTIVE = 5;
- private static final int MSG_SET_RINGING = 6;
- private static final int MSG_SET_DIALING = 7;
- private static final int MSG_SET_DISCONNECTED = 8;
- private static final int MSG_SET_ON_HOLD = 9;
- private static final int MSG_SET_REQUESTING_RINGBACK = 10;
- private static final int MSG_SET_CALL_CAPABILITIES = 11;
- private static final int MSG_SET_IS_CONFERENCED = 12;
- private static final int MSG_ADD_CONFERENCE_CALL = 13;
- private static final int MSG_REMOVE_CALL = 14;
- private static final int MSG_ON_POST_DIAL_WAIT = 15;
- private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 16;
- private static final int MSG_SET_CALL_VIDEO_PROVIDER = 17;
- private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18;
- private static final int MSG_SET_STATUS_HINTS = 19;
- private static final int MSG_SET_HANDLE = 20;
- private static final int MSG_SET_CALLER_DISPLAY_NAME = 21;
+ private static final int MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL = 1;
+ private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2;
+ private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 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 static final int MSG_SET_REQUESTING_RINGBACK = 9;
+ private static final int MSG_SET_CALL_CAPABILITIES = 10;
+ private static final int MSG_SET_IS_CONFERENCED = 11;
+ private static final int MSG_ADD_CONFERENCE_CALL = 12;
+ private static final int MSG_REMOVE_CALL = 13;
+ private static final int MSG_ON_POST_DIAL_WAIT = 14;
+ private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15;
+ private static final int MSG_SET_CALL_VIDEO_PROVIDER = 16;
+ private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 17;
+ private static final int MSG_SET_STATUS_HINTS = 18;
+ private static final int MSG_SET_HANDLE = 19;
+ private static final int MSG_SET_CALLER_DISPLAY_NAME = 20;
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Call call;
switch (msg.what) {
- case MSG_NOTIFY_INCOMING_CALL: {
+ case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: {
ConnectionRequest request = (ConnectionRequest) msg.obj;
- call = mCallIdMapper.getCall(request.getCallId());
- if (call != null && mPendingIncomingCalls.remove(call) &&
- call.isIncoming()) {
- mIncomingCallsManager.handleSuccessfulIncomingCall(call, request);
+ if (mPendingResponses.containsKey(request.getCallId())) {
+ mPendingResponses.remove(
+ request.getCallId()).handleCreateConnectionSuccessful(request);
} else {
- // TODO(santoscordon): For this an the other commented logging, we need
- // to reenable it. At the moment all ConnectionServiceAdapters receive
- // notification of changes to all calls, even calls which it may not own
- // (ala remote connections). We need to fix that and then uncomment the
- // logging calls here.
- //Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
- // call, request.getId());
+ //Log.w(this, "handleCreateConnectionSuccessful, unknown call: %s", callId);
}
break;
}
- case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
- ConnectionRequest request = (ConnectionRequest) msg.obj;
- if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
- mPendingOutgoingCalls.remove(
- request.getCallId()).onOutgoingCallSuccess();
- } else {
- //Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
- }
- break;
- }
- case MSG_HANDLE_FAILED_OUTGOING_CALL: {
+ case MSG_HANDLE_CREATE_CONNECTION_FAILED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
ConnectionRequest request = (ConnectionRequest) args.arg1;
int statusCode = args.argi1;
String statusMsg = (String) args.arg2;
- // TODO(santoscordon): Do something with 'reason' or get rid of it.
-
- if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
- mPendingOutgoingCalls.remove(request.getCallId())
- .onOutgoingCallFailure(statusCode, statusMsg);
+ if (mPendingResponses.containsKey(request.getCallId())) {
+ mPendingResponses.remove(request.getCallId())
+ .handleCreateConnectionFailed(statusCode, statusMsg);
mCallIdMapper.removeCall(request.getCallId());
} else {
- //Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
+ //Log.w(this, "handleCreateConnectionFailed, unknown call: %s", callId);
}
} finally {
args.recycle();
}
break;
}
- case MSG_CANCEL_OUTGOING_CALL: {
+ case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: {
ConnectionRequest request = (ConnectionRequest) msg.obj;
- if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
- mPendingOutgoingCalls.remove(
- request.getCallId()).onOutgoingCallCancel();
+ if (mPendingResponses.containsKey(request.getCallId())) {
+ mPendingResponses.remove(
+ request.getCallId()).handleCreateConnectionCancelled();
} else {
- //Log.w(this, "cancelOutgoingCall, unknown call: %s", callId);
+ //Log.w(this, "handleCreateConnectionCancelled, unknown call: %s", callId);
}
break;
}
@@ -268,8 +248,7 @@
break;
}
case MSG_QUERY_REMOTE_CALL_SERVICES: {
- ConnectionServiceWrapper.this.queryRemoteConnectionServices(
- (RemoteServiceCallback) msg.obj);
+ queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
break;
}
case MSG_SET_CALL_VIDEO_PROVIDER: {
@@ -335,38 +314,29 @@
private final class Adapter extends IConnectionServiceAdapter.Stub {
@Override
- public void notifyIncomingCall(ConnectionRequest request) {
- logIncoming("notifyIncomingCall %s", request);
+ public void handleCreateConnectionSuccessful(ConnectionRequest request) {
+ logIncoming("handleCreateConnectionSuccessful %s", request);
mCallIdMapper.checkValidCallId(request.getCallId());
- mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, request).sendToTarget();
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, request).sendToTarget();
}
@Override
- public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
- logIncoming("handleSuccessfulOutgoingCall %s", request);
- mCallIdMapper.checkValidCallId(request.getCallId());
- mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, request).sendToTarget();
- }
-
- @Override
- public void handleFailedOutgoingCall(
- ConnectionRequest request,
- int errorCode,
- String errorMsg) {
- logIncoming("handleFailedOutgoingCall %s %d %s", request, errorCode, errorMsg);
+ public void handleCreateConnectionFailed(
+ ConnectionRequest request, int errorCode, String errorMsg) {
+ logIncoming("handleCreateConnectionFailed %s %d %s", request, errorCode, errorMsg);
mCallIdMapper.checkValidCallId(request.getCallId());
SomeArgs args = SomeArgs.obtain();
args.arg1 = request;
args.argi1 = errorCode;
args.arg2 = errorMsg;
- mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget();
}
@Override
- public void cancelOutgoingCall(ConnectionRequest request) {
- logIncoming("cancelOutgoingCall %s", request);
+ public void handleCreateConnectionCancelled(ConnectionRequest request) {
+ logIncoming("handleCreateConnectionCancelled %s", request);
mCallIdMapper.checkValidCallId(request.getCallId());
- mHandler.obtainMessage(MSG_CANCEL_OUTGOING_CALL, request).sendToTarget();
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget();
}
@Override
@@ -517,11 +487,9 @@
private final Adapter mAdapter = new Adapter();
private final CallsManager mCallsManager = CallsManager.getInstance();
- private final Set<Call> mPendingIncomingCalls = new HashSet<>();
private final Set<Call> mPendingConferenceCalls = new HashSet<>();
private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
- private final IncomingCallsManager mIncomingCallsManager;
- private final Map<String, OutgoingCallResponse> mPendingOutgoingCalls = new HashMap<>();
+ private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
private Binder mBinder = new Binder();
private IConnectionService mServiceInterface;
@@ -531,15 +499,11 @@
* Creates a connection service.
*
* @param componentName The component name of the service with which to bind.
- * @param incomingCallsManager Manages the incoming call initialization flow.
* @param connectionServiceRepository Connection service repository.
*/
ConnectionServiceWrapper(
- ComponentName componentName,
- IncomingCallsManager incomingCallsManager,
- ConnectionServiceRepository connectionServiceRepository) {
+ ComponentName componentName, ConnectionServiceRepository connectionServiceRepository) {
super(TelecommConstants.ACTION_CONNECTION_SERVICE, componentName);
- mIncomingCallsManager = incomingCallsManager;
mConnectionServiceRepository = connectionServiceRepository;
}
@@ -555,16 +519,15 @@
}
/**
- * Attempts to place the specified call, see {@link IConnectionService#call}. Returns the result
- * asynchronously through the specified callback.
+ * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
*/
- void call(final Call call, final OutgoingCallResponse callResponse) {
- Log.d(this, "call(%s) via %s.", call, getComponentName());
+ void createConnection(final Call call, final CreateConnectionResponse response) {
+ Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
BindCallback callback = new BindCallback() {
@Override
public void onSuccess() {
String callId = mCallIdMapper.getCallId(call);
- mPendingOutgoingCalls.put(callId, callResponse);
+ mPendingResponses.put(callId, response);
GatewayInfo gatewayInfo = call.getGatewayInfo();
Bundle extras = call.getExtras();
@@ -587,10 +550,10 @@
call.getVideoState());
try {
- mServiceInterface.call(request);
+ mServiceInterface.createConnection(request, call.isIncoming());
} catch (RemoteException e) {
- Log.e(this, e, "Failure to call -- %s", getComponentName());
- mPendingOutgoingCalls.remove(callId).onOutgoingCallFailure(
+ Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
+ mPendingResponses.remove(callId).handleCreateConnectionFailed(
DisconnectCause.ERROR_UNSPECIFIED, e.toString());
}
}
@@ -598,7 +561,7 @@
@Override
public void onFailure() {
Log.e(this, new Exception(), "Failure to call %s", getComponentName());
- callResponse.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
+ response.handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
}
};
@@ -657,47 +620,6 @@
}
}
- /**
- * Starts retrieval of details for an incoming call. Details are returned through the connection
- * service adapter using the specified call ID. Upon failure, the specified error callback is
- * invoked. Can be invoked even when the connection service is unbound. See
- * {@link IConnectionService#createIncomingCall}.
- *
- * @param call The call used for the incoming call.
- * @param errorCallback The callback to invoke upon failure.
- */
- void createIncomingCall(final Call call, final Runnable errorCallback) {
- Log.d(this, "createIncomingCall(%s) via %s.", call, getComponentName());
- BindCallback callback = new BindCallback() {
- @Override
- public void onSuccess() {
- if (isServiceValid("createIncomingCall")) {
- mPendingIncomingCalls.add(call);
- String callId = mCallIdMapper.getCallId(call);
- logOutgoing("createIncomingCall %s", callId);
- ConnectionRequest request = new ConnectionRequest(
- call.getPhoneAccount(),
- callId,
- call.getHandle(),
- call.getHandlePresentation(),
- call.getExtras(),
- call.getVideoState());
- try {
- mServiceInterface.createIncomingCall(request);
- } catch (RemoteException e) {
- }
- }
- }
-
- @Override
- public void onFailure() {
- errorCallback.run();
- }
- };
-
- mBinder.bind(callback);
- }
-
/** @see ConnectionService#disconnect(String) */
void disconnect(Call call) {
if (isServiceValid("disconnect")) {
@@ -768,12 +690,9 @@
}
void removeCall(Call call) {
- mPendingIncomingCalls.remove(call);
-
- OutgoingCallResponse outgoingResultCallback =
- mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
- if (outgoingResultCallback != null) {
- outgoingResultCallback.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
+ CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
+ if (response != null) {
+ response.handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
}
mCallIdMapper.removeCall(call);
@@ -865,27 +784,14 @@
* Called when the associated connection service dies.
*/
private void handleConnectionServiceDeath() {
- if (!mPendingOutgoingCalls.isEmpty()) {
- for (OutgoingCallResponse callback : mPendingOutgoingCalls.values()) {
- callback.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
- }
- mPendingOutgoingCalls.clear();
- }
-
- if (!mPendingIncomingCalls.isEmpty()) {
- // Iterate through a copy because the code inside the loop will modify the original
- // list.
- for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
- Preconditions.checkState(call.isIncoming());
- mIncomingCallsManager.handleFailedIncomingCall(call);
- }
-
- if (!mPendingIncomingCalls.isEmpty()) {
- Log.wtf(this, "Pending calls did not get cleared.");
- mPendingIncomingCalls.clear();
+ if (!mPendingResponses.isEmpty()) {
+ CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
+ new CreateConnectionResponse[mPendingResponses.values().size()]);
+ mPendingResponses.clear();
+ for (int i = 0; i < responses.length; i++) {
+ responses[i].handleCreateConnectionFailed(DisconnectCause.ERROR_UNSPECIFIED, null);
}
}
-
mCallIdMapper.clear();
}
@@ -900,15 +806,16 @@
private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
final List<IBinder> connectionServices = new ArrayList<>();
final List<ComponentName> components = new ArrayList<>();
+ final List<ConnectionServiceWrapper> servciesAttempted = new ArrayList<>();
final Collection<ConnectionServiceWrapper> services =
mConnectionServiceRepository.lookupServices();
+ Log.v(this, "queryRemoteConnectionServices, services: " + services.size());
+
for (ConnectionServiceWrapper cs : services) {
if (cs != this) {
final ConnectionServiceWrapper currentConnectionService = cs;
cs.mBinder.bind(new BindCallback() {
- private int mRemainingResponses = services.size() - 1;
-
@Override
public void onSuccess() {
Log.d(this, "Adding ***** %s", currentConnectionService.getComponentName());
@@ -920,13 +827,12 @@
@Override
public void onFailure() {
- // add null so that we always add up to totalExpected even if
- // some of the connection services fail to bind.
maybeComplete();
}
private void maybeComplete() {
- if (--mRemainingResponses == 0) {
+ servciesAttempted.add(currentConnectionService);
+ if (servciesAttempted.size() == services.size() - 1) {
try {
callback.onResult(components, connectionServices);
} catch (RemoteException ignored) {
diff --git a/src/com/android/telecomm/CreateConnectionProcessor.java b/src/com/android/telecomm/CreateConnectionProcessor.java
new file mode 100644
index 0000000..631d6fe
--- /dev/null
+++ b/src/com/android/telecomm/CreateConnectionProcessor.java
@@ -0,0 +1,173 @@
+/*
+ * 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.content.ComponentName;
+import android.net.Uri;
+import android.telephony.DisconnectCause;
+import android.telephony.PhoneNumberUtils;
+import android.telecomm.ConnectionRequest;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class creates connections to place new outgoing calls to attached to an existing incoming
+ * call. In either case, this class cycles through a set of connection services until:
+ * - a connection service returns a newly created connection in which case the call is displayed
+ * to the user
+ * - a connection service cancels the process, in which case the call is aborted
+ */
+final class CreateConnectionProcessor {
+ private final Call mCall;
+ private final ConnectionServiceRepository mRepository;
+ private List<ComponentName> mServiceComponentNames;
+ private Iterator<ComponentName> mServiceComponentNameIterator;
+ private CreateConnectionResponse mResponse;
+ private int mLastErrorCode = DisconnectCause.ERROR_UNSPECIFIED;
+ private String mLastErrorMsg;
+
+ CreateConnectionProcessor(
+ Call call, ConnectionServiceRepository repository, CreateConnectionResponse response) {
+ mCall = call;
+ mRepository = repository;
+ mResponse = response;
+ }
+
+ void process() {
+ Log.v(this, "process");
+
+ mServiceComponentNames = new ArrayList<>();
+
+ // TODO(sail): Remove once there's a way to pick the service.
+ ArrayList<ComponentName> priorityComponents = new ArrayList<>();
+ priorityComponents.add(new ComponentName("com.android.phone",
+ "com.android.services.telephony.sip.SipConnectionService"));
+ priorityComponents.add(new ComponentName("com.google.android.talk",
+ "com.google.android.apps.babel.telephony.TeleConnectionService"));
+ priorityComponents.add(new ComponentName("com.android.telecomm.tests",
+ "com.android.telecomm.testapps.TestConnectionService"));
+
+ for (ConnectionServiceWrapper service : mRepository.lookupServices()) {
+ ComponentName serviceName = service.getComponentName();
+ if (priorityComponents.contains(serviceName)) {
+ Log.i(this, "Moving connection service %s to top of list", serviceName);
+ mServiceComponentNames .add(0, serviceName);
+ } else {
+ mServiceComponentNames.add(serviceName);
+ }
+ }
+
+ adjustComponentNamesForEmergency();
+ mServiceComponentNameIterator = mServiceComponentNames.iterator();
+ attemptNextConnectionService();
+ }
+
+ void abort() {
+ Log.v(this, "abort");
+
+ // Clear the response first to prevent attemptNextConnectionService from attempting any
+ // more services.
+ CreateConnectionResponse response = mResponse;
+ mResponse = null;
+
+ ConnectionServiceWrapper service = mCall.getConnectionService();
+ if (service != null) {
+ service.abort(mCall);
+ mCall.clearConnectionService();
+ }
+ if (response != null) {
+ response.handleCreateConnectionCancelled();
+ }
+ }
+
+ private void attemptNextConnectionService() {
+ Log.v(this, "attemptNextConnectionService");
+
+ if (mResponse != null && mServiceComponentNameIterator.hasNext()) {
+ ComponentName component = mServiceComponentNameIterator.next();
+ ConnectionServiceWrapper service = mRepository.getService(component);
+ if (service == null) {
+ attemptNextConnectionService();
+ } else {
+ mCall.setConnectionService(service);
+ Log.i(this, "Attempting to call from %s", service.getComponentName());
+ service.createConnection(mCall, new Response(service));
+ }
+ } else {
+ Log.v(this, "attemptNextConnectionService, no more services, failing");
+ if (mResponse != null) {
+ mResponse.handleCreateConnectionFailed(mLastErrorCode, mLastErrorMsg);
+ mResponse = null;
+ mCall.clearConnectionService();
+ }
+ }
+ }
+
+ // If we are possibly attempting to call a local emergency number, ensure that the
+ // plain PSTN connection service, if it exists, is attempted first.
+ private void adjustComponentNamesForEmergency() {
+ if (shouldProcessAsEmergency(mCall.getHandle())) {
+ for (int i = 0; i < mServiceComponentNames.size(); i++) {
+ if (TelephonyUtil.isPstnComponentName(mServiceComponentNames.get(i))) {
+ mServiceComponentNames.add(0, mServiceComponentNames.remove(i));
+ return;
+ }
+ }
+ }
+ }
+
+ private boolean shouldProcessAsEmergency(Uri handle) {
+ return handle != null && PhoneNumberUtils.isPotentialLocalEmergencyNumber(
+ TelecommApp.getInstance(), handle.getSchemeSpecificPart());
+ }
+
+ private class Response implements CreateConnectionResponse {
+ private final ConnectionServiceWrapper mService;
+
+ Response(ConnectionServiceWrapper service) {
+ mService = service;
+ }
+
+ @Override
+ public void handleCreateConnectionSuccessful(ConnectionRequest request) {
+ if (mResponse == null) {
+ mService.abort(mCall);
+ } else {
+ mResponse.handleCreateConnectionSuccessful(request);
+ mResponse= null;
+ }
+ }
+
+ @Override
+ public void handleCreateConnectionFailed(int code, String msg) {
+ mLastErrorCode = code;
+ mLastErrorMsg = msg;
+ attemptNextConnectionService();
+ }
+
+ @Override
+ public void handleCreateConnectionCancelled() {
+ if (mResponse != null) {
+ mResponse.handleCreateConnectionCancelled();
+ mResponse = null;
+ }
+ }
+ }
+}
diff --git a/src/com/android/telecomm/OutgoingCallResponse.java b/src/com/android/telecomm/CreateConnectionResponse.java
similarity index 67%
rename from src/com/android/telecomm/OutgoingCallResponse.java
rename to src/com/android/telecomm/CreateConnectionResponse.java
index 3d5c393..caf2c1a 100644
--- a/src/com/android/telecomm/OutgoingCallResponse.java
+++ b/src/com/android/telecomm/CreateConnectionResponse.java
@@ -16,11 +16,13 @@
package com.android.telecomm;
+import android.telecomm.ConnectionRequest;
+
/**
- * A callback for providing the result of placing an outgoing call.
+ * A callback for providing the result of creating a connection.
*/
-interface OutgoingCallResponse {
- void onOutgoingCallSuccess();
- void onOutgoingCallFailure(int code, String msg);
- void onOutgoingCallCancel();
+interface CreateConnectionResponse {
+ void handleCreateConnectionSuccessful(ConnectionRequest request);
+ void handleCreateConnectionFailed(int code, String msg);
+ void handleCreateConnectionCancelled();
}
diff --git a/src/com/android/telecomm/IncomingCallsManager.java b/src/com/android/telecomm/IncomingCallsManager.java
deleted file mode 100644
index 5fa80f5..0000000
--- a/src/com/android/telecomm/IncomingCallsManager.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.Bundle;
-import android.telecomm.ConnectionService;
-import android.telecomm.ConnectionRequest;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import java.util.Set;
-
-/**
- * Used to retrieve details about an incoming call. This is invoked after an incoming call intent.
- */
-final class IncomingCallsManager {
-
- private final Set<Call> mPendingIncomingCalls = Sets.newLinkedHashSet();
-
- /**
- * Retrieves details of an incoming call through its associated connection service.
- *
- * @param call The call object.
- */
- void retrieveIncomingCall(final Call call) {
- ThreadUtil.checkOnMainThread();
- Log.d(this, "retrieveIncomingCall");
-
- // Just to be safe, lets make sure we're not already processing this call.
- Preconditions.checkState(!mPendingIncomingCalls.contains(call));
-
- mPendingIncomingCalls.add(call);
-
- Runnable errorCallback = new Runnable() {
- @Override public void run() {
- handleFailedIncomingCall(call);
- }
- };
-
- call.getConnectionService().createIncomingCall(call, errorCallback);
- }
-
- /**
- * Notifies the incoming call of success after removing it from the pending
- * list.
- *
- * @param request The details of the call.
- */
- void handleSuccessfulIncomingCall(Call call, ConnectionRequest request) {
- ThreadUtil.checkOnMainThread();
-
- if (mPendingIncomingCalls.contains(call)) {
- Log.d(this, "Incoming call %s found.", call);
- mPendingIncomingCalls.remove(call);
- call.handleVerifiedIncoming(request);
- }
- }
-
- /**
- * Notifies incoming call of failure after removing it from the pending list.
- */
- void handleFailedIncomingCall(Call call) {
- ThreadUtil.checkOnMainThread();
-
- if (mPendingIncomingCalls.contains(call)) {
- Log.i(this, "Failed to get details for incoming call %s", call);
- mPendingIncomingCalls.remove(call);
- // The call was found still waiting for details. Consider it failed.
- call.handleFailedIncoming();
- }
- }
-}
diff --git a/src/com/android/telecomm/MissedCallNotifier.java b/src/com/android/telecomm/MissedCallNotifier.java
index bb2f092..ed79f5a 100644
--- a/src/com/android/telecomm/MissedCallNotifier.java
+++ b/src/com/android/telecomm/MissedCallNotifier.java
@@ -279,7 +279,7 @@
}
// Convert the data to a call object
- Call call = new Call(null, null, null, true, false);
+ Call call = new Call(null, null, null, null, true, false);
call.setDisconnectCause(DisconnectCause.INCOMING_MISSED, "");
call.setState(CallState.DISCONNECTED);
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
deleted file mode 100644
index c24a83b..0000000
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * 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.content.ComponentName;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.DisconnectCause;
-import android.telephony.PhoneNumberUtils;
-
-import com.google.android.collect.Sets;
-import com.google.common.collect.Maps;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Utility class to place a call using the specified set of connection services. Each of the
- * connection services is then attempted until either the outgoing call is placed, the attempted
- * call is aborted, or the list is exhausted -- whichever occurs first.
- *
- * Except for the abort case, all other scenarios should terminate with the call notified
- * of the result.
- */
-final class OutgoingCallProcessor {
-
- /**
- * The outgoing call this processor is tasked with placing.
- */
- private final Call mCall;
-
- /**
- * The set of attempted connection services, used to ensure services are attempted at most once
- * per outgoing-call attempt.
- */
- private final Set<ConnectionServiceWrapper> mAttemptedServices = Sets.newHashSet();
-
- private final ConnectionServiceRepository mConnectionServiceRepository;
-
- /**
- * The duplicate-free list of currently-available connection service component names.
- */
- private List<ComponentName> mServiceComponentNames;
-
- /**
- * The iterator over the currently-selected ordered list of connection service component names.
- */
- private Iterator<ComponentName> mServiceComponentNameIterator;
-
- private OutgoingCallResponse mResultCallback;
-
- private boolean mIsAborted = false;
-
- private int mLastErrorCode = 0;
-
- private String mLastErrorMsg = null;
-
- /**
- * Persists the specified parameters and iterates through the prioritized list of call
- * services. Stops once a matching connection service is found. Calls with no matching
- * connection service will eventually be killed by the cleanup/monitor switchboard handler.
- *
- * @param call The call to place.
- * @param resultCallback The callback on which to return the result.
- */
- OutgoingCallProcessor(
- Call call,
- ConnectionServiceRepository connectionServiceRepository,
- OutgoingCallResponse resultCallback) {
-
- ThreadUtil.checkOnMainThread();
-
- mCall = call;
- mResultCallback = resultCallback;
- mConnectionServiceRepository = connectionServiceRepository;
- }
-
- /**
- * Initiates the attempt to place the call. No-op beyond the first invocation.
- */
- void process() {
- Log.v(this, "process, mIsAborted: %b", mIsAborted);
- if (!mIsAborted) {
- setConnectionServices(mConnectionServiceRepository.lookupServices());
- }
- }
-
- /**
- * Aborts the attempt to place the relevant call. Intended to be invoked by
- * switchboard through the outgoing-calls manager.
- */
- void abort() {
- Log.v(this, "abort");
- ThreadUtil.checkOnMainThread();
- if (!mIsAborted && mResultCallback != null) {
- mIsAborted = true;
-
- // On an abort, we need to check if we already told the connection service to place the
- // call. If so, we need to tell it to abort.
- // TODO(santoscordon): The conneciton service is saved with the call and so we have to
- // query the call to get it, which is a bit backwards. Ideally, the connection service
- // would be saved inside this class until the whole thing is complete and then set on
- // the call.
- ConnectionServiceWrapper service = mCall.getConnectionService();
- if (service != null) {
- service.abort(mCall);
- }
-
- // We consider a deliberate abort to be a "normal" disconnect, not
- // requiring special reporting.
- sendResult(false, DisconnectCause.LOCAL, null);
- }
- }
-
- boolean isAborted() {
- return mIsAborted;
- }
-
- /**
- * Completes the outgoing call sequence by setting the connection service on the call object.
- * This is invoked when the connection service adapter receives positive confirmation that the
- * connection service placed the call.
- */
- void handleSuccessfulCallAttempt(ConnectionServiceWrapper service) {
- Log.v(this, "handleSuccessfulCallAttempt");
- ThreadUtil.checkOnMainThread();
-
- if (mIsAborted) {
- service.abort(mCall);
- return;
- }
-
- sendResult(true, DisconnectCause.NOT_DISCONNECTED, null);
- }
-
- /**
- * Attempts the next connection service if the specified connection service is the one currently
- * being attempted.
- *
- * @param errorCode The reason for the failure, one of {@link DisconnectCause}.
- * @param errorMsg Optional text reason for the failure.
- */
- void handleFailedCallAttempt(int errorCode, String errorMsg) {
- Log.v(this, "handleFailedCallAttempt %s %s", DisconnectCause.toString(errorCode), errorMsg);
- // Store latest error code and message. If this is our last available attempt at placing
- // a call, these error details will be considered "the" cause of the failure.
- mLastErrorCode = errorCode;
- mLastErrorMsg = errorMsg;
- if (!mIsAborted) {
- ThreadUtil.checkOnMainThread();
- attemptNextConnectionService();
- }
- }
-
- /**
- * Sets the connection services to attempt for this outgoing call.
- *
- * @param services The connection services.
- */
- private void setConnectionServices(Collection<ConnectionServiceWrapper> services) {
- mServiceComponentNames = new ArrayList<>();
-
- // TODO(sail): Remove once there's a way to pick the service.
- ArrayList<ComponentName> priorityComponents = new ArrayList<>();
- priorityComponents.add(new ComponentName("com.android.phone",
- "com.android.services.telephony.sip.SipConnectionService"));
- priorityComponents.add(new ComponentName("com.google.android.talk",
- "com.google.android.apps.babel.telephony.TeleConnectionService"));
- priorityComponents.add(new ComponentName("com.android.telecomm.tests",
- "com.android.telecomm.testapps.TestConnectionService"));
-
- for (ConnectionServiceWrapper service : services) {
- ComponentName serviceName = service.getComponentName();
- if (priorityComponents.contains(serviceName)) {
- Log.i(this, "Moving connection service %s to top of list", serviceName);
- mServiceComponentNames .add(0, serviceName);
- } else {
- mServiceComponentNames.add(serviceName);
- }
- }
-
- adjustComponentNamesForEmergency();
-
- mServiceComponentNameIterator = mServiceComponentNames.iterator();
- attemptNextConnectionService();
- }
-
- /**
- * Attempts to place the call using the connection service specified by the next connection
- * service component name of mServiceComponentNameIterator.
- */
- private void attemptNextConnectionService() {
- Log.v(this, "attemptNextConnectionService, mIsAborted: %b", mIsAborted);
- if (mIsAborted) {
- return;
- }
-
- if (mServiceComponentNameIterator != null && mServiceComponentNameIterator.hasNext()) {
- ComponentName component = mServiceComponentNameIterator.next();
- final ConnectionServiceWrapper service =
- mConnectionServiceRepository.getService(component);
-
- if (service == null || mAttemptedServices.contains(service)) {
- // The next connection service is either null or has already been attempted, fast
- // forward to the next.
- attemptNextConnectionService();
- } else {
- mAttemptedServices.add(service);
- mCall.setConnectionService(service);
-
- // Increment the associated call count until we get a result. This prevents the call
- // service from unbinding while we are using it.
- service.incrementAssociatedCallCount();
-
- Log.i(this, "Attempting to call from %s", service.getComponentName());
- service.call(mCall, new OutgoingCallResponse() {
- @Override
- public void onOutgoingCallSuccess() {
- handleSuccessfulCallAttempt(service);
- service.decrementAssociatedCallCount();
- }
-
- @Override
- public void onOutgoingCallFailure(int code, String msg) {
- handleFailedCallAttempt(code, msg);
- service.decrementAssociatedCallCount();
- }
-
- @Override
- public void onOutgoingCallCancel() {
- abort();
- service.decrementAssociatedCallCount();
- }
- });
- }
- } else {
- Log.v(this, "attemptNextConnectionService, no more services, failing");
- mServiceComponentNameIterator = null;
- mCall.clearConnectionService();
- sendResult(false, mLastErrorCode, mLastErrorMsg);
- }
- }
-
- private void sendResult(boolean wasCallPlaced, int errorCode, String errorMsg) {
- if (mResultCallback != null) {
- if (mIsAborted) {
- mResultCallback.onOutgoingCallCancel();
- } else if (wasCallPlaced) {
- mResultCallback.onOutgoingCallSuccess();
- } else {
- mResultCallback.onOutgoingCallFailure(errorCode, errorMsg);
- }
- mResultCallback = null;
- } else {
- Log.wtf(this, "Attempting to return outgoing result twice for call %s", mCall);
- }
- }
-
- // If we are possibly attempting to call a local emergency number, ensure that the
- // plain PSTN connection service, if it exists, is attempted first.
- private void adjustComponentNamesForEmergency() {
- for (int i = 0; i < mServiceComponentNames.size(); i++) {
- if (shouldProcessAsEmergency(mCall.getHandle())) {
- if (TelephonyUtil.isPstnComponentName(mServiceComponentNames.get(i))) {
- mServiceComponentNames.add(0, mServiceComponentNames.remove(i));
- return;
- }
- }
- }
- }
-
- private boolean shouldProcessAsEmergency(Uri handle) {
- return PhoneNumberUtils.isPotentialLocalEmergencyNumber(
- TelecommApp.getInstance(), handle.getSchemeSpecificPart());
- }
-}
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
deleted file mode 100644
index 2dfcfea..0000000
--- a/src/com/android/telecomm/Switchboard.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2013, 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.content.ComponentName;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.telecomm.TelecommConstants;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Switchboard is responsible for gathering the {@link ConnectionServiceWrapper}s through
- * which to place outgoing calls
- */
-final class Switchboard {
- private final static Switchboard sInstance = new Switchboard();
-
- /** Used to retrieve incoming call details. */
- private final IncomingCallsManager mIncomingCallsManager;
-
- private final ConnectionServiceRepository mConnectionServiceRepository;
-
- /** Singleton accessor. */
- static Switchboard getInstance() {
- return sInstance;
- }
-
- /**
- * Persists the specified parameters and initializes Switchboard.
- */
- private Switchboard() {
- ThreadUtil.checkOnMainThread();
-
- mIncomingCallsManager = new IncomingCallsManager();
- mConnectionServiceRepository =
- new ConnectionServiceRepository(mIncomingCallsManager);
- }
-
- ConnectionServiceRepository getConnectionServiceRepository() {
- return mConnectionServiceRepository;
- }
-
- /**
- * Retrieves details about the incoming call through the incoming call manager.
- *
- * @param call The call object.
- */
- void retrieveIncomingCall(Call call) {
- Log.d(this, "retrieveIncomingCall");
- ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
- call.getPhoneAccount().getComponentName());
- call.setConnectionService(service);
- mIncomingCallsManager.retrieveIncomingCall(call);
- }
-}