Simplify Respositories, Switchboard and Switchboard-indirection.
This is a step into separating Call into Call + Connection.
Changes:
1. Update Repositories to:
A. Share code via the new BaseRepository
B. Perform a lookup per call instead of attempting to have 1 lookup
work for concurrent calls. This allowed removal of extra state
out of the repositories (mIsLookupInProgress) and out of
Switchboard (mNewOutgoingCalls, mPendingOutgoingCalls, etc).
2. Add a OutgoingCallEntry class to Switchboard to support 1 service
lookup per outgoing call. The new class maintains the necessary
state (CS collection & selector collection).
3. Outgoing/IncomingCallsManager now reports success/failure directly
to the Call class instead of indirecting through the switchboard.
4. Switchboard, for the time being, kept the outgoing call timeout and
triggers it through OutgoingCallEntry.
Change-Id: I01196dd5384ad256cf09035018a76abaadb2c04d
diff --git a/src/com/android/telecomm/BaseRepository.java b/src/com/android/telecomm/BaseRepository.java
new file mode 100644
index 0000000..4b913ee
--- /dev/null
+++ b/src/com/android/telecomm/BaseRepository.java
@@ -0,0 +1,94 @@
+/*
+ * 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.content.ComponentName;
+
+import com.google.common.collect.Maps;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Looks up, caches and returns binder services. Ensures that there is only one instance of a
+ * service per component.
+ */
+abstract class BaseRepository<ServiceClass extends ServiceBinder<?>>
+ implements ServiceBinder.Listener<ServiceClass> {
+
+ /**
+ * Callback interface for notifying when a lookup has completed.
+ */
+ interface LookupCallback<ServiceClass> {
+ void onComplete(Collection<ServiceClass> services);
+ }
+
+ /** Map of cached services. */
+ private final Map<ComponentName, ServiceClass> mServiceCache = Maps.newHashMap();
+
+ /**
+ * Removes the specified service from the cache when the service unbinds.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void onUnbind(ServiceClass service) {
+ mServiceCache.remove(service.getComponentName());
+ }
+
+ /**
+ * Looks up service implementations.
+ *
+ * @param callback The callback on which to return execution when complete.
+ */
+ final void lookupServices(final LookupCallback<ServiceClass> callback) {
+ onLookupServices(callback);
+ }
+
+ /**
+ * Performs the grunt work of the lookup, to be implemented by the subclass.
+ *
+ * @param callback The callback on which to return execution when complete.
+ */
+ protected abstract void onLookupServices(LookupCallback<ServiceClass> callback);
+
+ /**
+ * Creates a new service implementation for the specified component.
+ *
+ * @param componentName The component name for the service.
+ * @param param Sub-class-provided parameter, see {@link #getService}.
+ */
+ protected abstract ServiceClass onCreateNewServiceWrapper(
+ ComponentName componentName, Object param);
+
+ /**
+ * Returns a cached implementation of the service. If no cached version exists, makes a
+ * request to the subclass for a new implementation which will then be cached and returned.
+ *
+ * @param componentName The component name for the service.
+ * @param param Sub-class-provided parameter.
+ */
+ protected final ServiceClass getService(ComponentName componentName, Object param) {
+ ServiceClass service = mServiceCache.get(componentName);
+ if (service == null) {
+ service = onCreateNewServiceWrapper(componentName, param);
+ service.addListener(this);
+ mServiceCache.put(componentName, service);
+ }
+ return service;
+ }
+}
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index fe984cd..d86a8f9 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -394,6 +394,10 @@
}
void handleFailedOutgoing(boolean isAborted) {
+ if (isAborted) {
+ finalizeAbort();
+ }
+
// TODO(santoscordon): Replace this with state transitions related to "connecting".
for (Listener l : mListeners) {
l.onFailedOutgoingCall(this, isAborted);
@@ -479,7 +483,7 @@
// objects and remove any multi-class shared state of incoming and outgoing call
// processing.
Switchboard.getInstance().abortCall(this);
- } else {
+ } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Preconditions.checkNotNull(mCallService);
Log.i(this, "Send disconnect to call service for call: %s", this);
diff --git a/src/com/android/telecomm/CallServiceRepository.java b/src/com/android/telecomm/CallServiceRepository.java
index c58a332..0589358 100644
--- a/src/com/android/telecomm/CallServiceRepository.java
+++ b/src/com/android/telecomm/CallServiceRepository.java
@@ -22,308 +22,176 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
-import android.os.Looper;
import android.telecomm.CallServiceDescriptor;
import android.telecomm.TelecommConstants;
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;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
- * Uses package manager to find all implementations of {@link ICallServiceProvider} and uses them to
- * get the corresponding list of call-service descriptor. Ultimately provides the up-to-date list of
- * call services as {@link CallServiceWrapper}s. The resulting call services may or may not be bound
- * at the time {@link Switchboard#setCallServices} is invoked.
- * TODO(santoscordon): Add performance timing to async calls.
- * TODO(santoscordon): Need to unbind/remove unused call services stored in the cache.
+ * Searches for and returns call services.
*/
-final class CallServiceRepository {
+class CallServiceRepository extends BaseRepository<CallServiceWrapper> {
+ /**
+ * The representation of a single lookup. Maintains lookup state and invokes the "complete"
+ * callback when finished.
+ */
+ private final class CallServiceLookup {
+ final Set<ComponentName> mOutstandingProviders = Sets.newHashSet();
+ final Set<CallServiceWrapper> mServices = Sets.newHashSet();
+ final LookupCallback<CallServiceWrapper> mCallback;
- private final Switchboard mSwitchboard;
+ CallServiceLookup(LookupCallback<CallServiceWrapper> callback) {
+ mCallback = callback;
+ }
+
+ /** Starts the lookup. */
+ void start() {
+ List<ComponentName> providerNames = getProviderNames();
+ if (providerNames.isEmpty()) {
+ finishLookup();
+ return;
+ }
+
+ for (ComponentName name : providerNames) {
+ mOutstandingProviders.add(name);
+ queryProviderForCallServices(name);
+ }
+
+ Log.i(this, "Found %d implementations of ICallServiceProvider.",
+ mOutstandingProviders.size());
+ }
+
+ /**
+ * Returns the all-inclusive list of call-service-provider names.
+ *
+ * @return The list containing the (component) names of all known ICallServiceProvider
+ * implementations or the empty list upon no available providers.
+ */
+ 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 = TelecommApp.getInstance().getPackageManager();
+ Intent intent = new Intent(TelecommConstants.ACTION_CALL_SERVICE_PROVIDER);
+ for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
+ ServiceInfo serviceInfo = entry.serviceInfo;
+ if (serviceInfo != null) {
+ // The entry resolves to a proper service, add it to the list of provider names.
+ providerNames.add(
+ new ComponentName(serviceInfo.packageName, serviceInfo.name));
+ }
+ }
+
+ return providerNames;
+ }
+
+ /**
+ * Attempts to obtain call-service descriptors from the specified provider (asynchronously)
+ * and passes the list through to {@link #processCallServices}, which then relinquishes
+ * control back to the switchboard.
+ *
+ * @param providerName The component name of the relevant provider.
+ */
+ private void queryProviderForCallServices(final ComponentName providerName) {
+ final CallServiceProviderWrapper provider = new CallServiceProviderWrapper(
+ providerName);
+
+ ICallServiceLookupResponse response = new ICallServiceLookupResponse.Stub() {
+ @Override
+ public void setCallServiceDescriptors(
+ final List<CallServiceDescriptor> callServiceDescriptors) {
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ processCallServices(provider, Sets.newHashSet(callServiceDescriptors));
+ }
+ });
+ }
+ };
+
+ Runnable errorCallback = new Runnable() {
+ @Override
+ public void run() {
+ processCallServices(provider, null);
+ }
+ };
+
+ provider.lookupCallServices(response, errorCallback);
+ }
+
+ /**
+ * Processes the call-service descriptors provided by the specified provider.
+ *
+ * @param provider The call-service provider.
+ * @param callServiceDescriptors The set of descriptors to process.
+ */
+ private void processCallServices(
+ CallServiceProviderWrapper provider,
+ Set<CallServiceDescriptor> callServiceDescriptors) {
+
+ // Descriptor lookup finished, we no longer need the provider.
+ provider.unbind();
+
+ ComponentName providerName = provider.getComponentName();
+ if (mOutstandingProviders.remove(providerName)) {
+ if (callServiceDescriptors != null) {
+ // Add all the call services from this provider to the call-service cache.
+ for (CallServiceDescriptor descriptor : callServiceDescriptors) {
+ mServices.add(getService(descriptor.getServiceComponent(), descriptor));
+ }
+ }
+
+ if (mOutstandingProviders.isEmpty()) {
+ finishLookup();
+ }
+ } else {
+ Log.i(this, "Unexpected call services from %s in lookup.", providerName);
+ }
+ }
+
+ void finishLookup() {
+ mCallback.onComplete(mServices);
+ }
+ }
private final OutgoingCallsManager mOutgoingCallsManager;
-
private final IncomingCallsManager mIncomingCallsManager;
+ private final Handler mHandler = new Handler();
- /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- private final Runnable mTimeoutLookupTerminator = new Runnable() {
- @Override
- public void run() {
- Log.d(CallServiceRepository.this, "Timed out processing providers");
- terminateLookup();
- }
- };
-
- /**
- * The current lookup-cycle ID, unique per invocation of {@link #initiateLookup}.
- */
- private int mLookupId = -1;
-
- /**
- * Determines whether or not a lookup cycle is already running.
- */
- private boolean mIsLookupInProgress = false;
-
- /**
- * Stores the names of the providers to bind to in one lookup cycle. During lookup two things
- * can happen:
- * - lookup can succeed, in this case this set will be empty at the end of the lookup.
- * - lookup can timeout, in this case any outstanding providers will be discarded.
- */
- private final Set<ComponentName> mOutstandingProviders = Sets.newHashSet();
-
- /**
- * The map of call-service wrappers keyed by their ComponentName. This is passed back to the
- * switchboard once lookup is complete.
- */
- 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.
- * @param outgoingCallsManager Manages the placing of outgoing calls.
- * @param incomingCallsManager Manages the incoming call initialization flow.
- */
+ /** Persists specified parameters. */
CallServiceRepository(
- Switchboard switchboard,
OutgoingCallsManager outgoingCallsManager,
IncomingCallsManager incomingCallsManager) {
- mSwitchboard = switchboard;
mOutgoingCallsManager = outgoingCallsManager;
mIncomingCallsManager = incomingCallsManager;
}
/**
- * 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 lookupId The switchboard-supplied lookup ID.
- */
- void initiateLookup(int lookupId) {
- ThreadUtil.checkOnMainThread();
-
- if (mIsLookupInProgress) {
- // At most one active lookup is allowed at any given time, bail out.
- return;
- }
-
- mFoundCallServices.clear();
- List<ComponentName> providerNames = getProviderNames();
- if (providerNames.isEmpty()) {
- Log.i(this, "No ICallServiceProvider implementations found, bailing out.");
- return;
- }
-
- mLookupId = lookupId;
- mIsLookupInProgress = true;
-
- mOutstandingProviders.clear();
- for (ComponentName name : providerNames) {
- mOutstandingProviders.add(name);
- lookupCallServices(name);
- }
-
- Log.i(this, "Found %d implementations of ICallServiceProvider.",
- mOutstandingProviders.size());
-
- // Schedule a timeout.
- mHandler.postDelayed(mTimeoutLookupTerminator, Timeouts.getProviderLookupMillis());
- }
-
- /**
- * Creates the requested call service or pulls the previously-created entry from memory.
- *
- * @param descriptor The call service descriptor.
- * @return The corresponding call-service wrapper or null upon failure to retrieve it.
- */
- CallServiceWrapper getCallService(CallServiceDescriptor descriptor) {
- // Create the new call-service wrapper and update {@link #mCallServices}.
- registerCallService(descriptor);
-
- return mCallServices.get(descriptor.getServiceComponent());
- }
-
- /**
- * Returns the all-inclusive list of call-service-provider names.
- *
- * @return The list containing the (component) names of all known ICallServiceProvider
- * implementations or the empty list upon no available providers.
- */
- 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 = TelecommApp.getInstance().getPackageManager();
- Intent intent = new Intent(TelecommConstants.ACTION_CALL_SERVICE_PROVIDER);
- for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
- ServiceInfo serviceInfo = entry.serviceInfo;
- if (serviceInfo != null) {
- // The entry resolves to a proper service, add it to the list of provider names.
- providerNames.add(
- new ComponentName(serviceInfo.packageName, serviceInfo.name));
- }
- }
-
- return providerNames;
- }
-
- /**
- * Attempts to obtain call-service descriptors from the specified provider (asynchronously) and
- * passes the list through to {@link #processCallServices}, which then relinquishes control back
- * to the switchboard.
- *
- * @param providerName The component name of the relevant provider.
- */
- private void lookupCallServices(final ComponentName providerName) {
- final CallServiceProviderWrapper provider = new CallServiceProviderWrapper(providerName);
-
- ICallServiceLookupResponse response = new ICallServiceLookupResponse.Stub() {
- @Override
- public void setCallServiceDescriptors(
- final List<CallServiceDescriptor> callServiceDescriptors) {
-
- // TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity()?
- mHandler.post(new Runnable() {
- @Override public void run() {
- if (mIsLookupInProgress) {
- processCallServices(provider, Sets.newHashSet(callServiceDescriptors));
- }
- }
- });
- }
- };
-
- Runnable errorCallback = new Runnable() {
- @Override public void run() {
- removeOutstandingProvider(providerName);
- }
- };
-
- provider.lookupCallServices(response, errorCallback);
- }
-
- /**
- * Creates {@link CallServiceWrapper}s from the given {@link CallServiceDescriptor}s.
- *
- * @param provider The provider associated with call services.
- * @param callServiceDescriptors The set of call service descriptors to process.
- */
- private void processCallServices(
- CallServiceProviderWrapper provider,
- Set<CallServiceDescriptor> callServiceDescriptors) {
-
- ThreadUtil.checkOnMainThread();
-
- Preconditions.checkNotNull(provider);
- Preconditions.checkNotNull(callServiceDescriptors);
-
- // The set of call-service descriptors is available, unbind the provider.
- provider.unbind();
-
- ComponentName providerName = provider.getComponentName();
- 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);
- }
-
- removeOutstandingProvider(providerName);
- } else {
- Log.i(this, "Unexpected call services from %s in lookup %s", providerName, mLookupId);
- }
- }
-
- /**
- * Creates a call-service wrapper from the given call-service descriptor if no cached instance
- * exists.
+ * Returns the call service implementation specified by the descriptor.
*
* @param descriptor The call-service descriptor.
*/
- private void registerCallService(CallServiceDescriptor descriptor) {
- Preconditions.checkNotNull(descriptor);
-
- // TODO(santoscordon): Rename getServiceComponent to getComponentName.
- ComponentName callServiceName = descriptor.getServiceComponent();
-
- CallServiceWrapper callService = mCallServices.get(callServiceName);
- if (callService == null) {
- mCallServices.put(callServiceName, new CallServiceWrapper(descriptor,
- mOutgoingCallsManager, mIncomingCallsManager));
- }
+ CallServiceWrapper getService(CallServiceDescriptor descriptor) {
+ return getService(descriptor.getServiceComponent(), descriptor);
}
- /**
- * Removes an entry from the set of outstanding providers. When the final outstanding
- * provider is removed, terminates the lookup.
- *
- * @param providerName The component name of the relevant provider.
- */
- private void removeOutstandingProvider(ComponentName providerName) {
- ThreadUtil.checkOnMainThread();
-
- if (mIsLookupInProgress) {
- mOutstandingProviders.remove(providerName);
- if (mOutstandingProviders.size() < 1) {
- terminateLookup(); // No other providers to wait for.
- }
- }
+ /** {@inheritDoc} */
+ @Override
+ protected void onLookupServices(LookupCallback<CallServiceWrapper> callback) {
+ new CallServiceLookup(callback).start();
}
- /**
- * Terminates the current lookup cycle, either due to a timeout or completed lookup.
- */
- private void terminateLookup() {
- 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;
- }
-
- /**
- * Updates the switchboard with the call services from the latest lookup.
- */
- private void updateSwitchboard() {
- ThreadUtil.checkOnMainThread();
-
- Set<CallServiceWrapper> callServices = Sets.newHashSet(mCallServices.values());
- mSwitchboard.setCallServices(callServices);
+ @Override
+ protected CallServiceWrapper onCreateNewServiceWrapper(ComponentName componentName,
+ Object param) {
+ return new CallServiceWrapper(
+ (CallServiceDescriptor) param, mOutgoingCallsManager, mIncomingCallsManager);
}
}
diff --git a/src/com/android/telecomm/CallServiceSelectorRepository.java b/src/com/android/telecomm/CallServiceSelectorRepository.java
index 90e2d55..99a2f52 100644
--- a/src/com/android/telecomm/CallServiceSelectorRepository.java
+++ b/src/com/android/telecomm/CallServiceSelectorRepository.java
@@ -36,9 +36,8 @@
* Helper class to retrieve {@link ICallServiceSelector} implementations on the device and
* asynchronously bind to them.
*/
-final class CallServiceSelectorRepository implements ServiceBinder.Listener {
+final class CallServiceSelectorRepository extends BaseRepository<CallServiceSelectorWrapper> {
- private final Switchboard mSwitchboard;
private final OutgoingCallsManager mOutgoingCallsManager;
/**
@@ -50,21 +49,15 @@
/**
* Persists the specified parameters and initializes the new instance.
*
- * @param switchboard The switchboard for this finer to work against.
+ * @param outgoingCallsManager The outgoing calls manager.
*/
- CallServiceSelectorRepository(
- Switchboard switchboard,
- OutgoingCallsManager outgoingCallsManager) {
- mSwitchboard = switchboard;
+ CallServiceSelectorRepository(OutgoingCallsManager outgoingCallsManager) {
mOutgoingCallsManager = outgoingCallsManager;
}
- /**
- * Initiates a lookup cycle for call-service selectors. Must be called from the UI thread.
- *
- * @param lookupId The switchboard-supplied lookup ID.
- */
- void initiateLookup(int lookupId) {
+ /** {@inheritDoc} */
+ @Override
+ protected void onLookupServices(LookupCallback<CallServiceSelectorWrapper> callback) {
ThreadUtil.checkOnMainThread();
List<ComponentName> selectorNames = getSelectorNames();
@@ -72,11 +65,7 @@
// Register any new selectors.
for (ComponentName name : selectorNames) {
- CallServiceSelectorWrapper selector = mCallServiceSelectors.get(name);
- if (selector == null) {
- selector = createWrapper(name);
- mCallServiceSelectors.put(name, selector);
- }
+ CallServiceSelectorWrapper selector = getService(name, null);
if (TelephonyUtil.isTelephonySelector(selector)) {
// Add telephony selectors to the end to serve as a fallback.
@@ -86,26 +75,17 @@
foundSelectors.add(0, selector);
}
}
-
Log.i(this, "Found %d implementations of ICallServiceSelector", selectorNames.size());
- updateSwitchboard(foundSelectors);
+ callback.onComplete(foundSelectors);
}
- /**
- * Removes the specified selector (as a ServiceBinder) from the map of registered selectors.
- *
- * {@inheritDoc}
- */
- @SuppressWarnings("rawtypes")
+ /** {@inheritDoc} */
@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());
- }
+ protected CallServiceSelectorWrapper onCreateNewServiceWrapper(
+ ComponentName componentName, Object param) {
+
+ return new CallServiceSelectorWrapper(
+ componentName, CallsManager.getInstance(), mOutgoingCallsManager);
}
/**
@@ -117,13 +97,12 @@
private CallServiceSelectorWrapper createWrapper(ComponentName componentName) {
CallServiceSelectorWrapper selector = new CallServiceSelectorWrapper(
componentName, CallsManager.getInstance(), mOutgoingCallsManager);
- selector.addListener(this);
return selector;
}
/**
- * @return The list containing the (component) names of all known ICallServiceSelector
- * implementations or the empty list upon no available selectors.
+ * Returns the list containing the (component) names of all known ICallServiceSelector
+ * implementations or the empty list upon no available selectors.
*/
private List<ComponentName> getSelectorNames() {
// The list of selector names to return to the caller, may be populated below.
@@ -143,13 +122,4 @@
return selectorNames;
}
-
- /**
- * Updates the switchboard passing the relevant call services selectors.
- *
- * @param selectors The selectors found during lookup.
- */
- 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 bef1dfa..85b9340 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -41,7 +41,7 @@
*/
public final class CallsManager implements Call.Listener {
- // TODO(santoscordon): Consider renaming this CallsManagerPlugin.
+ // TODO(santoscordon): Consider renaming this CallsManagerPlugin.
interface CallsManagerListener {
void onCallAdded(Call call);
void onCallRemoved(Call call);
diff --git a/src/com/android/telecomm/IncomingCallsManager.java b/src/com/android/telecomm/IncomingCallsManager.java
index 41b98f2..13ffb64 100644
--- a/src/com/android/telecomm/IncomingCallsManager.java
+++ b/src/com/android/telecomm/IncomingCallsManager.java
@@ -31,19 +31,9 @@
*/
final class IncomingCallsManager {
- private final Switchboard mSwitchboard;
private final Set<Call> mPendingIncomingCalls = Sets.newLinkedHashSet();
/**
- * Persists the specified parameters.
- *
- * @param switchboard The switchboard.
- */
- IncomingCallsManager(Switchboard switchboard) {
- mSwitchboard = switchboard;
- }
-
- /**
* Retrieves details of an incoming call through its associated call service.
*
* @param call The call object.
@@ -69,7 +59,7 @@
}
/**
- * Notifies the switchboard of a successful incoming call after removing it from the pending
+ * Notifies the incoming call of success after removing it from the pending
* list.
*
* @param callInfo The details of the call.
@@ -80,12 +70,12 @@
if (mPendingIncomingCalls.contains(call)) {
Log.d(this, "Incoming call %s found.", call);
mPendingIncomingCalls.remove(call);
- mSwitchboard.handleSuccessfulIncomingCall(call, callInfo);
+ call.handleSuccessfulIncoming(callInfo);
}
}
/**
- * Notifies switchboard of the failed incoming call after removing it from the pending list.
+ * Notifies incoming call of failure after removing it from the pending list.
*/
void handleFailedIncomingCall(Call call) {
ThreadUtil.checkOnMainThread();
@@ -94,7 +84,7 @@
Log.i(this, "Failed to get details for incoming call %s", call);
mPendingIncomingCalls.remove(call);
// The call was found still waiting for details. Consider it failed.
- mSwitchboard.handleFailedIncomingCall(call);
+ call.handleFailedIncoming();
}
}
}
diff --git a/src/com/android/telecomm/OutgoingCallProcessor.java b/src/com/android/telecomm/OutgoingCallProcessor.java
index 5c8d5f5..3874bd5 100644
--- a/src/com/android/telecomm/OutgoingCallProcessor.java
+++ b/src/com/android/telecomm/OutgoingCallProcessor.java
@@ -33,10 +33,9 @@
* Iterates through the selectors and gets a sorted list of supported call-service descriptors
* for each selector. Upon receiving each sorted list (one list per selector), each of the
* corresponding 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.
+ * attempted call is aborted, 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 call notified
* of the result.
*
* NOTE(gilad): Currently operating under the assumption that we'll have one timeout per (outgoing)
@@ -79,8 +78,6 @@
/** Manages all outgoing call processors. */
private final OutgoingCallsManager mOutgoingCallsManager;
- private final Switchboard mSwitchboard;
-
private final Runnable mNextCallServiceCallback = new Runnable() {
@Override public void run() {
attemptNextCallService();
@@ -113,21 +110,18 @@
* @param callServices The available call-service implementations.
* @param selectors The available call-service selector implementations.
* @param outgoingCallsManager Manager of all outgoing call processors.
- * @param switchboard The switchboard.
*/
OutgoingCallProcessor(
Call call,
- Set<CallServiceWrapper> callServices,
+ Collection<CallServiceWrapper> callServices,
Collection<CallServiceSelectorWrapper> selectors,
- OutgoingCallsManager outgoingCallsManager,
- Switchboard switchboard) {
+ OutgoingCallsManager outgoingCallsManager) {
ThreadUtil.checkOnMainThread();
mCall = call;
mSelectors = selectors;
mOutgoingCallsManager = outgoingCallsManager;
- mSwitchboard = switchboard;
// Populate the list and map of call-service descriptors. The list is needed since
// it's being passed down to selectors.
@@ -214,7 +208,7 @@
for (CallServiceWrapper callService : mIncompatibleCallServices) {
mCall.addIncompatibleCallService(callService);
}
- mSwitchboard.handleSuccessfulOutgoingCall(mCall);
+ mCall.handleSuccessfulOutgoing();
}
/**
diff --git a/src/com/android/telecomm/OutgoingCallsManager.java b/src/com/android/telecomm/OutgoingCallsManager.java
index 7bbfd04..05bdad0 100644
--- a/src/com/android/telecomm/OutgoingCallsManager.java
+++ b/src/com/android/telecomm/OutgoingCallsManager.java
@@ -30,22 +30,16 @@
* 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
- * to {@link OutgoingCallProcessor} so that other classes (Switchboard, CallServiceAdapter, etc),
+ * to {@link OutgoingCallProcessor} so that other classes (CallServiceAdapter, etc),
* can simply call into this class instead of individual OutgoingCallProcessors.
*/
final class OutgoingCallsManager {
- private final Switchboard mSwitchboard;
/**
* Maps call to {@link OutgoingCallProcessor}s.
*/
private Map<Call, 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})
@@ -53,12 +47,12 @@
* {@link #handleFailedCallAttempt}.
*
* @param call The call to place.
- * @param callServices The set of call services which can potentially place the call.
+ * @param callServices The collection 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<CallServiceWrapper> callServices,
+ Collection<CallServiceWrapper> callServices,
Collection<CallServiceSelectorWrapper> selectors) {
Log.i(this, "Placing an outgoing call: %s", call);
@@ -67,7 +61,7 @@
// attempts can be aborted etc.
// TODO(gilad): Consider passing mSelector as an immutable set.
OutgoingCallProcessor processor =
- new OutgoingCallProcessor(call, callServices, selectors, this, mSwitchboard);
+ new OutgoingCallProcessor(call, callServices, selectors, this);
mOutgoingCallProcessors.put(call, processor);
processor.process();
@@ -92,7 +86,7 @@
/**
* 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
+ * the call. This method is invoked from {@link CallServiceAdapter} after a call service
* has notified Telecomm that it successfully placed the call.
*/
void handleSuccessfulCallAttempt(Call call) {
@@ -129,10 +123,10 @@
/**
* 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
+ * call. 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.
+ * should cleanup and notify the call.
*
* @param call The failed outgoing call.
* @param isAborted True if the call timedout and is aborted.
@@ -140,7 +134,7 @@
void handleFailedOutgoingCall(Call call, boolean isAborted) {
Log.v(this, "handleFailedOutgoingCall, call: %s", call);
mOutgoingCallProcessors.remove(call);
- mSwitchboard.handleFailedOutgoingCall(call, isAborted);
+ call.handleFailedOutgoing(isAborted);
}
/**
@@ -166,9 +160,9 @@
* successful.
*/
boolean abort(Call call) {
- Log.v(this, "abort, call: %s", call);
OutgoingCallProcessor processor = mOutgoingCallProcessors.remove(call);
if (processor != null) {
+ Log.v(this, "abort, call: %s", call);
processor.abort();
return true;
}
diff --git a/src/com/android/telecomm/ServiceBinder.java b/src/com/android/telecomm/ServiceBinder.java
index 3dc86be..ed84bcf 100644
--- a/src/com/android/telecomm/ServiceBinder.java
+++ b/src/com/android/telecomm/ServiceBinder.java
@@ -49,9 +49,8 @@
/**
* Listener for bind events on ServiceBinder.
*/
- interface Listener {
- @SuppressWarnings("rawtypes")
- void onUnbind(ServiceBinder serviceBinder);
+ interface Listener<ServiceBinderClass extends ServiceBinder<?>> {
+ void onUnbind(ServiceBinderClass serviceBinder);
}
/**
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index 81ab172..5b8d25d 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -24,6 +24,7 @@
import android.telecomm.CallServiceDescriptor;
import android.telecomm.TelecommConstants;
+import com.android.telecomm.BaseRepository.LookupCallback;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
@@ -37,9 +38,81 @@
* - gathering the {@link CallServiceWrapper}s and {@link CallServiceSelectorWrapper}s through
* which to place outgoing calls
* - starting outgoing calls (via {@link OutgoingCallsManager}
- * - switching active calls between call services.
*/
final class Switchboard {
+ /**
+ * Encapsulates a request to place an outgoing call.
+ * TODO(santoscordon): Move this state into Call and remove this class.
+ */
+ private final class OutgoingCallEntry {
+ final Call call;
+
+ private Collection<CallServiceWrapper> mCallServices;
+ private Collection<CallServiceSelectorWrapper> mSelectors;
+ private boolean mIsCallPending = true;
+
+ OutgoingCallEntry(Call call) {
+ this.call = call;
+ }
+
+ /**
+ * Sets the call services to attempt for this outgoing call.
+ *
+ * @param callServices The call services.
+ */
+ void setCallServices(Collection<CallServiceWrapper> callServices) {
+ mCallServices = callServices;
+ onLookupComplete();
+ }
+
+ Collection<CallServiceWrapper> getCallServices() {
+ return mCallServices;
+ }
+
+ /**
+ * Sets the selectors to attemnpt for this outgoing call.
+ *
+ * @param selectors The call-service selectors.
+ */
+ void setSelectors(Collection<CallServiceSelectorWrapper> selectors) {
+ mSelectors = selectors;
+ onLookupComplete();
+ }
+
+ Collection<CallServiceSelectorWrapper> getSelectors() {
+ return mSelectors;
+ }
+
+ /** Expires the pending outgoing call and stops it from being made. */
+ void expire() {
+ // This can be executed in three states:
+ // 1) We are still waiting for the list of CSs (Call Services)
+ // 2) We told outgoing calls manager to place the call using the CSs
+ // 3) Outgoing calls manager already successfully placed the call.
+ if (mIsCallPending) {
+ // Handle state (1), tell the call to clean itself up and shut everything down.
+ mIsCallPending = false;
+ call.handleFailedOutgoing(true /* isAborted */);
+ } else {
+ // Handle states (2) & (3). We can safely call abort() in either case. If the call
+ // is not yet successful, then it will abort. If the call was already placed, then
+ // outgoing calls manager will do nothing (and return false which we ignore).
+ boolean isAborted = mOutgoingCallsManager.abort(call);
+ Log.v(this, "expire() caused abort: %b", isAborted);
+ }
+ }
+
+ /** Initiates processing of the call once call-services and selectors are set. */
+ private void onLookupComplete() {
+ if (mIsCallPending) {
+ if (mSelectors != null && mCallServices != null) {
+ mIsCallPending = false;
+ processNewOutgoingCall(this);
+ }
+ }
+ }
+ }
+
private final static int MSG_EXPIRE_STALE_CALL = 1;
private final static Switchboard sInstance = new Switchboard();
@@ -60,7 +133,7 @@
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_EXPIRE_STALE_CALL:
- expireStaleCall((Call) msg.obj);
+ ((OutgoingCallEntry) msg.obj).expire();
break;
default:
Log.wtf(Switchboard.this, "Unexpected message %d.", msg.what);
@@ -68,30 +141,6 @@
}
};
- private final Set<Call> mNewOutgoingCalls = Sets.newLinkedHashSet();
-
- private final Set<Call> mPendingOutgoingCalls = Sets.newLinkedHashSet();
-
- /**
- * The set of currently available call-service implementations, see
- * {@link CallServiceRepository}. Populated during call-service lookup cycles as part of the
- * {@link #placeOutgoingCall} flow and cleared upon zero-remaining new/pending outgoing calls.
- */
- private final Set<CallServiceWrapper> mCallServices = Sets.newHashSet();
-
- /**
- * The set of currently available call-service-selector implementations,
- * see {@link CallServiceSelectorRepository}.
- * TODO(gilad): Clear once the active-call count goes to zero.
- */
- private ImmutableCollection<CallServiceSelectorWrapper> mSelectors = ImmutableList.of();
-
- /**
- * The current lookup-cycle ID used with the repositories. Incremented with each invocation
- * of {@link #placeOutgoingCall} and passed to the repositories via initiateLookup().
- */
- private int mLookupId = 0;
-
/** Singleton accessor. */
static Switchboard getInstance() {
return sInstance;
@@ -103,11 +152,11 @@
private Switchboard() {
ThreadUtil.checkOnMainThread();
- mOutgoingCallsManager = new OutgoingCallsManager(this);
- mIncomingCallsManager = new IncomingCallsManager(this);
- mSelectorRepository = new CallServiceSelectorRepository(this, mOutgoingCallsManager);
+ mOutgoingCallsManager = new OutgoingCallsManager();
+ mIncomingCallsManager = new IncomingCallsManager();
+ mSelectorRepository = new CallServiceSelectorRepository(mOutgoingCallsManager);
mCallServiceRepository =
- new CallServiceRepository(this, mOutgoingCallsManager, mIncomingCallsManager);
+ new CallServiceRepository(mOutgoingCallsManager, mIncomingCallsManager);
}
/**
@@ -118,30 +167,30 @@
* @param call The yet-to-be-connected outgoing-call object.
*/
void placeOutgoingCall(Call call) {
- // 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
- // reset, (5) the selector lookup completes but the call-services are missing. This should
- // be okay since the call-service lookup completed. Specifically the already-available call
- // services are cached and will be provided in response to the second lookup cycle.
- mCallServices.clear();
- mSelectors = ImmutableList.of();
+ final OutgoingCallEntry callEntry = new OutgoingCallEntry(call);
- mNewOutgoingCalls.add(call);
+ // Lookup call services
+ mCallServiceRepository.lookupServices(new LookupCallback<CallServiceWrapper>() {
+ @Override
+ public void onComplete(Collection<CallServiceWrapper> services) {
+ callEntry.setCallServices(services);
+ }
+ });
- // Initiate a lookup every time to account for newly-installed apps and/or updated settings.
- mLookupId++;
- mCallServiceRepository.initiateLookup(mLookupId);
- mSelectorRepository.initiateLookup(mLookupId);
+ // Lookup selectors
+ mSelectorRepository.lookupServices(new LookupCallback<CallServiceSelectorWrapper>() {
+ @Override
+ public void onComplete(Collection<CallServiceSelectorWrapper> selectors) {
+ callEntry.setSelectors(selectors);
+ }
+ });
- Message msg = mHandler.obtainMessage(MSG_EXPIRE_STALE_CALL, call);
+ Message msg = mHandler.obtainMessage(MSG_EXPIRE_STALE_CALL, callEntry);
mHandler.sendMessageDelayed(msg, Timeouts.getNewOutgoingCallMillis());
}
/**
- * Retrieves details about the incoming call through the incoming call manager. The incoming
- * call manager will invoke either {@link #handleSuccessfulIncomingCall} or
- * {@link #handleFailedIncomingCall} depending on the result of the retrieval.
+ * Retrieves details about the incoming call through the incoming call manager.
*
* @param call The call object.
* @param descriptor The relevant call-service descriptor.
@@ -150,96 +199,12 @@
*/
void retrieveIncomingCall(Call call, CallServiceDescriptor descriptor, Bundle extras) {
Log.d(this, "retrieveIncomingCall");
- CallServiceWrapper callService = mCallServiceRepository.getCallService(descriptor);
+ CallServiceWrapper callService = mCallServiceRepository.getService(descriptor);
call.setCallService(callService);
mIncomingCallsManager.retrieveIncomingCall(call, extras);
}
/**
- * Persists the specified set of call services and attempts to place any pending outgoing
- * calls. Intended to be invoked by {@link CallServiceRepository} exclusively.
- *
- * @param callServices The potentially-partial set of call services. Partial since the lookup
- * process is time-boxed, such that some providers/call-services may be slow to respond and
- * hence effectively omitted from the specified list.
- */
- void setCallServices(Set<CallServiceWrapper> callServices) {
- mCallServices.clear();
- mCallServices.addAll(callServices);
- processNewOutgoingCalls();
- }
-
- /**
- * Persists the specified list of selectors and attempts to connect any pending outgoing
- * calls. Intended to be invoked by {@link CallServiceSelectorRepository} exclusively.
- *
- * @param selectors Collection of selectors. The order of the collection determines the order in
- * which the selectors are tried.
- */
- void setSelectors(ImmutableCollection<CallServiceSelectorWrapper> selectors) {
- ThreadUtil.checkOnMainThread();
- Preconditions.checkNotNull(selectors);
-
- // TODO(gilad): Add logic to include the built-in selectors (e.g. for dealing with
- // emergency calls) and order the entire set prior to the assignment below. If the
- // built-in selectors can be implemented in a manner that does not require binding,
- // that's probably preferred.
- mSelectors = selectors;
- processNewOutgoingCalls();
- }
-
- /**
- * Handles the case where an outgoing call has been successfully placed,
- * see {@link OutgoingCallProcessor}.
- */
- void handleSuccessfulOutgoingCall(Call call) {
- Log.d(this, "handleSuccessfulOutgoingCall");
-
- finalizeOutgoingCall(call);
- call.handleSuccessfulOutgoing();
- }
-
- /**
- * Handles the case where an outgoing call could not be proceed by any of the
- * selector/call-service implementations, see {@link OutgoingCallProcessor}.
- */
- void handleFailedOutgoingCall(Call call, boolean isAborted) {
- Log.d(this, "handleFailedOutgoingCall");
-
- finalizeOutgoingCall(call);
- call.handleFailedOutgoing(isAborted);
- }
-
- /**
- * Handles the case where we successfully receive details of an incoming call. Hands the
- * resulting call to {@link CallsManager} as the final step in the incoming sequence. At that
- * point, {@link CallsManager} should bring up the incoming-call UI.
- */
- void handleSuccessfulIncomingCall(Call call, CallInfo callInfo) {
- Log.d(this, "handleSuccessfulIncomingCall");
- call.handleSuccessfulIncoming(callInfo);
- }
-
- /**
- * Handles the case where we failed to retrieve an incoming call after receiving an incoming-call
- * intent via {@link CallActivity}.
- *
- * @param call The call.
- */
- void handleFailedIncomingCall(Call call) {
- Log.d(this, "handleFailedIncomingCall");
-
- // Since we set the call service before calling into incoming-calls manager, we clear it for
- // good measure if an error is reported.
- call.handleFailedIncoming();
-
- // At the moment there is nothing more to do if an incoming call is not retrieved. We may at
- // a future date bind to the in-call app optimistically during the incoming-call sequence
- // and this method could tell {@link CallsManager} to unbind from the in-call app if the
- // incoming call was not retrieved.
- }
-
- /**
* Ensures any state regarding a call is cleaned up.
*
* @param call The call.
@@ -250,38 +215,19 @@
}
/**
- * Attempts to process the next new outgoing calls that have not yet been expired.
- */
- private void processNewOutgoingCalls() {
- if (mCallServices.isEmpty() || mSelectors.isEmpty()) {
- // At least one call service and one selector are required to process outgoing calls.
- return;
- }
-
- if (!mNewOutgoingCalls.isEmpty()) {
- Call call = mNewOutgoingCalls.iterator().next();
- mNewOutgoingCalls.remove(call);
- mPendingOutgoingCalls.add(call);
-
- // Specifically only attempt to place one call at a time such that call services
- // can be freed from needing to deal with concurrent requests.
- processNewOutgoingCall(call);
- }
- }
-
- /**
* Attempts to place the specified call.
*
- * @param call The call to place.
+ * @param callEntry The call entry to place.
*/
- private void processNewOutgoingCall(Call call) {
+ private void processNewOutgoingCall(OutgoingCallEntry callEntry) {
Collection<CallServiceSelectorWrapper> selectors;
+ Call call = callEntry.call;
// Use the call's selector if it's already tied to one. This is the case for handoff calls.
if (call.getCallServiceSelector() != null) {
selectors = ImmutableList.of(call.getCallServiceSelector());
} else {
- selectors = mSelectors;
+ selectors = callEntry.getSelectors();
}
boolean useEmergencySelector =
@@ -308,39 +254,6 @@
selectors = selectorsBuilder.build();
}
- mOutgoingCallsManager.placeCall(call, mCallServices, selectors);
- }
-
- /**
- * Finalizes the outgoing-call sequence, regardless if it succeeded or failed.
- */
- private void finalizeOutgoingCall(Call call) {
- mPendingOutgoingCalls.remove(call);
-
- processNewOutgoingCalls(); // Process additional (new) calls, if any.
- }
-
- /**
- * Expires calls which are taking too long to connect.
- *
- * @param call The call to expire.
- */
- 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);
- }
-
- 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);
- }
- }
+ mOutgoingCallsManager.placeCall(call, callEntry.getCallServices(), selectors);
}
}