blob: d24c612b6ac7a4d283bdbdf773f3656bef039c18 [file] [log] [blame]
Santos Cordon8e8b8d22013-12-19 14:14:05 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.telecomm;
18
19import android.content.ComponentName;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080020import android.content.Intent;
21import android.content.pm.PackageManager;
22import android.content.pm.ResolveInfo;
23import android.content.pm.ServiceInfo;
Evan Charlton0958f532014-01-10 16:58:02 -080024import android.os.Handler;
Ben Giladd17443c2014-01-06 11:04:15 -080025import android.os.IBinder;
Evan Charlton0958f532014-01-10 16:58:02 -080026import android.os.Looper;
Santos Cordoncb83fb62014-01-06 10:57:57 -080027import android.os.RemoteException;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080028import android.telecomm.ICallService;
Santos Cordoncb83fb62014-01-06 10:57:57 -080029import android.telecomm.ICallServiceLookupResponse;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080030import android.telecomm.ICallServiceProvider;
31import android.util.Log;
32
Ben Giladd17443c2014-01-06 11:04:15 -080033import com.google.common.base.Preconditions;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080034import com.google.common.collect.Lists;
Santos Cordon63aeb162014-02-10 09:20:40 -080035import com.google.common.collect.Maps;
Ben Giladd17443c2014-01-06 11:04:15 -080036import com.google.common.collect.Sets;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080037
38import java.util.List;
Santos Cordon63aeb162014-02-10 09:20:40 -080039import java.util.Map;
Ben Giladd17443c2014-01-06 11:04:15 -080040import java.util.Set;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080041
42/**
43 * Finds {@link ICallService} and {@link ICallServiceProvider} implementations on the device.
44 * Uses binder APIs to find ICallServiceProviders and calls method on ICallServiceProvider to
45 * find ICallService implementations.
46 * TODO(santoscordon): Add performance timing to async calls.
47 */
48final class CallServiceFinder {
Ben Giladd17443c2014-01-06 11:04:15 -080049
Ben Giladd17443c2014-01-06 11:04:15 -080050 private static final String TAG = CallServiceFinder.class.getSimpleName();
Santos Cordon8e8b8d22013-12-19 14:14:05 -080051
52 /**
Evan Charlton0958f532014-01-10 16:58:02 -080053 * The longest period in milliseconds each lookup cycle is allowed to span over, see
54 * {@link #mLookupTerminator}.
Ben Giladd17443c2014-01-06 11:04:15 -080055 * TODO(gilad): Likely requires tuning.
Santos Cordon8e8b8d22013-12-19 14:14:05 -080056 */
Evan Charlton0958f532014-01-10 16:58:02 -080057 private static final int LOOKUP_TIMEOUT_MS = 100;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080058
Ben Gilad0407fb22014-01-09 16:18:41 -080059 private final Switchboard mSwitchboard;
60
Santos Cordon681663d2014-01-30 04:32:15 -080061 private final OutgoingCallsManager mOutgoingCallsManager;
62
Ben Giladd17443c2014-01-06 11:04:15 -080063 /**
64 * Determines whether or not a lookup cycle is already running.
65 */
66 private boolean mIsLookupInProgress = false;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080067
Ben Giladd17443c2014-01-06 11:04:15 -080068 /**
Santos Cordon63aeb162014-02-10 09:20:40 -080069 * The current lookup-cycle ID. Incremented upon initiateLookup calls.
Ben Giladb59769e2014-01-16 11:41:10 -080070 * TODO(gilad): If at all useful, consider porting the cycle ID concept to switchboard and
71 * have it centralized/shared between the two finders.
Ben Giladd17443c2014-01-06 11:04:15 -080072 */
Santos Cordon63aeb162014-02-10 09:20:40 -080073 private int mLookupId = 0;
Ben Giladd17443c2014-01-06 11:04:15 -080074
75 /**
Ben Gilad03292d42014-01-16 15:06:16 -080076 * The set of bound call-services. Only populated via initiateLookup scenarios. Entries should
77 * only be removed upon unbinding.
78 * TODO(gilad): Add the necessary logic to keep this set up to date.
79 */
80 private Set<ICallService> mCallServiceRegistry = Sets.newHashSet();
81
82 /**
Ben Giladd17443c2014-01-06 11:04:15 -080083 * The set of bound call-service providers. Only populated via initiateLookup scenarios.
84 * Providers should only be removed upon unbinding.
Santos Cordon63aeb162014-02-10 09:20:40 -080085 * TODO(santoscordon): This can be removed once this class starts using CallServiceWrapper
86 * since we'll be able to unbind the providers within registerProvider().
Ben Giladd17443c2014-01-06 11:04:15 -080087 */
Santos Cordon63aeb162014-02-10 09:20:40 -080088 private Set<CallServiceProviderWrapper> mProviderRegistry = Sets.newHashSet();
89
90 /**
91 * Map of {@link CallServiceProviderWrapper}s keyed by their ComponentName. Used as a long-lived
92 * cache in order to simplify management of service-wrapper construction/destruction.
93 */
94 private Map<ComponentName, CallServiceProviderWrapper> mProviderCache = Maps.newHashMap();
Ben Giladd17443c2014-01-06 11:04:15 -080095
96 /**
97 * Stores the names of the providers to bind to in one lookup cycle. The set size represents
98 * the number of call-service providers this finder expects to hear back from upon initiating
99 * call-service lookups, see initiateLookup. Whenever all providers respond before the lookup
100 * timeout occurs, the complete set of (available) call services is passed to the switchboard
Santos Cordon63aeb162014-02-10 09:20:40 -0800101 * for further processing of outgoing calls etc. When the timeout occurs before all responses
Ben Giladd17443c2014-01-06 11:04:15 -0800102 * are received, the partial (potentially empty) set gets passed (to the switchboard) instead.
Santos Cordon63aeb162014-02-10 09:20:40 -0800103 * Entries are removed from this set as providers register.
Ben Giladd17443c2014-01-06 11:04:15 -0800104 */
105 private Set<ComponentName> mUnregisteredProviders;
106
107 /**
108 * Used to interrupt lookup cycles that didn't terminate naturally within the allowed
109 * period, see LOOKUP_TIMEOUT.
110 */
Evan Charlton0958f532014-01-10 16:58:02 -0800111 private final Runnable mLookupTerminator = new Runnable() {
112 @Override
113 public void run() {
114 terminateLookup();
115 }
116 };
117
118 /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
119 private final Handler mHandler = new Handler(Looper.getMainLooper());
Ben Giladd17443c2014-01-06 11:04:15 -0800120
121 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800122 * Persists the specified parameters.
123 *
Santos Cordon681663d2014-01-30 04:32:15 -0800124 * @param switchboard The switchboard for this finder to work against.
125 * @param outgoingCallsManager Manager in charge of placing outgoing calls.
Ben Gilad0407fb22014-01-09 16:18:41 -0800126 */
Santos Cordon681663d2014-01-30 04:32:15 -0800127 CallServiceFinder(Switchboard switchboard, OutgoingCallsManager outgoingCallsManager) {
Ben Gilad0407fb22014-01-09 16:18:41 -0800128 mSwitchboard = switchboard;
Santos Cordon681663d2014-01-30 04:32:15 -0800129 mOutgoingCallsManager = outgoingCallsManager;
Ben Gilad0407fb22014-01-09 16:18:41 -0800130 }
131
132 /**
Evan Charlton0958f532014-01-10 16:58:02 -0800133 * Initiates a lookup cycle for call-service providers. Must be called from the UI thread.
Ben Giladd17443c2014-01-06 11:04:15 -0800134 * TODO(gilad): Expand this comment to describe the lookup flow in more detail.
Ben Giladd17443c2014-01-06 11:04:15 -0800135 */
Santos Cordon63aeb162014-02-10 09:20:40 -0800136 void initiateLookup() {
Evan Charlton0958f532014-01-10 16:58:02 -0800137 ThreadUtil.checkOnMainThread();
Ben Giladd17443c2014-01-06 11:04:15 -0800138 if (mIsLookupInProgress) {
139 // At most one active lookup is allowed at any given time, bail out.
140 return;
141 }
142
Santos Cordon63aeb162014-02-10 09:20:40 -0800143 List<ComponentName> providerNames = getProviderNames();
Ben Giladd17443c2014-01-06 11:04:15 -0800144 if (providerNames.isEmpty()) {
145 Log.i(TAG, "No ICallServiceProvider implementations found.");
146 updateSwitchboard();
147 return;
148 }
149
Santos Cordon63aeb162014-02-10 09:20:40 -0800150 mLookupId++;
Ben Giladd17443c2014-01-06 11:04:15 -0800151 mIsLookupInProgress = true;
152 mUnregisteredProviders = Sets.newHashSet();
153
Ben Giladd17443c2014-01-06 11:04:15 -0800154 for (ComponentName name : providerNames) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800155 // Bind to each of the providers that were found. Some of the providers may already be
156 // bound, and in those cases the provider wrapper will still invoke registerProvider()
157 // allowing us to treat bound and unbound providers the same.
158 getProvider(name).bind();
159 mUnregisteredProviders.add(name);
Ben Giladd17443c2014-01-06 11:04:15 -0800160 }
161
162 int providerCount = providerNames.size();
163 int unregisteredProviderCount = mUnregisteredProviders.size();
164
Ben Giladb59769e2014-01-16 11:41:10 -0800165 Log.i(TAG, "Found " + providerCount + " implementations of ICallServiceProvider, "
Ben Giladd17443c2014-01-06 11:04:15 -0800166 + unregisteredProviderCount + " of which are not currently registered.");
167
168 if (unregisteredProviderCount == 0) {
169 // All known (provider) implementations are already registered, pass control
170 // back to the switchboard.
171 updateSwitchboard();
172 } else {
Evan Charlton0958f532014-01-10 16:58:02 -0800173 // Schedule a lookup terminator to run after LOOKUP_TIMEOUT_MS milliseconds.
174 mHandler.removeCallbacks(mLookupTerminator);
175 mHandler.postDelayed(mLookupTerminator, LOOKUP_TIMEOUT_MS);
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800176 }
177 }
178
179 /**
Ben Giladd17443c2014-01-06 11:04:15 -0800180 * Returns the all-inclusive list of call-service-provider names.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800181 *
Ben Giladd17443c2014-01-06 11:04:15 -0800182 * @return The list containing the (component) names of all known ICallServiceProvider
183 * implementations or the empty list upon no available providers.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800184 */
Santos Cordon63aeb162014-02-10 09:20:40 -0800185 private List<ComponentName> getProviderNames() {
Ben Giladd17443c2014-01-06 11:04:15 -0800186 // The list of provider names to return to the caller, may be populated below.
187 List<ComponentName> providerNames = Lists.newArrayList();
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800188
Santos Cordon63aeb162014-02-10 09:20:40 -0800189 PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
190 Intent intent = new Intent(CallServiceProviderWrapper.CALL_SERVICE_PROVIDER_ACTION);
Ben Giladd17443c2014-01-06 11:04:15 -0800191 for (ResolveInfo entry : packageManager.queryIntentServices(intent, 0)) {
192 ServiceInfo serviceInfo = entry.serviceInfo;
193 if (serviceInfo != null) {
194 // The entry resolves to a proper service, add it to the list of provider names.
195 providerNames.add(
196 new ComponentName(serviceInfo.packageName, serviceInfo.name));
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800197 }
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800198 }
199
Ben Giladd17443c2014-01-06 11:04:15 -0800200 return providerNames;
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800201 }
202
203 /**
Santos Cordoncb83fb62014-01-06 10:57:57 -0800204 * Queries the supplied provider asynchronously for its CallServices and passes the list through
205 * to {@link #registerCallServices} which will relinquish control back to switchboard.
Ben Giladd17443c2014-01-06 11:04:15 -0800206 *
Ben Giladd17443c2014-01-06 11:04:15 -0800207 * @param providerName The component name of the relevant provider.
208 * @param provider The provider object to register.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800209 */
Santos Cordon63aeb162014-02-10 09:20:40 -0800210 void registerProvider(
211 final ComponentName providerName, final CallServiceProviderWrapper provider) {
Ben Giladd17443c2014-01-06 11:04:15 -0800212
Santos Cordoncb83fb62014-01-06 10:57:57 -0800213 // Query the provider for {@link ICallService} implementations.
Santos Cordon63aeb162014-02-10 09:20:40 -0800214 provider.lookupCallServices(new ICallServiceLookupResponse.Stub() {
215 @Override
216 public void setCallServices(final List<IBinder> binderList) {
217 // TODO(santoscordon): Do we need Binder.clear/restoreCallingIdentity()?
218 mHandler.post(new Runnable() {
219 @Override public void run() {
220 Set<ICallService> callServices = Sets.newHashSet();
221 for (IBinder binder : binderList) {
222 callServices.add(ICallService.Stub.asInterface(binder));
Evan Charlton18cc42f2014-01-28 14:44:11 -0800223 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800224 registerCallServices(providerName, provider, callServices);
225 }
226 });
227 }
228 });
Santos Cordoncb83fb62014-01-06 10:57:57 -0800229 }
230
231 /**
232 * Registers the {@link CallService}s for the specified provider and performs the necessary
233 * bookkeeping to potentially return control to the switchboard before the timeout for the
234 * current lookup cycle.
Santos Cordoncb83fb62014-01-06 10:57:57 -0800235 *
Santos Cordoncb83fb62014-01-06 10:57:57 -0800236 * @param providerName The component name of the relevant provider.
237 * @param provider The provider associated with callServices.
238 * @param callServices The {@link CallService}s to register.
239 */
Evan Charlton18cc42f2014-01-28 14:44:11 -0800240 private void registerCallServices(
Santos Cordoncb83fb62014-01-06 10:57:57 -0800241 ComponentName providerName,
Santos Cordon63aeb162014-02-10 09:20:40 -0800242 CallServiceProviderWrapper provider,
Evan Charlton18cc42f2014-01-28 14:44:11 -0800243 Set<ICallService> callServices) {
244 ThreadUtil.checkOnMainThread();
Santos Cordoncb83fb62014-01-06 10:57:57 -0800245
Santos Cordoncb83fb62014-01-06 10:57:57 -0800246 if (mUnregisteredProviders.remove(providerName)) {
247 mProviderRegistry.add(provider);
Santos Cordon63aeb162014-02-10 09:20:40 -0800248
249 // Add all the call services from this provider to the call-service registry.
Santos Cordon681663d2014-01-30 04:32:15 -0800250 for (ICallService callService : callServices) {
251 try {
252 CallServiceAdapter adapter = new CallServiceAdapter(mOutgoingCallsManager);
253 callService.setCallServiceAdapter(adapter);
254 mCallServiceRegistry.add(callService);
255 } catch (RemoteException e) {
256 Log.e(TAG, "Failed to set call-service adapter.");
257 }
258 }
Ben Gilad03292d42014-01-16 15:06:16 -0800259
Santos Cordoncb83fb62014-01-06 10:57:57 -0800260 if (mUnregisteredProviders.size() < 1) {
261 terminateLookup(); // No other providers to wait for.
262 }
263 } else {
Santos Cordon63aeb162014-02-10 09:20:40 -0800264 Log.i(TAG, "Unexpected list of call services in lookup " + mLookupId + " from " +
265 providerName);
Santos Cordoncb83fb62014-01-06 10:57:57 -0800266 }
Ben Giladd17443c2014-01-06 11:04:15 -0800267 }
268
269 /**
270 * Unregisters the specified provider.
271 *
272 * @param providerName The component name of the relevant provider.
273 */
Santos Cordon63aeb162014-02-10 09:20:40 -0800274 void unregisterProvider(ComponentName providerName) {
Evan Charlton18cc42f2014-01-28 14:44:11 -0800275 ThreadUtil.checkOnMainThread();
Ben Giladd17443c2014-01-06 11:04:15 -0800276 mProviderRegistry.remove(providerName);
277 }
278
279 /**
280 * Timeouts the current lookup cycle, see LookupTerminator.
281 */
282 private void terminateLookup() {
Evan Charlton0958f532014-01-10 16:58:02 -0800283 mHandler.removeCallbacks(mLookupTerminator);
Santos Cordon63aeb162014-02-10 09:20:40 -0800284 mUnregisteredProviders.clear();
Ben Giladd17443c2014-01-06 11:04:15 -0800285
286 updateSwitchboard();
287 mIsLookupInProgress = false;
288 }
289
290 /**
291 * Updates the switchboard passing the relevant call services (as opposed
292 * to call-service providers).
293 */
294 private void updateSwitchboard() {
Evan Charlton0958f532014-01-10 16:58:02 -0800295 ThreadUtil.checkOnMainThread();
Ben Gilad03292d42014-01-16 15:06:16 -0800296 mSwitchboard.setCallServices(mCallServiceRegistry);
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800297 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800298
299 /**
300 * Returns the call-service provider wrapper for the specified componentName. Creates a new one
301 * if none is found in the cache.
302 *
303 * @param ComponentName The component name of the provider.
304 */
305 private CallServiceProviderWrapper getProvider(ComponentName componentName) {
306 Preconditions.checkNotNull(componentName);
307
308 CallServiceProviderWrapper provider = mProviderCache.get(componentName);
309 if (provider == null) {
310 provider = new CallServiceProviderWrapper(componentName, this);
311 mProviderCache.put(componentName, provider);
312 }
313
314 return provider;
315 }
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800316}