Adding a CallService & CallServiceProvider concrete classes.
First step in no longer using ICallService and ICallServiceProviders
directly.
Adds a generic ServiceBinder class to manage binding/unbinding.
Change-Id: I7d26958dd85a99316dbd5e70caba3fd91d35911b
diff --git a/src/com/android/telecomm/CallServiceFinder.java b/src/com/android/telecomm/CallServiceFinder.java
index 7fc187b..d24c612 100644
--- a/src/com/android/telecomm/CallServiceFinder.java
+++ b/src/com/android/telecomm/CallServiceFinder.java
@@ -17,9 +17,7 @@
package com.android.telecomm;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -34,9 +32,11 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -56,12 +56,6 @@
*/
private static final int LOOKUP_TIMEOUT_MS = 100;
- /**
- * Used to retrieve all known ICallServiceProvider implementations from the framework.
- * TODO(gilad): Move to a more logical place for this to be shared.
- */
- static final String CALL_SERVICE_PROVIDER_CLASS_NAME = ICallServiceProvider.class.getName();
-
private final Switchboard mSwitchboard;
private final OutgoingCallsManager mOutgoingCallsManager;
@@ -72,11 +66,11 @@
private boolean mIsLookupInProgress = false;
/**
- * Used to generate unique lookup-cycle identifiers. Incremented upon initiateLookup calls.
+ * The current lookup-cycle ID. Incremented upon initiateLookup calls.
* TODO(gilad): If at all useful, consider porting the cycle ID concept to switchboard and
* have it centralized/shared between the two finders.
*/
- private int mNextLookupId = 0;
+ private int mLookupId = 0;
/**
* The set of bound call-services. Only populated via initiateLookup scenarios. Entries should
@@ -88,18 +82,25 @@
/**
* The set of bound call-service providers. Only populated via initiateLookup scenarios.
* Providers should only be removed upon unbinding.
+ * TODO(santoscordon): This can be removed once this class starts using CallServiceWrapper
+ * since we'll be able to unbind the providers within registerProvider().
*/
- private Set<ICallServiceProvider> mProviderRegistry = Sets.newHashSet();
+ private Set<CallServiceProviderWrapper> mProviderRegistry = Sets.newHashSet();
+
+ /**
+ * Map of {@link CallServiceProviderWrapper}s keyed by their ComponentName. Used as a long-lived
+ * cache in order to simplify management of service-wrapper construction/destruction.
+ */
+ private Map<ComponentName, CallServiceProviderWrapper> mProviderCache = Maps.newHashMap();
/**
* Stores the names of the providers to bind to in one lookup cycle. The set size represents
* the number of call-service providers this finder expects to hear back from upon initiating
* call-service lookups, see initiateLookup. Whenever all providers respond before the lookup
* timeout occurs, the complete set of (available) call services is passed to the switchboard
- * for further processing of outgoing calls etc. When the timeout occurs before all responds
+ * for further processing of outgoing calls etc. When the timeout occurs before all responses
* are received, the partial (potentially empty) set gets passed (to the switchboard) instead.
- * Cached providers do not require finding and hence are excluded from this set. Entries are
- * removed from this set as providers register.
+ * Entries are removed from this set as providers register.
*/
private Set<ComponentName> mUnregisteredProviders;
@@ -131,34 +132,31 @@
/**
* Initiates a lookup cycle for call-service providers. Must be called from the UI thread.
* TODO(gilad): Expand this comment to describe the lookup flow in more detail.
- *
- * @param context The relevant application context.
*/
- void initiateLookup(Context context) {
+ void initiateLookup() {
ThreadUtil.checkOnMainThread();
if (mIsLookupInProgress) {
// At most one active lookup is allowed at any given time, bail out.
return;
}
- List<ComponentName> providerNames = getProviderNames(context);
+ List<ComponentName> providerNames = getProviderNames();
if (providerNames.isEmpty()) {
Log.i(TAG, "No ICallServiceProvider implementations found.");
updateSwitchboard();
return;
}
+ mLookupId++;
mIsLookupInProgress = true;
mUnregisteredProviders = Sets.newHashSet();
- int lookupId = mNextLookupId++;
for (ComponentName name : providerNames) {
- if (!mProviderRegistry.contains(name)) {
- // The provider is either not yet registered or has been unregistered
- // due to unbinding etc.
- bindProvider(name, lookupId, context);
- mUnregisteredProviders.add(name);
- }
+ // Bind to each of the providers that were found. Some of the providers may already be
+ // bound, and in those cases the provider wrapper will still invoke registerProvider()
+ // allowing us to treat bound and unbound providers the same.
+ getProvider(name).bind();
+ mUnregisteredProviders.add(name);
}
int providerCount = providerNames.size();
@@ -181,16 +179,15 @@
/**
* Returns the all-inclusive list of call-service-provider names.
*
- * @param context The relevant/application context to query against.
* @return The list containing the (component) names of all known ICallServiceProvider
* implementations or the empty list upon no available providers.
*/
- private List<ComponentName> getProviderNames(Context context) {
+ private List<ComponentName> getProviderNames() {
// The list of provider names to return to the caller, may be populated below.
List<ComponentName> providerNames = Lists.newArrayList();
- PackageManager packageManager = context.getPackageManager();
- Intent intent = new Intent(CALL_SERVICE_PROVIDER_CLASS_NAME);
+ PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
+ Intent intent = new Intent(CallServiceProviderWrapper.CALL_SERVICE_PROVIDER_ACTION);
for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
ServiceInfo serviceInfo = entry.serviceInfo;
if (serviceInfo != null) {
@@ -204,74 +201,31 @@
}
/**
- * Attempts to bind the specified provider and have it register upon successful binding. Also
- * performs the necessary wiring to unregister the provider upon un-binding.
- *
- * @param providerName The component name of the relevant provider.
- * @param lookupId The lookup-cycle ID.
- * @param context The relevant application context.
- */
- private void bindProvider(
- final ComponentName providerName, final int lookupId, Context context) {
-
- Preconditions.checkNotNull(providerName);
- Preconditions.checkNotNull(context);
-
- Intent serviceIntent =
- new Intent(CALL_SERVICE_PROVIDER_CLASS_NAME).setComponent(providerName);
- Log.i(TAG, "Binding to ICallServiceProvider through " + serviceIntent);
-
- // Connection object for the service binding.
- ServiceConnection connection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- ICallServiceProvider provider = ICallServiceProvider.Stub.asInterface(service);
- registerProvider(lookupId, providerName, provider);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- unregisterProvider(providerName);
- }
- };
-
- if (!context.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
- // TODO(santoscordon): Handle error.
- }
- }
-
- /**
* Queries the supplied provider asynchronously for its CallServices and passes the list through
* to {@link #registerCallServices} which will relinquish control back to switchboard.
*
- * @param lookupId The lookup-cycle ID.
* @param providerName The component name of the relevant provider.
* @param provider The provider object to register.
*/
- private void registerProvider(
- final int lookupId,
- final ComponentName providerName,
- final ICallServiceProvider provider) {
+ void registerProvider(
+ final ComponentName providerName, final CallServiceProviderWrapper provider) {
// Query the provider for {@link ICallService} implementations.
- try {
- provider.lookupCallServices(new ICallServiceLookupResponse.Stub() {
- @Override
- public void setCallServices(final List<IBinder> binderList) {
- mHandler.post(new Runnable() {
- @Override public void run() {
- Set<ICallService> callServices = Sets.newHashSet();
- for (IBinder binder : binderList) {
- callServices.add(ICallService.Stub.asInterface(binder));
- }
- registerCallServices(lookupId, providerName, provider, callServices);
+ provider.lookupCallServices(new ICallServiceLookupResponse.Stub() {
+ @Override
+ public void setCallServices(final List<IBinder> binderList) {
+ // TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity()?
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ Set<ICallService> callServices = Sets.newHashSet();
+ for (IBinder binder : binderList) {
+ callServices.add(ICallService.Stub.asInterface(binder));
}
- });
- }
- });
- } catch (RemoteException e) {
- Log.e(TAG, "Could not retrieve call services from: " + providerName);
- }
+ registerCallServices(providerName, provider, callServices);
+ }
+ });
+ }
+ });
}
/**
@@ -279,24 +233,20 @@
* bookkeeping to potentially return control to the switchboard before the timeout for the
* current lookup cycle.
*
- * @param lookupId The lookup-cycle ID.
* @param providerName The component name of the relevant provider.
* @param provider The provider associated with callServices.
* @param callServices The {@link CallService}s to register.
*/
private void registerCallServices(
- int lookupId,
ComponentName providerName,
- ICallServiceProvider provider,
+ CallServiceProviderWrapper provider,
Set<ICallService> callServices) {
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.
-
if (mUnregisteredProviders.remove(providerName)) {
mProviderRegistry.add(provider);
+
+ // Add all the call services from this provider to the call-service registry.
for (ICallService callService : callServices) {
try {
CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
@@ -307,16 +257,12 @@
}
}
- // TODO(gilad): Introduce a map to retain the association between call services
- // and the corresponding provider such that mCallServiceRegistry can be updated
- // upon unregisterProvider calls.
-
if (mUnregisteredProviders.size() < 1) {
terminateLookup(); // No other providers to wait for.
}
} else {
- Log.i(TAG, "Received multiple lists of call services in lookup " + lookupId +
- " from " + providerName);
+ Log.i(TAG, "Unexpected list of call services in lookup " + mLookupId + " from " +
+ providerName);
}
}
@@ -325,7 +271,7 @@
*
* @param providerName The component name of the relevant provider.
*/
- private void unregisterProvider(ComponentName providerName) {
+ void unregisterProvider(ComponentName providerName) {
ThreadUtil.checkOnMainThread();
mProviderRegistry.remove(providerName);
}
@@ -335,6 +281,7 @@
*/
private void terminateLookup() {
mHandler.removeCallbacks(mLookupTerminator);
+ mUnregisteredProviders.clear();
updateSwitchboard();
mIsLookupInProgress = false;
@@ -348,4 +295,22 @@
ThreadUtil.checkOnMainThread();
mSwitchboard.setCallServices(mCallServiceRegistry);
}
+
+ /**
+ * Returns the call-service provider wrapper for the specified componentName. Creates a new one
+ * if none is found in the cache.
+ *
+ * @param ComponentName The component name of the provider.
+ */
+ private CallServiceProviderWrapper getProvider(ComponentName componentName) {
+ Preconditions.checkNotNull(componentName);
+
+ CallServiceProviderWrapper provider = mProviderCache.get(componentName);
+ if (provider == null) {
+ provider = new CallServiceProviderWrapper(componentName, this);
+ mProviderCache.put(componentName, provider);
+ }
+
+ return provider;
+ }
}