Simplify deallocation/unbinding of services.
Call counts of call services and selectors are kept current during call
and during the outgoing call process. This allows us to unbind simply
when the call-count goes down to 0.
A second optimization that can be made is to remove associated-call
counts from ServiceBinder and use the callIdMapper to maintain counts of
the associated calls. This binds the call count to the mapper items,
however there are two small roadblocks:
1. It isn't as easy to deal with the replace() scenario, but doable
2. The caller-ID mapper implementations between CS and selectors are
separate and it's nice to keep a single associated count implementation
for all ServiceBinders...this is also addressable, just not that
important at the moment.
Change-Id: Ibbf894ed5b7dd9ede1b088e530dd9cc2e0e649c2
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index fdc271e..82f94fa 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -20,6 +20,8 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+
+import android.os.Message;
import android.telecomm.CallInfo;
import android.telecomm.CallServiceDescriptor;
import android.telecomm.TelecommConstants;
@@ -41,6 +43,7 @@
* - switching active calls between call services.
*/
final class Switchboard {
+ private final static int MSG_EXPIRE_STALE_CALL = 1;
private final CallsManager mCallsManager;
@@ -54,22 +57,16 @@
private final CallServiceSelectorRepository mSelectorRepository;
- private final BinderDeallocator mBinderDeallocator;
-
/** Used to schedule tasks on the main (UI) thread. */
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- /**
- * Executes a single tick task and potentially schedules the next such that polling continues
- * as long as necessary.
- * NOTE(gilad): by design no two tick invocations should ever overlap.
- */
- private final Runnable mTicker = new Runnable() {
+ private final Handler mHandler = new Handler() {
@Override
- public void run() {
- tick();
- if (isTicking()) {
- scheduleNextTick();
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_EXPIRE_STALE_CALL:
+ expireStaleCall((Call) msg.obj);
+ break;
+ default:
+ Log.wtf(Switchboard.this, "Unexpected message %d.", msg.what);
}
}
};
@@ -102,14 +99,14 @@
* Persists the specified parameters and initializes Switchboard.
*/
Switchboard(CallsManager callsManager) {
+ ThreadUtil.checkOnMainThread();
+
mCallsManager = callsManager;
mOutgoingCallsManager = new OutgoingCallsManager(this);
mIncomingCallsManager = new IncomingCallsManager(this);
mSelectorRepository = new CallServiceSelectorRepository(this, mOutgoingCallsManager);
mCallServiceRepository =
new CallServiceRepository(this, mOutgoingCallsManager, mIncomingCallsManager);
-
- mBinderDeallocator = new BinderDeallocator();
}
/**
@@ -120,8 +117,6 @@
* @param call The yet-to-be-connected outgoing-call object.
*/
void placeOutgoingCall(Call call) {
- mBinderDeallocator.acquireUsePermit();
-
// Reset prior to initiating the next lookup. One case to consider is (1) placeOutgoingCall
// is invoked with call A, (2) the call-service lookup completes, but the one for selectors
// does not, (3) placeOutgoingCall is invoked again with call B, (4) mCallServices below is
@@ -137,6 +132,9 @@
mLookupId++;
mCallServiceRepository.initiateLookup(mLookupId);
mSelectorRepository.initiateLookup(mLookupId);
+
+ Message msg = mHandler.obtainMessage(MSG_EXPIRE_STALE_CALL, call);
+ mHandler.sendMessageDelayed(msg, Timeouts.getNewOutgoingCallMillis());
}
/**
@@ -151,8 +149,6 @@
*/
void retrieveIncomingCall(Call call, CallServiceDescriptor descriptor, Bundle extras) {
Log.d(this, "retrieveIncomingCall");
- mBinderDeallocator.acquireUsePermit();
-
CallServiceWrapper callService = mCallServiceRepository.getCallService(descriptor);
call.setCallService(callService);
mIncomingCallsManager.retrieveIncomingCall(call, extras);
@@ -167,8 +163,6 @@
* hence effectively omitted from the specified list.
*/
void setCallServices(Set<CallServiceWrapper> callServices) {
- mBinderDeallocator.updateBinders(mCallServices);
-
mCallServices.clear();
mCallServices.addAll(callServices);
processNewOutgoingCalls();
@@ -182,7 +176,6 @@
* which the selectors are tried.
*/
void setSelectors(ImmutableCollection<CallServiceSelectorWrapper> selectors) {
- // TODO(santoscordon):: Need to invoke updateBinders(selectors).
ThreadUtil.checkOnMainThread();
Preconditions.checkNotNull(selectors);
@@ -225,7 +218,6 @@
Log.d(this, "handleSuccessfulIncomingCall");
mCallsManager.handleSuccessfulIncomingCall(call, callInfo);
- mBinderDeallocator.releaseUsePermit();
}
/**
@@ -240,9 +232,6 @@
// Since we set the call service before calling into incoming-calls manager, we clear it for
// good measure if an error is reported.
call.clearCallService();
-
- mBinderDeallocator.releaseUsePermit();
-
mCallsManager.handleUnsuccessfulIncomingCall(call);
// At the moment there is nothing more to do if an incoming call is not retrieved. We may at
@@ -262,22 +251,6 @@
}
/**
- * @return True if ticking should continue (or be resumed) and false otherwise.
- */
- private boolean isTicking() {
- // TODO(gilad): return true every time at least one outgoing call is pending (i.e. waiting
- // to be connected by a call service).
- return false;
- }
-
- /**
- * Schedules the next tick invocation.
- */
- private void scheduleNextTick() {
- mHandler.postDelayed(mTicker, Timeouts.getTickMillis());
- }
-
- /**
* Attempts to process the next new outgoing calls that have not yet been expired.
*/
private void processNewOutgoingCalls() {
@@ -345,48 +318,29 @@
private void finalizeOutgoingCall(Call call) {
mPendingOutgoingCalls.remove(call);
- mBinderDeallocator.releaseUsePermit();
processNewOutgoingCalls(); // Process additional (new) calls, if any.
}
/**
- * Performs the set of tasks that needs to be executed on polling basis.
- */
- private void tick() {
- // TODO(gilad): Add the necessary logic to support switching.
-
- expireStaleOutgoingCalls(mNewOutgoingCalls);
- expireStaleOutgoingCalls(mPendingOutgoingCalls);
- }
-
- /**
- * Identifies stale calls and takes the necessary steps to mark these as expired.
+ * Expires calls which are taking too long to connect.
*
- * @param calls The set of calls to iterate through.
+ * @param call The call to expire.
*/
- private void expireStaleOutgoingCalls(Set<Call> calls) {
- if (calls.isEmpty()) {
- return;
+ private void expireStaleCall(Call call) {
+ final long newCallTimeoutMillis = Timeouts.getNewOutgoingCallMillis();
+
+ if (call.getAgeMillis() < newCallTimeoutMillis) {
+ Log.wtf(this, "Expiring a call early. Age: %d, Time since attempt: %d",
+ call.getAgeMillis(), newCallTimeoutMillis);
}
- final long newCallTimeoutMillis = Timeouts.getNewOutgoingCallMillis();
- Iterator<Call> iterator = calls.iterator();
- while (iterator.hasNext()) {
- Call call = iterator.next();
- if (call.getAgeMillis() >= newCallTimeoutMillis) {
- Log.d(this, "Call %s timed out.", call);
- mOutgoingCallsManager.abort(call);
- calls.remove(call);
-
- // TODO(gilad): We may also have expired calls that are not yet associated with an
- // OutgoingCallProcessor (e.g. when newer calls are "blocked" on older-yet-expired
- // ones), in which case call.abort may need to be invoked directly. Alternatively
- // we can also create an OutgoingCallsManager instance for every new call at intent-
- // processing time.
-
- // TODO(gilad): Notify the user in the relevant cases (at least outgoing).
-
- mBinderDeallocator.releaseUsePermit();
+ if (mNewOutgoingCalls.remove(call)) {
+ // The call had not yet been processed so all we have to do is report the
+ // failure.
+ handleFailedOutgoingCall(call, true /* isAborted */);
+ } else if (mPendingOutgoingCalls.remove(call)) {
+ if (!mOutgoingCallsManager.abort(call)) {
+ Log.wtf(this, "Pending call failed to abort, call: %s.", call);
}
}
}