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/BinderDeallocator.java b/src/com/android/telecomm/BinderDeallocator.java
deleted file mode 100644
index 22f0758..0000000
--- a/src/com/android/telecomm/BinderDeallocator.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 com.google.common.collect.Sets;
-
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * Keeps track of all outstanding binder connections and provides a facility to clean up unnecessary
- * binders (after initializing outgoing or incoming calls). This in turn allows the classes involved
- * in incoming and outgoing initialization to not have to unbind at all possible failure conditions,
- * which are numerous.
- *
- * To provide (theoretical) context, the two candidate approaches seem to be (1) provide a notion of
- * guaranteed outcome to every attempted action only deallocating resources upon no pending actions,
- * and (2) while other outcomes may be provided, only guarantee the potential outcome of success and
- * then rely on timeouts to treat all expired actions as failures. Since Telecomm pretty much has to
- * deal with timeouts (e.g. due to relying on third-party code), it seems to make little or no sense
- * to guarantee an outcome to every attempted action particularly via using timeouts. Instead, since
- * relying on timeouts is required in both cases, using a centralized timeout solution appears to be
- * beneficial. This gives rise to the following implementation. Any time the switchboard enters what
- * can be thought of as a critical section, it is likely to cause certain resources to be created or
- * bounded. Additional resources may be created and/or bounded throughout that section just as well.
- * To account for that, the switchboard is expected to acquire a use permit and then release it once
- * the critical section is exited. Since two or more critical sections may overlap (by design), it
- * is imperative that no resources are deallocated until the last critical section is exited. This
- * ensures that resources that have been obtained but not yet used aren't identified as unused which
- * would otherwise lead to their removal. This also allows switchboard to maintain a single timeout
- * loop, freeing for example the incoming/outgoing call managers from needing to implement the same.
- * Once the switchboard expires certain actions (e.g. pending outgoing calls) it should also release
- * the corresponding permit, just as it would upon explicit success/failure outcomes. Subsequently,
- * once all pending permits are released, a resource-deallocation cycle can safely follow. In terms
- * of implementation, there is no real need to associate specific resources with the action/s during
- * which these resources turned up to be necessary such that counting permits seems sufficient.
- */
-final class BinderDeallocator {
-
- /**
- * The number of actions currently permitted to use previously-allocated resources and/or create
- * new ones.
- */
- private int mPermitCount = 0;
-
- /**
- * The set of all known binders, either in use or potentially about to be used.
- */
- @SuppressWarnings("rawtypes")
- private final Set<ServiceBinder> mBinders = Sets.newHashSet();
-
- /**
- * Accounts for the action entering a critical section (i.e. potentially needing access to
- * resources).
- */
- void acquireUsePermit() {
- ThreadUtil.checkOnMainThread();
-
- mPermitCount++;
- }
-
- /**
- * Updates the set of binders.
- *
- * @param binders The binders to potentially add to the all-inclusive set of known binders,
- * see {@link #mBinders}.
- */
- @SuppressWarnings("rawtypes")
- void updateBinders(Set<? extends ServiceBinder> binders) {
- ThreadUtil.checkOnMainThread();
-
- if (binders != null) {
- mBinders.addAll(binders);
- }
- }
-
- /**
- * Accounts for the action exiting a critical section (i.e. no longer needing access to
- * resources).
- */
- void releaseUsePermit() {
- ThreadUtil.checkOnMainThread();
-
- if (mPermitCount < 1) {
- Log.wtf(this, "releaseUsePermit should only be invoked upon mPermitCount > 0");
- } else if (--mPermitCount == 0) {
- deallocateUnusedResources();
- }
- }
-
- /**
- * Starts a resource-deallocation cycle.
- */
- @SuppressWarnings("rawtypes")
- private void deallocateUnusedResources() {
- Iterator<ServiceBinder> iterator = mBinders.iterator();
- while (iterator.hasNext()) {
- ServiceBinder binder = iterator.next();
- if (binder.getAssociatedCallCount() < 1) {
- binder.unbind();
- mBinders.remove(binder);
- }
- }
- }
-}
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 024d7d4..e21f3de 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -278,9 +278,17 @@
*/
void clearCallService() {
if (mCallService != null) {
- decrementAssociatedCallCount(mCallService);
- mCallService.removeCall(this);
+ CallServiceWrapper callServiceTemp = mCallService;
mCallService = null;
+ callServiceTemp.removeCall(this);
+
+ // Decrementing the count can cause the service to unbind, which itself can trigger the
+ // service-death code. Since the service death code tries to clean up any associated
+ // calls, we need to make sure to remove that information (e.g., removeCall()) before
+ // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
+ // necessary, but cleaning up mCallService prior to triggering an unbind is good to do.
+ // If you change this, make sure to update {@link clearCallServiceSelector} as well.
+ decrementAssociatedCallCount(callServiceTemp);
}
}
@@ -293,16 +301,19 @@
clearCallServiceSelector();
+ selector.incrementAssociatedCallCount();
mCallServiceSelector = selector;
mCallServiceSelector.addCall(this);
}
void clearCallServiceSelector() {
if (mCallServiceSelector != null) {
- // TODO(sail): Stop leaking selectors.
- // decrementAssociatedCallCount(mCallServiceSelector);
- mCallServiceSelector.removeCall(this);
+ CallServiceSelectorWrapper selectorTemp = mCallServiceSelector;
mCallServiceSelector = null;
+ selectorTemp.removeCall(this);
+
+ // See comment on {@link #clearCallService}.
+ decrementAssociatedCallCount(selectorTemp);
}
}
diff --git a/src/com/android/telecomm/CallServiceRepository.java b/src/com/android/telecomm/CallServiceRepository.java
index 50ef977..c58a332 100644
--- a/src/com/android/telecomm/CallServiceRepository.java
+++ b/src/com/android/telecomm/CallServiceRepository.java
@@ -29,6 +29,8 @@
import com.android.internal.telecomm.ICallServiceLookupResponse;
import com.android.internal.telecomm.ICallServiceProvider;
import com.google.common.base.Preconditions;
+
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -90,6 +92,11 @@
private final Map<ComponentName, CallServiceWrapper> mCallServices = Maps.newHashMap();
/**
+ * The set of call-service components found during a single lookup.
+ */
+ private final Set<ComponentName> mFoundCallServices = Sets.newHashSet();
+
+ /**
* Persists the specified parameters.
*
* @param switchboard The switchboard.
@@ -120,6 +127,7 @@
return;
}
+ mFoundCallServices.clear();
List<ComponentName> providerNames = getProviderNames();
if (providerNames.isEmpty()) {
Log.i(this, "No ICallServiceProvider implementations found, bailing out.");
@@ -156,28 +164,6 @@
}
/**
- * Iterates through the map of active services and removes the ones that are not associated
- * with active calls.
- * TODO(gilad): Invoke this from Switchboard upon resource deallocation cycles.
- */
- void purgeInactiveCallServices() {
- Iterator<ComponentName> iterator = mCallServices.keySet().iterator();
- while (iterator.hasNext()) {
- ComponentName callServiceName = iterator.next();
- CallServiceWrapper callService = mCallServices.get(callServiceName);
-
- // TODO(gilad): Either add ICallService.getActiveCallCount() or have this tracked by the
- // Switchboard if we rather not rely on 3rd-party code to do the bookkeeping for us. If
- // we prefer the latter, we can also have purgeInactiveCallService(descriptor).
- // Otherwise this might look something like:
- //
- // if (callService.getActiveCallCount() < 1) {
- // mCallServices.remove(callServiceName);
- // }
- }
- }
-
- /**
* Returns the all-inclusive list of call-service-provider names.
*
* @return The list containing the (component) names of all known ICallServiceProvider
@@ -258,6 +244,7 @@
if (mOutstandingProviders.contains(providerName)) {
// Add all the call services from this provider to the call-service cache.
for (CallServiceDescriptor descriptor : callServiceDescriptors) {
+ mFoundCallServices.add(descriptor.getServiceComponent());
registerCallService(descriptor);
}
@@ -310,6 +297,22 @@
mHandler.removeCallbacks(mTimeoutLookupTerminator);
mOutstandingProviders.clear();
+ // Clean out any old call services. Since the set of call services can change (app installs,
+ // uninstalls, etc.) between lookups, we need to make sure that we purge our registry
+ // of any old call services (call services which were not found in this lookup). However,
+ // we dont purge any call services that might still have calls associated with them.
+ for (ComponentName component : ImmutableSet.copyOf(mCallServices.keySet().iterator())) {
+ if (!mFoundCallServices.contains(component)) {
+ // This component is registered, but was not found in the latest lookup...so try
+ // to remove it.
+ CallServiceWrapper callService = mCallServices.get(component);
+ if (callService.getAssociatedCallCount() < 1) {
+ mCallServices.remove(component);
+ }
+ }
+ }
+ mFoundCallServices.clear();
+
updateSwitchboard();
mIsLookupInProgress = false;
}
diff --git a/src/com/android/telecomm/CallServiceSelectorRepository.java b/src/com/android/telecomm/CallServiceSelectorRepository.java
index 709d440..90e2d55 100644
--- a/src/com/android/telecomm/CallServiceSelectorRepository.java
+++ b/src/com/android/telecomm/CallServiceSelectorRepository.java
@@ -17,7 +17,6 @@
package com.android.telecomm;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -26,6 +25,7 @@
import com.android.internal.telecomm.ICallServiceSelector;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -36,7 +36,7 @@
* Helper class to retrieve {@link ICallServiceSelector} implementations on the device and
* asynchronously bind to them.
*/
-final class CallServiceSelectorRepository {
+final class CallServiceSelectorRepository implements ServiceBinder.Listener {
private final Switchboard mSwitchboard;
private final OutgoingCallsManager mOutgoingCallsManager;
@@ -68,15 +68,57 @@
ThreadUtil.checkOnMainThread();
List<ComponentName> selectorNames = getSelectorNames();
+ List<CallServiceSelectorWrapper> foundSelectors = Lists.newLinkedList();
+
+ // Register any new selectors.
for (ComponentName name : selectorNames) {
- if (!mCallServiceSelectors.containsKey(name)) {
- mCallServiceSelectors.put(name, new CallServiceSelectorWrapper(name,
- CallsManager.getInstance(), mOutgoingCallsManager));
+ CallServiceSelectorWrapper selector = mCallServiceSelectors.get(name);
+ if (selector == null) {
+ selector = createWrapper(name);
+ mCallServiceSelectors.put(name, selector);
+ }
+
+ if (TelephonyUtil.isTelephonySelector(selector)) {
+ // Add telephony selectors to the end to serve as a fallback.
+ foundSelectors.add(selector);
+ } else {
+ // TODO(sail): Need a way to order selectors.
+ foundSelectors.add(0, selector);
}
}
Log.i(this, "Found %d implementations of ICallServiceSelector", selectorNames.size());
- updateSwitchboard();
+ updateSwitchboard(foundSelectors);
+ }
+
+ /**
+ * Removes the specified selector (as a ServiceBinder) from the map of registered selectors.
+ *
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void onUnbind(ServiceBinder serviceBinder) {
+ if (serviceBinder instanceof CallServiceSelectorWrapper) {
+ CallServiceSelectorWrapper selector = (CallServiceSelectorWrapper) serviceBinder;
+ mCallServiceSelectors.remove(selector.getComponentName());
+ } else {
+ Log.wtf(this, "Received unbind notice from non-selector: %s.",
+ serviceBinder.getComponentName().flattenToShortString());
+ }
+ }
+
+ /**
+ * Creates a wrapper for the specified component name and starts listening to it's unbind event.
+ *
+ * @param componentName The component name of the call-service selector.
+ * @return The wrapper for the selector.
+ */
+ private CallServiceSelectorWrapper createWrapper(ComponentName componentName) {
+ CallServiceSelectorWrapper selector = new CallServiceSelectorWrapper(
+ componentName, CallsManager.getInstance(), mOutgoingCallsManager);
+ selector.addListener(this);
+ return selector;
}
/**
@@ -104,21 +146,10 @@
/**
* Updates the switchboard passing the relevant call services selectors.
+ *
+ * @param selectors The selectors found during lookup.
*/
- private void updateSwitchboard() {
- ThreadUtil.checkOnMainThread();
-
- List<CallServiceSelectorWrapper> selectors = Lists.newLinkedList();
- for (CallServiceSelectorWrapper selector : mCallServiceSelectors.values()) {
- if (TelephonyUtil.isTelephonySelector(selector)) {
- // Add telephony selectors to the end to serve as a fallback.
- selectors.add(selector);
- } else {
- // TODO(sail): Need a way to order selectors.
- selectors.add(0, selector);
- }
- }
-
- mSwitchboard.setSelectors(ImmutableList.copyOf((selectors)));
+ private void updateSwitchboard(List<CallServiceSelectorWrapper> selectors) {
+ mSwitchboard.setSelectors(ImmutableList.copyOf(selectors));
}
}
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 81243a9..04dd9ad 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -550,6 +550,7 @@
private void removeCall(Call call) {
// If a handoff is pending then the original call shouldn't be removed.
Preconditions.checkState(call.getHandoffCallServiceDescriptor() == null);
+ Log.v(this, "removeCall(%s)", call);
call.clearCallService();
call.clearCallServiceSelector();
diff --git a/src/com/android/telecomm/OutgoingCallsManager.java b/src/com/android/telecomm/OutgoingCallsManager.java
index def5211..7bbfd04 100644
--- a/src/com/android/telecomm/OutgoingCallsManager.java
+++ b/src/com/android/telecomm/OutgoingCallsManager.java
@@ -162,12 +162,17 @@
* Aborts any ongoing attempts to connect the specified (outgoing) call.
*
* @param call The call to be aborted.
+ * @return False if the call was not found; True otherwise, indicating that the abort was
+ * successful.
*/
- void abort(Call call) {
+ boolean abort(Call call) {
Log.v(this, "abort, call: %s", call);
OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(call);
if (processor != null) {
processor.abort();
+ return true;
}
+
+ return false;
}
}
diff --git a/src/com/android/telecomm/ServiceBinder.java b/src/com/android/telecomm/ServiceBinder.java
index b5c0050..3dc86be 100644
--- a/src/com/android/telecomm/ServiceBinder.java
+++ b/src/com/android/telecomm/ServiceBinder.java
@@ -25,6 +25,8 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -40,8 +42,16 @@
* Callback to notify after a binding succeeds or fails.
*/
interface BindCallback {
- public void onSuccess();
- public void onFailure();
+ void onSuccess();
+ void onFailure();
+ }
+
+ /**
+ * Listener for bind events on ServiceBinder.
+ */
+ interface Listener {
+ @SuppressWarnings("rawtypes")
+ void onUnbind(ServiceBinder serviceBinder);
}
/**
@@ -89,10 +99,12 @@
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
ThreadUtil.checkOnMainThread();
+ Log.i(this, "Service bound %s", componentName);
// Unbind request was queued so unbind immediately.
if (mIsBindingAborted) {
clearAbort();
+ logServiceDisconnected("onServiceConnected");
mContext.unbindService(this);
handleFailedConnection();
return;
@@ -105,6 +117,8 @@
@Override
public void onServiceDisconnected(ComponentName componentName) {
+ logServiceDisconnected("onServiceDisconnected");
+
mServiceConnection = null;
clearAbort();
@@ -130,7 +144,6 @@
/** The binder provided by {@link ServiceConnection#onServiceConnected} */
private IBinder mBinder;
- /** The number of calls currently associated with this service. */
private int mAssociatedCallCount = 0;
/**
@@ -140,6 +153,11 @@
private boolean mIsBindingAborted;
/**
+ * Set of currently registered listeners.
+ */
+ private Set<Listener> mListeners = Sets.newHashSet();
+
+ /**
* Persists the specified parameters and initializes the new instance.
*
* @param serviceAction The intent-action used with {@link Context#bindService}.
@@ -156,11 +174,19 @@
final void incrementAssociatedCallCount() {
mAssociatedCallCount++;
+ Log.v(this, "Call count increment %d, %s", mAssociatedCallCount,
+ mComponentName.flattenToShortString());
}
final void decrementAssociatedCallCount() {
if (mAssociatedCallCount > 0) {
mAssociatedCallCount--;
+ Log.v(this, "Call count decrement %d, %s", mAssociatedCallCount,
+ mComponentName.flattenToShortString());
+
+ if (mAssociatedCallCount == 0) {
+ unbind();
+ }
} else {
Log.wtf(this, "%s: ignoring a request to decrement mAssociatedCallCount below zero",
mComponentName.getClassName());
@@ -181,6 +207,7 @@
// We're not yet bound, so queue up an abort request.
mIsBindingAborted = true;
} else {
+ logServiceDisconnected("unbind");
mContext.unbindService(mServiceConnection);
mServiceConnection = null;
setBinder(null);
@@ -200,6 +227,26 @@
return true;
}
+ final void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ final void removeListener(Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Logs a standard message upon service disconnection. This method exists because there is no
+ * single method called whenever the service unbinds and we want to log the same string in all
+ * instances where that occurs. (Context.unbindService() does not cause onServiceDisconnected
+ * to execute).
+ *
+ * @param sourceTag Tag to disambiguate
+ */
+ private void logServiceDisconnected(String sourceTag) {
+ Log.i(this, "Service unbound %s, from %s.", mComponentName, sourceTag);
+ }
+
/**
* Notifies all the outstanding callbacks that the service is successfully bound. The list of
* outstanding callbacks is cleared afterwards.
@@ -239,8 +286,19 @@
* @param binder The new binder value.
*/
private void setBinder(IBinder binder) {
- mBinder = binder;
- setServiceInterface(binder);
+ if (mBinder != binder) {
+ mBinder = binder;
+
+ setServiceInterface(binder);
+
+ if (binder == null) {
+ // Use a copy of the listener list to allow the listeners to unregister themselves
+ // as part of the unbind without causing issues.
+ for (Listener l : ImmutableSet.copyOf(mListeners)) {
+ l.onUnbind(this);
+ }
+ }
+ }
}
/**
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);
}
}
}
diff --git a/src/com/android/telecomm/Timeouts.java b/src/com/android/telecomm/Timeouts.java
index 4f8dc1c..0221ccd 100644
--- a/src/com/android/telecomm/Timeouts.java
+++ b/src/com/android/telecomm/Timeouts.java
@@ -54,20 +54,10 @@
}
/**
- * @return How frequently, in milliseconds, to run {@link Switchboard}'s clean-up "tick" cycle.
- */
- public static long getTickMillis() {
- return get("tick_ms", 250);
- }
-
- /**
* Returns the longest period, in milliseconds, each new outgoing call is allowed to wait before
* being established. If the call does not connect before this time, abort the call.
- *
- * @return The longest period, in milliseconds, each new call is allowed to wait before being
- * established.
*/
public static long getNewOutgoingCallMillis() {
- return get("new_outgoing_call_ms", 5000);
+ return get("new_outgoing_call_ms", 60 * 1000L);
}
}
diff --git a/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java b/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
index 86319a4..28085ea 100644
--- a/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
+++ b/tests/src/com/android/telecomm/testcallservice/DummyCallServiceSelector.java
@@ -33,14 +33,12 @@
public class DummyCallServiceSelector extends CallServiceSelector {
private static DummyCallServiceSelector sInstance;
private static final String SCHEME_TEL = "tel";
- private static final String TELEPHONY_PACKAGE_NAME =
- "com.android.phone";
+ private static final String TELEPHONY_PACKAGE_NAME = "com.android.phone";
private static final String CUSTOM_HANDOFF_KEY = "custom_handoff_key";
private static final String CUSTOM_HANDOFF_VALUE = "custom_handoff_value";
public DummyCallServiceSelector() {
log("constructor");
- Preconditions.checkState(sInstance == null);
sInstance = this;
}