blob: 81ab1728dfa912f21a709db6611255c1aba273d1 [file] [log] [blame]
Santos Cordon8e8b8d22013-12-19 14:14:05 -08001/*
2 * Copyright 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
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Santos Cordon5b7b9b32014-03-26 14:00:22 -070019import android.content.ComponentName;
Evan Charltona05805b2014-03-05 08:21:46 -080020import android.os.Bundle;
Ben Gilad2313e622014-02-06 12:02:25 -080021import android.os.Handler;
Santos Cordonc499c1c2014-04-14 17:13:14 -070022import android.os.Message;
Sailesh Nepal810735e2014-03-18 18:15:46 -070023import android.telecomm.CallInfo;
Ben Giladc5b22692014-02-18 20:03:22 -080024import android.telecomm.CallServiceDescriptor;
Evan Charltona05805b2014-03-05 08:21:46 -080025import android.telecomm.TelecommConstants;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080026
Santos Cordon5b7b9b32014-03-26 14:00:22 -070027import com.google.common.base.Preconditions;
28import com.google.common.collect.ImmutableCollection;
29import com.google.common.collect.ImmutableList;
30import com.google.common.collect.Sets;
31
32import java.util.Collection;
Ben Gilad0407fb22014-01-09 16:18:41 -080033import java.util.Set;
Ben Gilad9f2bed32013-12-12 17:43:26 -080034
Santos Cordon8e8b8d22013-12-19 14:14:05 -080035/**
Sailesh Nepal18386a82014-03-19 10:22:40 -070036 * Switchboard is responsible for:
Santos Cordon5b7b9b32014-03-26 14:00:22 -070037 * - gathering the {@link CallServiceWrapper}s and {@link CallServiceSelectorWrapper}s through
38 * which to place outgoing calls
39 * - starting outgoing calls (via {@link OutgoingCallsManager}
40 * - switching active calls between call services.
Santos Cordon8e8b8d22013-12-19 14:14:05 -080041 */
Ben Giladd17443c2014-01-06 11:04:15 -080042final class Switchboard {
Santos Cordonc499c1c2014-04-14 17:13:14 -070043 private final static int MSG_EXPIRE_STALE_CALL = 1;
Ben Gilad2495d572014-01-09 17:26:19 -080044
Santos Cordon766d04f2014-05-06 10:28:25 -070045 private final static Switchboard sInstance = new Switchboard();
Ben Gilad2313e622014-02-06 12:02:25 -080046
Santos Cordon681663d2014-01-30 04:32:15 -080047 /** Used to place outgoing calls. */
48 private final OutgoingCallsManager mOutgoingCallsManager;
49
Santos Cordon493e8f22014-02-19 03:15:12 -080050 /** Used to retrieve incoming call details. */
Santos Cordon80d9bdc2014-02-13 18:28:46 -080051 private final IncomingCallsManager mIncomingCallsManager;
52
Santos Cordonc195e362014-02-11 17:05:31 -080053 private final CallServiceRepository mCallServiceRepository;
Santos Cordon681663d2014-01-30 04:32:15 -080054
Santos Cordonc195e362014-02-11 17:05:31 -080055 private final CallServiceSelectorRepository mSelectorRepository;
Ben Gilad2313e622014-02-06 12:02:25 -080056
57 /** Used to schedule tasks on the main (UI) thread. */
Santos Cordonc499c1c2014-04-14 17:13:14 -070058 private final Handler mHandler = new Handler() {
Ben Gilad2313e622014-02-06 12:02:25 -080059 @Override
Santos Cordonc499c1c2014-04-14 17:13:14 -070060 public void handleMessage(Message msg) {
61 switch(msg.what) {
62 case MSG_EXPIRE_STALE_CALL:
63 expireStaleCall((Call) msg.obj);
64 break;
65 default:
66 Log.wtf(Switchboard.this, "Unexpected message %d.", msg.what);
Ben Gilad2313e622014-02-06 12:02:25 -080067 }
68 }
69 };
70
71 private final Set<Call> mNewOutgoingCalls = Sets.newLinkedHashSet();
72
73 private final Set<Call> mPendingOutgoingCalls = Sets.newLinkedHashSet();
Ben Giladb59769e2014-01-16 11:41:10 -080074
Ben Gilad03292d42014-01-16 15:06:16 -080075 /**
Ben Giladbb167cd2014-02-25 16:24:05 -080076 * The set of currently available call-service implementations, see
Ben Gilad37587232014-02-19 16:03:44 -080077 * {@link CallServiceRepository}. Populated during call-service lookup cycles as part of the
78 * {@link #placeOutgoingCall} flow and cleared upon zero-remaining new/pending outgoing calls.
Ben Gilad03292d42014-01-16 15:06:16 -080079 */
Evan Charltonf02e9882014-03-06 12:54:52 -080080 private final Set<CallServiceWrapper> mCallServices = Sets.newHashSet();
Ben Giladb59769e2014-01-16 11:41:10 -080081
Ben Gilad03292d42014-01-16 15:06:16 -080082 /**
83 * The set of currently available call-service-selector implementations,
Santos Cordonc195e362014-02-11 17:05:31 -080084 * see {@link CallServiceSelectorRepository}.
Evan Charltonf02e9882014-03-06 12:54:52 -080085 * TODO(gilad): Clear once the active-call count goes to zero.
Ben Gilad03292d42014-01-16 15:06:16 -080086 */
Sailesh Nepal18386a82014-03-19 10:22:40 -070087 private ImmutableCollection<CallServiceSelectorWrapper> mSelectors = ImmutableList.of();
Ben Gilad0407fb22014-01-09 16:18:41 -080088
Santos Cordon681663d2014-01-30 04:32:15 -080089 /**
Santos Cordonc195e362014-02-11 17:05:31 -080090 * The current lookup-cycle ID used with the repositories. Incremented with each invocation
Ben Gilad37587232014-02-19 16:03:44 -080091 * of {@link #placeOutgoingCall} and passed to the repositories via initiateLookup().
Santos Cordonc195e362014-02-11 17:05:31 -080092 */
93 private int mLookupId = 0;
94
Santos Cordon766d04f2014-05-06 10:28:25 -070095 /** Singleton accessor. */
96 static Switchboard getInstance() {
97 return sInstance;
98 }
99
Santos Cordonc195e362014-02-11 17:05:31 -0800100 /**
Santos Cordon681663d2014-01-30 04:32:15 -0800101 * Persists the specified parameters and initializes Switchboard.
102 */
Santos Cordon766d04f2014-05-06 10:28:25 -0700103 private Switchboard() {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700104 ThreadUtil.checkOnMainThread();
105
Santos Cordon681663d2014-01-30 04:32:15 -0800106 mOutgoingCallsManager = new OutgoingCallsManager(this);
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800107 mIncomingCallsManager = new IncomingCallsManager(this);
Sailesh Nepal6ab6fb72014-04-01 20:03:19 -0700108 mSelectorRepository = new CallServiceSelectorRepository(this, mOutgoingCallsManager);
Santos Cordon493e8f22014-02-19 03:15:12 -0800109 mCallServiceRepository =
110 new CallServiceRepository(this, mOutgoingCallsManager, mIncomingCallsManager);
Santos Cordon681663d2014-01-30 04:32:15 -0800111 }
Ben Giladd17443c2014-01-06 11:04:15 -0800112
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800113 /**
Santos Cordonc195e362014-02-11 17:05:31 -0800114 * Starts the process of placing an outgoing call by searching for available call services
115 * through which the call can be placed. After a lookup for those services completes, execution
116 * returns to {@link #setCallServices} where the process of placing the call continues.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800117 *
Ben Gilad13329fd2014-02-11 17:20:29 -0800118 * @param call The yet-to-be-connected outgoing-call object.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800119 */
Santos Cordonc195e362014-02-11 17:05:31 -0800120 void placeOutgoingCall(Call call) {
Ben Giladbb167cd2014-02-25 16:24:05 -0800121 // Reset prior to initiating the next lookup. One case to consider is (1) placeOutgoingCall
122 // is invoked with call A, (2) the call-service lookup completes, but the one for selectors
123 // does not, (3) placeOutgoingCall is invoked again with call B, (4) mCallServices below is
124 // reset, (5) the selector lookup completes but the call-services are missing. This should
125 // be okay since the call-service lookup completed. Specifically the already-available call
126 // services are cached and will be provided in response to the second lookup cycle.
Evan Charltonf02e9882014-03-06 12:54:52 -0800127 mCallServices.clear();
Sailesh Nepal18386a82014-03-19 10:22:40 -0700128 mSelectors = ImmutableList.of();
Ben Giladbb167cd2014-02-25 16:24:05 -0800129
Sailesh Nepalb6141ae2014-02-18 08:45:26 -0800130 mNewOutgoingCalls.add(call);
Ben Giladb59769e2014-01-16 11:41:10 -0800131
Ben Giladebd9b662014-02-19 16:03:44 -0800132 // Initiate a lookup every time to account for newly-installed apps and/or updated settings.
Sailesh Nepalb6141ae2014-02-18 08:45:26 -0800133 mLookupId++;
Santos Cordonc195e362014-02-11 17:05:31 -0800134 mCallServiceRepository.initiateLookup(mLookupId);
135 mSelectorRepository.initiateLookup(mLookupId);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700136
137 Message msg = mHandler.obtainMessage(MSG_EXPIRE_STALE_CALL, call);
138 mHandler.sendMessageDelayed(msg, Timeouts.getNewOutgoingCallMillis());
Ben Gilad0407fb22014-01-09 16:18:41 -0800139 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800140
Ben Gilad0407fb22014-01-09 16:18:41 -0800141 /**
Santos Cordon493e8f22014-02-19 03:15:12 -0800142 * Retrieves details about the incoming call through the incoming call manager. The incoming
143 * call manager will invoke either {@link #handleSuccessfulIncomingCall} or
144 * {@link #handleFailedIncomingCall} depending on the result of the retrieval.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800145 *
146 * @param call The call object.
Ben Giladc5b22692014-02-18 20:03:22 -0800147 * @param descriptor The relevant call-service descriptor.
Evan Charltona05805b2014-03-05 08:21:46 -0800148 * @param extras The optional extras passed via
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700149 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800150 */
Evan Charltona05805b2014-03-05 08:21:46 -0800151 void retrieveIncomingCall(Call call, CallServiceDescriptor descriptor, Bundle extras) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800152 Log.d(this, "retrieveIncomingCall");
Ben Giladc5b22692014-02-18 20:03:22 -0800153 CallServiceWrapper callService = mCallServiceRepository.getCallService(descriptor);
Santos Cordon493e8f22014-02-19 03:15:12 -0800154 call.setCallService(callService);
Evan Charltona05805b2014-03-05 08:21:46 -0800155 mIncomingCallsManager.retrieveIncomingCall(call, extras);
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800156 }
157
158 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800159 * Persists the specified set of call services and attempts to place any pending outgoing
Santos Cordonc195e362014-02-11 17:05:31 -0800160 * calls. Intended to be invoked by {@link CallServiceRepository} exclusively.
Ben Gilad0407fb22014-01-09 16:18:41 -0800161 *
Ben Gilad03292d42014-01-16 15:06:16 -0800162 * @param callServices The potentially-partial set of call services. Partial since the lookup
163 * process is time-boxed, such that some providers/call-services may be slow to respond and
164 * hence effectively omitted from the specified list.
Ben Gilad0407fb22014-01-09 16:18:41 -0800165 */
Santos Cordonc195e362014-02-11 17:05:31 -0800166 void setCallServices(Set<CallServiceWrapper> callServices) {
Evan Charltonf02e9882014-03-06 12:54:52 -0800167 mCallServices.clear();
168 mCallServices.addAll(callServices);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800169 processNewOutgoingCalls();
Ben Giladb59769e2014-01-16 11:41:10 -0800170 }
171
172 /**
173 * Persists the specified list of selectors and attempts to connect any pending outgoing
Santos Cordonc195e362014-02-11 17:05:31 -0800174 * calls. Intended to be invoked by {@link CallServiceSelectorRepository} exclusively.
Ben Giladb59769e2014-01-16 11:41:10 -0800175 *
Sailesh Nepal18386a82014-03-19 10:22:40 -0700176 * @param selectors Collection of selectors. The order of the collection determines the order in
177 * which the selectors are tried.
Ben Giladb59769e2014-01-16 11:41:10 -0800178 */
Sailesh Nepal18386a82014-03-19 10:22:40 -0700179 void setSelectors(ImmutableCollection<CallServiceSelectorWrapper> selectors) {
Ben Giladb59769e2014-01-16 11:41:10 -0800180 ThreadUtil.checkOnMainThread();
Sailesh Nepal18386a82014-03-19 10:22:40 -0700181 Preconditions.checkNotNull(selectors);
Ben Giladb59769e2014-01-16 11:41:10 -0800182
Ben Gilad134cf092014-01-16 18:26:12 -0800183 // TODO(gilad): Add logic to include the built-in selectors (e.g. for dealing with
184 // emergency calls) and order the entire set prior to the assignment below. If the
185 // built-in selectors can be implemented in a manner that does not require binding,
Sailesh Nepal18386a82014-03-19 10:22:40 -0700186 // that's probably preferred.
187 mSelectors = selectors;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800188 processNewOutgoingCalls();
Ben Giladb59769e2014-01-16 11:41:10 -0800189 }
190
191 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800192 * Handles the case where an outgoing call has been successfully placed,
193 * see {@link OutgoingCallProcessor}.
Ben Giladb59769e2014-01-16 11:41:10 -0800194 */
Ben Gilad0bf5b912014-01-28 17:55:57 -0800195 void handleSuccessfulOutgoingCall(Call call) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800196 Log.d(this, "handleSuccessfulOutgoingCall");
Ben Gilad0bf5b912014-01-28 17:55:57 -0800197
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800198 finalizeOutgoingCall(call);
Santos Cordon766d04f2014-05-06 10:28:25 -0700199 call.handleSuccessfulOutgoing();
Ben Gilad0bf5b912014-01-28 17:55:57 -0800200 }
201
202 /**
203 * Handles the case where an outgoing call could not be proceed by any of the
204 * selector/call-service implementations, see {@link OutgoingCallProcessor}.
205 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700206 void handleFailedOutgoingCall(Call call, boolean isAborted) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800207 Log.d(this, "handleFailedOutgoingCall");
Ben Gilad0bf5b912014-01-28 17:55:57 -0800208
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800209 finalizeOutgoingCall(call);
Santos Cordon766d04f2014-05-06 10:28:25 -0700210 call.handleFailedOutgoing(isAborted);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800211 }
212
213 /**
Santos Cordon493e8f22014-02-19 03:15:12 -0800214 * Handles the case where we successfully receive details of an incoming call. Hands the
215 * resulting call to {@link CallsManager} as the final step in the incoming sequence. At that
216 * point, {@link CallsManager} should bring up the incoming-call UI.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800217 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700218 void handleSuccessfulIncomingCall(Call call, CallInfo callInfo) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800219 Log.d(this, "handleSuccessfulIncomingCall");
Santos Cordon766d04f2014-05-06 10:28:25 -0700220 call.handleSuccessfulIncoming(callInfo);
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800221 }
222
223 /**
Santos Cordon493e8f22014-02-19 03:15:12 -0800224 * Handles the case where we failed to retrieve an incoming call after receiving an incoming-call
225 * intent via {@link CallActivity}.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800226 *
227 * @param call The call.
228 */
229 void handleFailedIncomingCall(Call call) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800230 Log.d(this, "handleFailedIncomingCall");
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800231
Santos Cordon493e8f22014-02-19 03:15:12 -0800232 // Since we set the call service before calling into incoming-calls manager, we clear it for
233 // good measure if an error is reported.
Santos Cordon766d04f2014-05-06 10:28:25 -0700234 call.handleFailedIncoming();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700235
Santos Cordon493e8f22014-02-19 03:15:12 -0800236 // At the moment there is nothing more to do if an incoming call is not retrieved. We may at
237 // a future date bind to the in-call app optimistically during the incoming-call sequence
238 // and this method could tell {@link CallsManager} to unbind from the in-call app if the
239 // incoming call was not retrieved.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800240 }
241
242 /**
Ihab Awad4e0f1922014-04-14 17:35:38 -0700243 * Ensures any state regarding a call is cleaned up.
244 *
245 * @param call The call.
246 */
247 void abortCall(Call call) {
248 Log.d(this, "abortCall");
249 mOutgoingCallsManager.abort(call);
250 }
251
252 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800253 * Attempts to process the next new outgoing calls that have not yet been expired.
254 */
255 private void processNewOutgoingCalls() {
Evan Charltonf02e9882014-03-06 12:54:52 -0800256 if (mCallServices.isEmpty() || mSelectors.isEmpty()) {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800257 // At least one call service and one selector are required to process outgoing calls.
258 return;
259 }
260
261 if (!mNewOutgoingCalls.isEmpty()) {
262 Call call = mNewOutgoingCalls.iterator().next();
263 mNewOutgoingCalls.remove(call);
264 mPendingOutgoingCalls.add(call);
265
266 // Specifically only attempt to place one call at a time such that call services
267 // can be freed from needing to deal with concurrent requests.
268 processNewOutgoingCall(call);
Ben Gilad0407fb22014-01-09 16:18:41 -0800269 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800270 }
271
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800272 /**
Ben Giladb59769e2014-01-16 11:41:10 -0800273 * Attempts to place the specified call.
274 *
Ben Gilad0bf5b912014-01-28 17:55:57 -0800275 * @param call The call to place.
Ben Giladb59769e2014-01-16 11:41:10 -0800276 */
Ben Gilad0bf5b912014-01-28 17:55:57 -0800277 private void processNewOutgoingCall(Call call) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700278 Collection<CallServiceSelectorWrapper> selectors;
279
280 // Use the call's selector if it's already tied to one. This is the case for handoff calls.
281 if (call.getCallServiceSelector() != null) {
282 selectors = ImmutableList.of(call.getCallServiceSelector());
283 } else {
284 selectors = mSelectors;
285 }
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700286
287 boolean useEmergencySelector =
288 EmergencyCallServiceSelector.shouldUseSelector(call.getHandle());
289 Log.d(this, "processNewOutgoingCall, isEmergency=%b", useEmergencySelector);
290
291 if (useEmergencySelector) {
292 // This is potentially an emergency call so add the emergency selector before the
293 // other selectors.
294 ImmutableList.Builder<CallServiceSelectorWrapper> selectorsBuilder =
295 ImmutableList.builder();
296
297 ComponentName componentName = new ComponentName(
298 TelecommApp.getInstance(), EmergencyCallServiceSelector.class);
299 CallServiceSelectorWrapper emergencySelector =
300 new CallServiceSelectorWrapper(
301 componentName.flattenToShortString(),
302 componentName,
Santos Cordon766d04f2014-05-06 10:28:25 -0700303 CallsManager.getInstance(),
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700304 mOutgoingCallsManager);
305
306 selectorsBuilder.add(emergencySelector);
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700307 selectorsBuilder.addAll(selectors);
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700308 selectors = selectorsBuilder.build();
309 }
310
311 mOutgoingCallsManager.placeCall(call, mCallServices, selectors);
Ben Giladb59769e2014-01-16 11:41:10 -0800312 }
313
314 /**
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800315 * Finalizes the outgoing-call sequence, regardless if it succeeded or failed.
316 */
317 private void finalizeOutgoingCall(Call call) {
318 mPendingOutgoingCalls.remove(call);
319
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800320 processNewOutgoingCalls(); // Process additional (new) calls, if any.
321 }
322
323 /**
Santos Cordonc499c1c2014-04-14 17:13:14 -0700324 * Expires calls which are taking too long to connect.
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800325 *
Santos Cordonc499c1c2014-04-14 17:13:14 -0700326 * @param call The call to expire.
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800327 */
Santos Cordonc499c1c2014-04-14 17:13:14 -0700328 private void expireStaleCall(Call call) {
329 final long newCallTimeoutMillis = Timeouts.getNewOutgoingCallMillis();
330
331 if (call.getAgeMillis() < newCallTimeoutMillis) {
332 Log.wtf(this, "Expiring a call early. Age: %d, Time since attempt: %d",
333 call.getAgeMillis(), newCallTimeoutMillis);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800334 }
335
Santos Cordonc499c1c2014-04-14 17:13:14 -0700336 if (mNewOutgoingCalls.remove(call)) {
337 // The call had not yet been processed so all we have to do is report the
338 // failure.
339 handleFailedOutgoingCall(call, true /* isAborted */);
340 } else if (mPendingOutgoingCalls.remove(call)) {
341 if (!mOutgoingCallsManager.abort(call)) {
342 Log.wtf(this, "Pending call failed to abort, call: %s.", call);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800343 }
344 }
345 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800346}