OutgoingCalls(1/6) - Filling in lifecycle of outgoing call.
This CL adds the following: 1. CallServiceAdapter implementation 2. Setting call-service adapters when the finder receives call services. 3. CallServiceAdapter notifies OutgoingCallManager/OCP about a successful outgoing call 4. Switchboard notifies CallsManager of the successful outgoing call 5. CallsManager adds the call to it's call repository. What remains after this CL: CallsManager sending the call to the InCall app and listening to commands from in-call. depends on: I086443e3f17ae0233b7b0fd5629119e989d06a40 Change-Id: I86f3e7ab02a47485eb2a5fb3557819418f3c4adf
This commit is contained in:
parent
2313e62356
commit
681663d17b
|
@ -18,6 +18,7 @@ package com.android.telecomm;
|
|||
|
||||
import android.telecomm.CallInfo;
|
||||
import android.telecomm.CallState;
|
||||
import android.telecomm.ICallService;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
@ -52,6 +53,12 @@ final class Call {
|
|||
*/
|
||||
private final Date mCreationTime;
|
||||
|
||||
/**
|
||||
* The call service which is currently connecting this call, null as long as the call is not
|
||||
* connected.
|
||||
*/
|
||||
private ICallService mCallService;
|
||||
|
||||
/**
|
||||
* Read-only and parcelable version of this call.
|
||||
*/
|
||||
|
@ -107,6 +114,21 @@ final class Call {
|
|||
return new Date().getTime() - mCreationTime.getTime();
|
||||
}
|
||||
|
||||
ICallService getCallService() {
|
||||
return mCallService;
|
||||
}
|
||||
|
||||
void setCallService(ICallService callService) {
|
||||
mCallService = callService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the associated call service.
|
||||
*/
|
||||
void clearCallService() {
|
||||
setCallService(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An object containing read-only information about this call.
|
||||
*/
|
||||
|
|
|
@ -1,16 +1,140 @@
|
|||
/*
|
||||
* Copyright (C) 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.Handler;
|
||||
import android.os.Looper;
|
||||
import android.telecomm.CallInfo;
|
||||
import android.telecomm.ICallServiceAdapter;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
* Only exposes the CallsManager APIs that CallService implementations should
|
||||
* have access to.
|
||||
* Used by call services in order to update state and control calls while the call service is bound
|
||||
* to Telecomm. Each call service is given its own instance for the lifetime of the binding between
|
||||
* Telecomm and the call service.
|
||||
* TODO(santoscordon): Whenever we get any method invocations from the call service, we need to
|
||||
* check that the invocation is expected from that call service.
|
||||
* TODO(santoscordon): Move away from Runnable objects and into messages so that we create fewer
|
||||
* objects per IPC method call.
|
||||
*/
|
||||
public class CallServiceAdapter {
|
||||
private CallsManager callsManager;
|
||||
public final class CallServiceAdapter extends ICallServiceAdapter.Stub {
|
||||
|
||||
private CallService callService;
|
||||
private final CallsManager mCallsManager;
|
||||
|
||||
/** Package private */
|
||||
CallServiceAdapter(CallsManager callsManager) {
|
||||
this.callsManager = callsManager;
|
||||
}
|
||||
private final OutgoingCallsManager mOutgoingCallsManager;
|
||||
|
||||
/** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* Persists the specified parameters.
|
||||
*/
|
||||
CallServiceAdapter(OutgoingCallsManager outgoingCallsManager) {
|
||||
mCallsManager = CallsManager.getInstance();
|
||||
mOutgoingCallsManager = outgoingCallsManager;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void getNextCallId() {
|
||||
// TODO(santoscordon): needs response object.
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void setCompatibleWith(String callId, boolean isCompatible) {
|
||||
// TODO(santoscordon): fill in.
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(santoscordon): Rename to handleIncomingCall.
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override public void newIncomingCall(CallInfo callInfo) {
|
||||
// TODO(santoscordon): fill in.
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void handleSuccessfulOutgoingCall(final String callId) {
|
||||
checkValidCallId(callId);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
mOutgoingCallsManager.handleSuccessfulCallAttempt(callId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void handleFailedOutgoingCall(final String callId, final String reason) {
|
||||
checkValidCallId(callId);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
mOutgoingCallsManager.handleFailedCallAttempt(callId, reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void setActive(final String callId) {
|
||||
checkValidCallId(callId);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
mCallsManager.markCallAsActive(callId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void setRinging(final String callId) {
|
||||
checkValidCallId(callId);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
mCallsManager.markCallAsRinging(callId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void setDialing(final String callId) {
|
||||
checkValidCallId(callId);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
mCallsManager.markCallAsDialing(callId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override public void setDisconnected(final String callId) {
|
||||
checkValidCallId(callId);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
mCallsManager.markCallAsDisconnected(callId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an IllegalArgumentException if the specified call ID is invalid.
|
||||
*
|
||||
* @param callId The call ID to check.
|
||||
*/
|
||||
private void checkValidCallId(String callId) {
|
||||
if (Strings.isNullOrEmpty(callId)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ final class CallServiceFinder {
|
|||
|
||||
private final Switchboard mSwitchboard;
|
||||
|
||||
private final OutgoingCallsManager mOutgoingCallsManager;
|
||||
|
||||
/**
|
||||
* Determines whether or not a lookup cycle is already running.
|
||||
*/
|
||||
|
@ -118,10 +120,12 @@ final class CallServiceFinder {
|
|||
/**
|
||||
* Persists the specified parameters.
|
||||
*
|
||||
* @param switchboard The switchboard for this finer to work against.
|
||||
* @param switchboard The switchboard for this finder to work against.
|
||||
* @param outgoingCallsManager Manager in charge of placing outgoing calls.
|
||||
*/
|
||||
CallServiceFinder(Switchboard switchboard) {
|
||||
CallServiceFinder(Switchboard switchboard, OutgoingCallsManager outgoingCallsManager) {
|
||||
mSwitchboard = switchboard;
|
||||
mOutgoingCallsManager = outgoingCallsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,11 +292,20 @@ final class CallServiceFinder {
|
|||
ThreadUtil.checkOnMainThread();
|
||||
|
||||
// TODO(santoscordon): When saving the call services into this class, also add code to
|
||||
// unregister (remove) the call services upon disconnect. Potentially use RemoteCallbackList.
|
||||
// unregister (remove) the call services upon disconnect. Potentially use
|
||||
// RemoteCallbackList.
|
||||
|
||||
if (mUnregisteredProviders.remove(providerName)) {
|
||||
mProviderRegistry.add(provider);
|
||||
mCallServiceRegistry.addAll(callServices);
|
||||
for (ICallService callService : callServices) {
|
||||
try {
|
||||
CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
|
||||
callService.setCallServiceAdapter(adapter);
|
||||
mCallServiceRegistry.add(callService);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to set call-service adapter.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gilad): Introduce a map to retain the association between call services
|
||||
// and the corresponding provider such that mCallServiceRegistry can be updated
|
||||
|
|
|
@ -17,11 +17,18 @@
|
|||
package com.android.telecomm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telecomm.CallState;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.telecomm.exceptions.RestrictedCallException;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Singleton.
|
||||
|
@ -31,6 +38,7 @@ import java.util.List;
|
|||
* beyond the com.android.telecomm package boundary.
|
||||
*/
|
||||
public final class CallsManager {
|
||||
private static final String TAG = CallsManager.class.getSimpleName();
|
||||
|
||||
private static final CallsManager INSTANCE = new CallsManager();
|
||||
|
||||
|
@ -39,6 +47,14 @@ public final class CallsManager {
|
|||
/** Used to control the in-call app. */
|
||||
private final InCallController mInCallController;
|
||||
|
||||
/**
|
||||
* The main call repository. Keeps an instance of all live calls keyed by call ID. New incoming
|
||||
* and outgoing calls are added to the map and removed when the calls move to the disconnected
|
||||
* state.
|
||||
* TODO(santoscordon): Add new CallId class and use it in place of String.
|
||||
*/
|
||||
private final Map<String, Call> mCalls = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* May be unnecessary per off-line discussions (between santoscordon and gilad) since the set
|
||||
* of CallsManager APIs that need to be exposed to the dialer (or any application firing call
|
||||
|
@ -65,7 +81,7 @@ public final class CallsManager {
|
|||
*/
|
||||
private CallsManager() {
|
||||
mSwitchboard = new Switchboard();
|
||||
mInCallController = new InCallController(this);
|
||||
mInCallController = new InCallController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,6 +105,21 @@ public final class CallsManager {
|
|||
mSwitchboard.placeOutgoingCall(handle, contactInfo, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new outgoing call to the list of live calls and notifies the in-call app.
|
||||
*
|
||||
* @param call The new outgoing call.
|
||||
*/
|
||||
void handleSuccessfulOutgoingCall(Call call) {
|
||||
// OutgoingCallProcessor sets the call state to DIALING when it receives confirmation of the
|
||||
// placed call from the call service so there is no need to set it here. Instead, check that
|
||||
// the state is appropriate.
|
||||
Preconditions.checkState(call.getState() == CallState.DIALING);
|
||||
|
||||
addCall(call);
|
||||
// TODO(santoscordon): Notify in-call UI.
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs Telecomm to answer the specified call. Intended to be invoked by the in-call
|
||||
* app through {@link InCallAdapter} after Telecomm notifies it of an incoming call followed by
|
||||
|
@ -121,4 +152,57 @@ public final class CallsManager {
|
|||
void disconnectCall(String callId) {
|
||||
// TODO(santoscordon): fill in and check that the call is in the active state.
|
||||
}
|
||||
|
||||
void markCallAsRinging(String callId) {
|
||||
setCallState(callId, CallState.RINGING);
|
||||
}
|
||||
|
||||
void markCallAsDialing(String callId) {
|
||||
setCallState(callId, CallState.DIALING);
|
||||
}
|
||||
|
||||
void markCallAsActive(String callId) {
|
||||
setCallState(callId, CallState.ACTIVE);
|
||||
}
|
||||
|
||||
void markCallAsDisconnected(String callId) {
|
||||
setCallState(callId, CallState.DISCONNECTED);
|
||||
mCalls.remove(callId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified state on the specified call.
|
||||
*
|
||||
* @param callId The ID of the call to update.
|
||||
* @param state The new state of the call.
|
||||
*/
|
||||
private void setCallState(String callId, CallState state) {
|
||||
Preconditions.checkState(!Strings.isNullOrEmpty(callId));
|
||||
Preconditions.checkNotNull(state);
|
||||
|
||||
Call call = mCalls.get(callId);
|
||||
if (call == null) {
|
||||
Log.e(TAG, "Call " + callId + " was not found while attempting to upda the state to " +
|
||||
state + ".");
|
||||
} else {
|
||||
// Unfortunately, in the telephony world, the radio is king. So if the call notifies us
|
||||
// that the call is in a particular state, we allow it even if it doesn't make sense
|
||||
// (e.g., ACTIVE -> RINGING).
|
||||
// TODO(santoscordon): Consider putting a stop to the above and turning CallState into
|
||||
// a well-defined state machine.
|
||||
// TODO(santoscordon): Define expected state transitions here, and log when an
|
||||
// unexpected transition occurs.
|
||||
call.setState(state);
|
||||
// TODO(santoscordon): Notify the in-call app whenever a call changes state.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified call to the main list of live calls.
|
||||
*
|
||||
* @param call The call to add.
|
||||
*/
|
||||
private void addCall(Call call) {
|
||||
mCalls.put(call.getId(), call);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,11 +76,9 @@ public final class InCallController {
|
|||
|
||||
/**
|
||||
* Persists the specified parameters.
|
||||
*
|
||||
* @param callsManager The singleton calls manager instance.
|
||||
*/
|
||||
InCallController(CallsManager callsManager) {
|
||||
mCallsManager = callsManager;
|
||||
InCallController() {
|
||||
mCallsManager = CallsManager.getInstance();
|
||||
}
|
||||
|
||||
// TODO(santoscordon): May be better to expose the IInCallService methods directly from this
|
||||
|
|
|
@ -24,9 +24,11 @@ import android.os.Handler;
|
|||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.telecomm.CallInfo;
|
||||
import android.telecomm.CallState;
|
||||
import android.telecomm.ICallService;
|
||||
import android.telecomm.ICallServiceSelectionResponse;
|
||||
import android.telecomm.ICallServiceSelector;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -40,7 +42,8 @@ import java.util.Set;
|
|||
* call services is then attempted until either the outgoing call is placed, the attempted call
|
||||
* is aborted (by the switchboard), or the list is exhausted -- whichever occurs first.
|
||||
*
|
||||
* Except for the abort case, all other scenarios should terminate with the switchboard notified.
|
||||
* Except for the abort case, all other scenarios should terminate with the switchboard notified
|
||||
* of the result.
|
||||
*
|
||||
* NOTE(gilad): Currently operating under the assumption that we'll have one timeout per (outgoing)
|
||||
* call attempt. If we (also) like to timeout individual selectors and/or call services, the code
|
||||
|
@ -48,11 +51,6 @@ import java.util.Set;
|
|||
*/
|
||||
final class OutgoingCallProcessor {
|
||||
|
||||
/**
|
||||
* The (singleton) Telecomm switchboard instance.
|
||||
*/
|
||||
private final Switchboard mSwitchboard;
|
||||
|
||||
/**
|
||||
* The outgoing call this processor is tasked with placing.
|
||||
*/
|
||||
|
@ -76,6 +74,11 @@ final class OutgoingCallProcessor {
|
|||
/** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/** Manages all outgoing call processors. */
|
||||
private final OutgoingCallsManager mOutgoingCallsManager;
|
||||
|
||||
private final Switchboard mSwitchboard;
|
||||
|
||||
/**
|
||||
* The iterator over the currently-selected ordered list of call-service IDs.
|
||||
*/
|
||||
|
@ -83,6 +86,12 @@ final class OutgoingCallProcessor {
|
|||
|
||||
private Iterator<ICallServiceSelector> mSelectorIterator;
|
||||
|
||||
/**
|
||||
* The last call service which we asked to place the call. If null, it indicates that there
|
||||
* exists no call service that we expect to place this call.
|
||||
*/
|
||||
private ICallService mCallService;
|
||||
|
||||
private boolean mIsAborted = false;
|
||||
|
||||
/**
|
||||
|
@ -90,17 +99,19 @@ final class OutgoingCallProcessor {
|
|||
* passing to each selector (read-only versions of) the call object and all available call-
|
||||
* service IDs. Stops once a matching selector is found. Calls with no matching selectors
|
||||
* will eventually be killed by the cleanup/monitor switchboard handler, which will in turn
|
||||
* call the abort method of this processor.
|
||||
* call the abort method of this processor via {@link OutgoingCallsManager}.
|
||||
*
|
||||
* @param call The call to place.
|
||||
* @param callServices The available call-service implementations.
|
||||
* @param selectors The available call-service selector implementations.
|
||||
* @param switchboard The switchboard for this processor to work against.
|
||||
* @param outgoingCallsManager Manager of all outgoing call processors.
|
||||
* @param switchboard The switchboard.
|
||||
*/
|
||||
OutgoingCallProcessor(
|
||||
Call call,
|
||||
Set<ICallService> callServices,
|
||||
List<ICallServiceSelector> selectors,
|
||||
OutgoingCallsManager outgoingCallsManager,
|
||||
Switchboard switchboard) {
|
||||
|
||||
ThreadUtil.checkOnMainThread();
|
||||
|
@ -110,6 +121,7 @@ final class OutgoingCallProcessor {
|
|||
|
||||
mCall = call;
|
||||
mSelectors = selectors;
|
||||
mOutgoingCallsManager = outgoingCallsManager;
|
||||
mSwitchboard = switchboard;
|
||||
|
||||
// Populate the list and map of call-service IDs. The list is needed since
|
||||
|
@ -131,7 +143,7 @@ final class OutgoingCallProcessor {
|
|||
if (mSelectors.isEmpty() || mCallServiceIds.isEmpty()) {
|
||||
// TODO(gilad): Consider adding a failure message/type to differentiate the various
|
||||
// cases, or potentially throw an exception in this case.
|
||||
mSwitchboard.handleFailedOutgoingCall(mCall);
|
||||
mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
|
||||
} else if (mSelectorIterator == null) {
|
||||
mSelectorIterator = mSelectors.iterator();
|
||||
attemptNextSelector();
|
||||
|
@ -140,13 +152,38 @@ final class OutgoingCallProcessor {
|
|||
|
||||
/**
|
||||
* Aborts the attempt to place the relevant call. Intended to be invoked by
|
||||
* switchboard.
|
||||
* switchboard through the outgoing-calls manager.
|
||||
*/
|
||||
void abort() {
|
||||
ThreadUtil.checkOnMainThread();
|
||||
resetCallService();
|
||||
mIsAborted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the outgoing call sequence by setting the call service on the call object. This is
|
||||
* invoked when the call service adapter receives positive confirmation that the call service
|
||||
* placed the call.
|
||||
*/
|
||||
void handleSuccessfulCallAttempt() {
|
||||
abort(); // Technically not needed but playing it safe.
|
||||
mCall.setCallService(mCallService);
|
||||
mCall.setState(CallState.DIALING);
|
||||
resetCallService();
|
||||
|
||||
mSwitchboard.handleSuccessfulOutgoingCall(mCall);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts the next call service if the specified call service is the one currently being
|
||||
* attempted.
|
||||
*
|
||||
* @param reason The call-service supplied reason for the failed call attempt.
|
||||
*/
|
||||
void handleFailedCallAttempt(String reason) {
|
||||
attemptNextCallService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to place the call using the next selector, no-op if no other selectors
|
||||
* are available.
|
||||
|
@ -166,7 +203,7 @@ final class OutgoingCallProcessor {
|
|||
}
|
||||
|
||||
} else {
|
||||
mSwitchboard.handleFailedOutgoingCall(mCall);
|
||||
mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,7 +243,9 @@ final class OutgoingCallProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* TODO(gilad): Add comment.
|
||||
* Attempts to place the call using the call service specified by the next call-service ID of
|
||||
* mCallServiceIdIterator. If there are no more call services to attempt, the process continues
|
||||
* to the next call-service selector via {@link #attemptNextSelector}.
|
||||
*/
|
||||
private void attemptNextCallService() {
|
||||
if (mIsAborted) {
|
||||
|
@ -215,10 +254,12 @@ final class OutgoingCallProcessor {
|
|||
|
||||
if (mCallServiceIdIterator.hasNext()) {
|
||||
String id = mCallServiceIdIterator.next();
|
||||
ICallService callService = mCallServicesById.get(id);
|
||||
if (callService != null) {
|
||||
mCallService = mCallServicesById.get(id);
|
||||
if (mCallService == null) {
|
||||
attemptNextCallService();
|
||||
} else {
|
||||
try {
|
||||
callService.call(mCall.toCallInfo());
|
||||
mCallService.call(mCall.toCallInfo());
|
||||
} catch (RemoteException e) {
|
||||
// TODO(gilad): Log etc.
|
||||
attemptNextCallService();
|
||||
|
@ -226,25 +267,16 @@ final class OutgoingCallProcessor {
|
|||
}
|
||||
} else {
|
||||
mCallServiceIdIterator = null;
|
||||
resetCallService();
|
||||
attemptNextSelector();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the successful outgoing-call case.
|
||||
* Nulls out the reference to the current call service. Invoked when the call service is no longer
|
||||
* expected to place the outgoing call.
|
||||
*/
|
||||
private void handleSuccessfulOutgoingCall() {
|
||||
// TODO(gilad): More here?
|
||||
|
||||
abort(); // Shouldn't be necessary but better safe than sorry.
|
||||
mSwitchboard.handleSuccessfulOutgoingCall(mCall);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the failed outgoing-call case.
|
||||
*/
|
||||
private void handleFailedOutgoingCall() {
|
||||
// TODO(gilad): Implement.
|
||||
attemptNextCallService();
|
||||
private void resetCallService() {
|
||||
mCallService = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.RemoteException;
|
||||
import android.telecomm.ICallService;
|
||||
import android.telecomm.ICallServiceSelector;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for placing all outgoing calls. For each outgoing call, this class creates an
|
||||
* instance of {@link OutgoingCallProcessor} which handles the details of connecting to the
|
||||
* appropriate call service and placing the call. This class maintains a mapping from call ID
|
||||
* to {@link OutgoingCallProcessor} so that other classes (Switchboard, CallServiceAdapter, etc),
|
||||
* can simply call into this class instead of individual OutgoingCallProcessors.
|
||||
*/
|
||||
final class OutgoingCallsManager {
|
||||
private static final String TAG = OutgoingCallsManager.class.getSimpleName();
|
||||
|
||||
private final Switchboard mSwitchboard;
|
||||
|
||||
/**
|
||||
* Maps call IDs to {@link OutgoingCallProcessor}s.
|
||||
*/
|
||||
private Map<String, OutgoingCallProcessor> mOutgoingCallProcessors = Maps.newHashMap();
|
||||
|
||||
/** Persists specified parameters. */
|
||||
OutgoingCallsManager(Switchboard switchboard) {
|
||||
mSwitchboard = switchboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the process of placing a call by constructing an outgoing call processor and asking
|
||||
* it to place the call. Upon success, execution will continue (via {@link CallServiceAdapter})
|
||||
* to {@link #handleSuccessfulCall}. Upon failure, execution will return to
|
||||
* {@link #handleFailedCall}.
|
||||
*
|
||||
* @param call The call to place.
|
||||
* @param callServices The set of call services which can potentially place the call.
|
||||
* @param selectors The ordered list of selectors used in placing the call.
|
||||
*/
|
||||
void placeCall(
|
||||
Call call, Set<ICallService> callServices, List<ICallServiceSelector> selectors) {
|
||||
|
||||
Log.i(TAG, "Placing an outgoing call (" + call.getId() + ")");
|
||||
|
||||
// Create the processor for this (outgoing) call and store it in a map such that call
|
||||
// attempts can be aborted etc.
|
||||
// TODO(gilad): Consider passing mSelector as an immutable set.
|
||||
OutgoingCallProcessor processor =
|
||||
new OutgoingCallProcessor(call, callServices, selectors, this, mSwitchboard);
|
||||
|
||||
mOutgoingCallProcessors.put(call.getId(), processor);
|
||||
processor.process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the outgoing call processor mapping for the successful call and returns execution to
|
||||
* the switchboard. This method is invoked from {@link CallServiceAdapter} after a call service
|
||||
* has notified Telecomm that it successfully placed the call.
|
||||
*
|
||||
* @param callId The ID of the call.
|
||||
*/
|
||||
void handleSuccessfulCallAttempt(String callId) {
|
||||
OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(callId);
|
||||
|
||||
if (processor == null) {
|
||||
// Shouldn't happen, so log a wtf if it does.
|
||||
Log.wtf(TAG, "Received an unexpected placed-call notification.");
|
||||
} else {
|
||||
processor.handleSuccessfulCallAttempt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the appropriate outgoing call processor that a call attempt to place the call has
|
||||
* failed and the processor should continue attempting to place the call with the next call
|
||||
* service. This method is called from {@link CallServiceAdapter} after a call service has
|
||||
* notified Telecomm that it could not place the call.
|
||||
*
|
||||
* @param callId The ID of the failed outgoing call.
|
||||
* @param reason The call-service supplied reason for the failed call attempt.
|
||||
*/
|
||||
void handleFailedCallAttempt(String callId, String reason) {
|
||||
OutgoingCallProcessor processor = mOutgoingCallProcessors.get(callId);
|
||||
|
||||
// TODO(santoscordon): Consider combining the check here and in handleSuccessfulCallAttempt.
|
||||
if (processor == null) {
|
||||
// Shouldn't happen, so log a wtf if it does.
|
||||
Log.wtf(TAG, "Received an unexpected failed-call notification.");
|
||||
} else {
|
||||
processor.handleFailedCallAttempt(reason);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the outgoing call processor mapping for the failed call and returns execution to the
|
||||
* switchboard. In contrast to handleFailedCallAttempt which comes from the call-service and
|
||||
* goes to the outgoing-call processor indicating a single failed call attempt, this method is
|
||||
* invoked by the outgoing-call processor to indicate that the entire process has failed and we
|
||||
* should cleanup and notify Switchboard.
|
||||
*
|
||||
* @param call The failed outgoing call.
|
||||
*/
|
||||
void handleFailedOutgoingCall(Call call) {
|
||||
mOutgoingCallProcessors.remove(call.getId());
|
||||
mSwitchboard.handleFailedOutgoingCall(call);
|
||||
}
|
||||
}
|
|
@ -45,9 +45,14 @@ final class Switchboard {
|
|||
*/
|
||||
private final static int TICK_FREQUENCY = 250;
|
||||
|
||||
private final CallServiceFinder mCallServiceFinder = new CallServiceFinder(this);
|
||||
private final CallsManager mCallsManager;
|
||||
|
||||
private final CallServiceSelectorFinder mSelectorFinder = new CallServiceSelectorFinder(this);
|
||||
/** Used to place outgoing calls. */
|
||||
private final OutgoingCallsManager mOutgoingCallsManager;
|
||||
|
||||
private CallServiceFinder mCallServiceFinder;
|
||||
|
||||
private CallServiceSelectorFinder mSelectorFinder;
|
||||
|
||||
/** Used to schedule tasks on the main (UI) thread. */
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
@ -84,7 +89,15 @@ final class Switchboard {
|
|||
*/
|
||||
private Set<ICallServiceSelector> mSelectors;
|
||||
|
||||
private Map<Call, OutgoingCallProcessor> outgoingCallProcessors = Maps.newHashMap();
|
||||
/**
|
||||
* Persists the specified parameters and initializes Switchboard.
|
||||
*/
|
||||
Switchboard() {
|
||||
mCallsManager = CallsManager.getInstance();
|
||||
mOutgoingCallsManager = new OutgoingCallsManager(this);
|
||||
mCallServiceFinder = new CallServiceFinder(this, mOutgoingCallsManager);
|
||||
mSelectorFinder = new CallServiceSelectorFinder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to place an outgoing call to the specified handle.
|
||||
|
@ -157,7 +170,7 @@ final class Switchboard {
|
|||
* see {@link OutgoingCallProcessor}.
|
||||
*/
|
||||
void handleSuccessfulOutgoingCall(Call call) {
|
||||
// TODO(gilad): More here.
|
||||
mCallsManager.handleSuccessfulOutgoingCall(call);
|
||||
|
||||
// Process additional (new) calls, if any.
|
||||
processNewOutgoingCalls();
|
||||
|
@ -236,14 +249,7 @@ final class Switchboard {
|
|||
List<ICallServiceSelector> selectors = Lists.newArrayList();
|
||||
selectors.addAll(mSelectors);
|
||||
|
||||
// Create the processor for this (outgoing) call and store it in a map such that call
|
||||
// attempts can be aborted etc.
|
||||
// TODO(gilad): Consider passing mSelector as an immutable set.
|
||||
OutgoingCallProcessor processor =
|
||||
new OutgoingCallProcessor(call, mCallServices, selectors, this);
|
||||
|
||||
outgoingCallProcessors.put(call, processor);
|
||||
processor.process();
|
||||
mOutgoingCallsManager.placeCall(call, mCallServices, selectors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -106,14 +106,10 @@ public class TestCallService extends CallService {
|
|||
public void call(CallInfo callInfo) {
|
||||
Log.i(TAG, "call(" + callInfo + ")");
|
||||
|
||||
try {
|
||||
createCall(callInfo.getId());
|
||||
createCall(callInfo.getId());
|
||||
|
||||
// This creates a call within CallsManager starting at the DIALING state.
|
||||
// TODO(santoscordon): When we define the call states, consider renaming newOutgoingCall
|
||||
// to newDialingCall to match the states exactly and as an indication of the starting
|
||||
// state for this new call. This depends on what the states are ultimately defined as.
|
||||
mCallsManagerAdapter.newOutgoingCall(callInfo.getId());
|
||||
try {
|
||||
mCallsManagerAdapter.handleSuccessfulOutgoingCall(callInfo.getId());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to create a newOutgoingCall().", e);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue