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/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);
+ }
+ }
+ }
}
/**