blob: a242866b30ce539c7b25c97312700bba6e991a5b [file] [log] [blame]
Ben Gilad0bf5b912014-01-28 17:55:57 -08001/*
2 * Copyright 2014, 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
Ben Giladd88323b2014-01-29 19:03:19 -080019import android.os.Handler;
20import android.os.Looper;
Ben Gilad0bf5b912014-01-28 17:55:57 -080021import android.os.RemoteException;
Santos Cordon681663d2014-01-30 04:32:15 -080022import android.telecomm.CallState;
Ben Giladc5b22692014-02-18 20:03:22 -080023import android.telecomm.CallServiceDescriptor;
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080024
Sailesh Nepala439e1b2014-03-11 18:19:58 -070025import com.android.internal.telecomm.ICallServiceSelectionResponse;
26import com.android.internal.telecomm.ICallServiceSelector;
Ben Gilad61925612014-03-11 19:06:36 -070027import com.google.android.collect.Sets;
28import com.google.common.collect.Maps;
29import com.google.common.collect.Lists;
Ben Gilad0bf5b912014-01-28 17:55:57 -080030
31import java.util.Iterator;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35
36/**
37 * Utility class to place a call using the specified set of call-services and ordered selectors.
Ben Giladc5b22692014-02-18 20:03:22 -080038 * Iterates through the selectors and gets a sorted list of supported call-service descriptors
39 * for each selector. Upon receiving each sorted list (one list per selector), each of the
40 * corresponding call services is then attempted until either the outgoing call is placed, the
41 * attempted call is aborted (by the switchboard), or the list is exhausted -- whichever occurs
42 * first.
Ben Gilad0bf5b912014-01-28 17:55:57 -080043 *
Santos Cordon681663d2014-01-30 04:32:15 -080044 * Except for the abort case, all other scenarios should terminate with the switchboard notified
45 * of the result.
Ben Giladd88323b2014-01-29 19:03:19 -080046 *
47 * NOTE(gilad): Currently operating under the assumption that we'll have one timeout per (outgoing)
48 * call attempt. If we (also) like to timeout individual selectors and/or call services, the code
49 * here will need to be re-factored (quite a bit) to support that.
Ben Gilad0bf5b912014-01-28 17:55:57 -080050 */
51final class OutgoingCallProcessor {
52
53 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -080054 * The outgoing call this processor is tasked with placing.
55 */
56 private final Call mCall;
57
58 /**
Ben Giladc5b22692014-02-18 20:03:22 -080059 * The duplicate-free list of currently-available call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -080060 */
Ben Giladc5b22692014-02-18 20:03:22 -080061 private final List<CallServiceDescriptor> mCallServiceDescriptors = Lists.newArrayList();
Ben Gilad0bf5b912014-01-28 17:55:57 -080062
63 /**
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080064 * The map of currently-available call-service implementations keyed by call-service ID.
Ben Gilad0bf5b912014-01-28 17:55:57 -080065 */
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080066 private final Map<String, CallServiceWrapper> mCallServicesById = Maps.newHashMap();
Ben Gilad0bf5b912014-01-28 17:55:57 -080067
68 /**
Ben Gilad61925612014-03-11 19:06:36 -070069 * The set of attempted call services, used to ensure services are attempted at most once per
70 * outgoing-call attempt.
71 */
72 private final Set<CallServiceWrapper> mAttemptedCallServices = Sets.newHashSet();
73
74 /**
75 * The set of incompatible call services, used to suppress unnecessary call switching attempts.
76 */
77 private final Set<CallServiceWrapper> mIncompatibleCallServices = Sets.newHashSet();
78
79 /**
Ben Gilad28e8ad62014-03-06 17:01:54 -080080 * The list of currently-available call-service selector implementations.
Ben Gilad0bf5b912014-01-28 17:55:57 -080081 */
82 private final List<ICallServiceSelector> mSelectors;
83
Ben Giladd88323b2014-01-29 19:03:19 -080084 /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
85 private final Handler mHandler = new Handler(Looper.getMainLooper());
86
Santos Cordon681663d2014-01-30 04:32:15 -080087 /** Manages all outgoing call processors. */
88 private final OutgoingCallsManager mOutgoingCallsManager;
89
90 private final Switchboard mSwitchboard;
91
Ben Gilad61925612014-03-11 19:06:36 -070092 private final Runnable mNextCallServiceCallback = new Runnable() {
93 @Override public void run() {
94 attemptNextCallService();
95 }
96 };
97
Ben Gilad0bf5b912014-01-28 17:55:57 -080098 /**
Ben Giladc5b22692014-02-18 20:03:22 -080099 * The iterator over the currently-selected ordered list of call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800100 */
Ben Giladc5b22692014-02-18 20:03:22 -0800101 private Iterator<CallServiceDescriptor> mCallServiceDescriptorIterator;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800102
103 private Iterator<ICallServiceSelector> mSelectorIterator;
104
105 private boolean mIsAborted = false;
106
107 /**
108 * Persists the specified parameters and iterates through the prioritized list of selectors
109 * passing to each selector (read-only versions of) the call object and all available call-
Ben Giladc5b22692014-02-18 20:03:22 -0800110 * service descriptors. Stops once a matching selector is found. Calls with no matching
111 * selectors will eventually be killed by the cleanup/monitor switchboard handler, which will
112 * in turn call the abort method of this processor via {@link OutgoingCallsManager}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800113 *
114 * @param call The call to place.
115 * @param callServices The available call-service implementations.
116 * @param selectors The available call-service selector implementations.
Santos Cordon681663d2014-01-30 04:32:15 -0800117 * @param outgoingCallsManager Manager of all outgoing call processors.
118 * @param switchboard The switchboard.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800119 */
120 OutgoingCallProcessor(
121 Call call,
Santos Cordonc195e362014-02-11 17:05:31 -0800122 Set<CallServiceWrapper> callServices,
Ben Gilad0bf5b912014-01-28 17:55:57 -0800123 List<ICallServiceSelector> selectors,
Santos Cordon681663d2014-01-30 04:32:15 -0800124 OutgoingCallsManager outgoingCallsManager,
Ben Gilad0bf5b912014-01-28 17:55:57 -0800125 Switchboard switchboard) {
126
Ben Giladd88323b2014-01-29 19:03:19 -0800127 ThreadUtil.checkOnMainThread();
128
Ben Gilad0bf5b912014-01-28 17:55:57 -0800129 mCall = call;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800130 mSelectors = selectors;
Santos Cordon681663d2014-01-30 04:32:15 -0800131 mOutgoingCallsManager = outgoingCallsManager;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800132 mSwitchboard = switchboard;
133
Ben Giladc5b22692014-02-18 20:03:22 -0800134 // Populate the list and map of call-service descriptors. The list is needed since
Ben Gilad0bf5b912014-01-28 17:55:57 -0800135 // it's being passed down to selectors.
Santos Cordonc195e362014-02-11 17:05:31 -0800136 for (CallServiceWrapper callService : callServices) {
Ben Giladc5b22692014-02-18 20:03:22 -0800137 CallServiceDescriptor descriptor = callService.getDescriptor();
138 mCallServiceDescriptors.add(descriptor);
139 mCallServicesById.put(descriptor.getCallServiceId(), callService);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800140 }
141 }
142
143 /**
144 * Initiates the attempt to place the call. No-op beyond the first invocation.
145 */
146 void process() {
Ben Gilad28e8ad62014-03-06 17:01:54 -0800147 if (!mIsAborted) {
148 // Only process un-aborted calls.
149 ThreadUtil.checkOnMainThread();
Ben Giladd88323b2014-01-29 19:03:19 -0800150
Ben Gilad28e8ad62014-03-06 17:01:54 -0800151 if (mSelectorIterator == null) {
152 mSelectorIterator = mSelectors.iterator();
153 attemptNextSelector();
154 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800155 }
156 }
157
158 /**
Ben Gilad61925612014-03-11 19:06:36 -0700159 * Handles the specified compatibility status from the call-service implementation.
160 * TODO(gilad): Consider making this class stateful, potentially rejecting out-of-order/
161 * unexpected invocations (i.e. beyond checking for unexpected call IDs).
162 *
163 * @param callId The ID of the call.
164 * @param isCompatible True if the call-service is compatible with the corresponding call and
165 * false otherwise.
166 */
167 void setIsCompatibleWith(String callId, boolean isCompatible) {
168 if (callId != mCall.getId()) {
169 Log.wtf(this, "setIsCompatibleWith invoked with unexpected call ID: %s", callId);
170 return;
171 }
172
173 if (!mIsAborted) {
174 CallServiceWrapper callService = mCall.getCallService();
175 if (callService != null) {
176 if (isCompatible) {
177 callService.call(mCall.toCallInfo(), mNextCallServiceCallback);
178 return;
179 }
180 mIncompatibleCallServices.add(callService);
181 }
182 attemptNextCallService();
183 }
184 }
185
186 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800187 * Aborts the attempt to place the relevant call. Intended to be invoked by
Santos Cordon681663d2014-01-30 04:32:15 -0800188 * switchboard through the outgoing-calls manager.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800189 */
190 void abort() {
Ben Gilad9c234112014-03-04 16:07:33 -0800191 ThreadUtil.checkOnMainThread();
192 if (!mIsAborted) {
193 mCall.abort();
Ben Gilad9c234112014-03-04 16:07:33 -0800194 mIsAborted = true;
195 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800196 }
197
198 /**
Santos Cordon681663d2014-01-30 04:32:15 -0800199 * Completes the outgoing call sequence by setting the call service on the call object. This is
200 * invoked when the call service adapter receives positive confirmation that the call service
201 * placed the call.
202 */
203 void handleSuccessfulCallAttempt() {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800204 ThreadUtil.checkOnMainThread();
205
206 if (mIsAborted) {
207 // TODO(gilad): Ask the call service to drop the call?
208 return;
209 }
210
Santos Cordon681663d2014-01-30 04:32:15 -0800211 mCall.setState(CallState.DIALING);
Ben Gilad61925612014-03-11 19:06:36 -0700212 for (CallServiceWrapper callService : mIncompatibleCallServices) {
213 mCall.addIncompatibleCallService(callService);
214 }
Santos Cordon681663d2014-01-30 04:32:15 -0800215 mSwitchboard.handleSuccessfulOutgoingCall(mCall);
216 }
217
218 /**
219 * Attempts the next call service if the specified call service is the one currently being
220 * attempted.
221 *
222 * @param reason The call-service supplied reason for the failed call attempt.
223 */
224 void handleFailedCallAttempt(String reason) {
Ben Gilad28e8ad62014-03-06 17:01:54 -0800225 if (!mIsAborted) {
226 ThreadUtil.checkOnMainThread();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800227
Ben Gilad28e8ad62014-03-06 17:01:54 -0800228 mCall.clearCallService();
229 mCall.clearCallServiceSelector();
230 attemptNextCallService();
231 }
Santos Cordon681663d2014-01-30 04:32:15 -0800232 }
233
234 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800235 * Attempts to place the call using the next selector, no-op if no other selectors
236 * are available.
237 */
238 private void attemptNextSelector() {
239 if (mIsAborted) {
240 return;
241 }
242
243 if (mSelectorIterator.hasNext()) {
244 ICallServiceSelector selector = mSelectorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800245 mCall.setCallServiceSelector(selector);
246
Ben Giladd88323b2014-01-29 19:03:19 -0800247 ICallServiceSelectionResponse.Stub response = createSelectionResponse();
248 try {
Ben Giladc5b22692014-02-18 20:03:22 -0800249 selector.select(mCall.toCallInfo(), mCallServiceDescriptors, response);
Ben Giladd88323b2014-01-29 19:03:19 -0800250 } catch (RemoteException e) {
251 attemptNextSelector();
252 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800253
254 } else {
Santos Cordon681663d2014-01-30 04:32:15 -0800255 mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800256 }
257 }
258
259 /**
Ben Giladd88323b2014-01-29 19:03:19 -0800260 * @return A new selection-response object that's wired to run on the main (UI) thread.
261 */
262 private ICallServiceSelectionResponse.Stub createSelectionResponse() {
263 return new ICallServiceSelectionResponse.Stub() {
Ben Giladc5b22692014-02-18 20:03:22 -0800264 @Override public void setSelectedCallServiceDescriptors(
265 final List<CallServiceDescriptor> selectedCallServiceDescriptors) {
Ben Giladd88323b2014-01-29 19:03:19 -0800266
267 Runnable runnable = new Runnable() {
268 @Override public void run() {
Ben Giladc5b22692014-02-18 20:03:22 -0800269 processSelectedCallServiceDescriptors(selectedCallServiceDescriptors);
Ben Giladd88323b2014-01-29 19:03:19 -0800270 }
271 };
272
273 mHandler.post(runnable);
274 }
275 };
276 }
277
278 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800279 * Persists the ordered-list of call-service descriptor as selected by the current selector and
280 * starts iterating through the corresponding call services continuing the attempt to place the
281 * call.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800282 *
Ben Giladc5b22692014-02-18 20:03:22 -0800283 * @selectedCallServiceDescriptors The (ordered) list of call-service descriptor.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800284 */
Ben Giladc5b22692014-02-18 20:03:22 -0800285 private void processSelectedCallServiceDescriptors(
286 List<CallServiceDescriptor> selectedCallServiceDescriptors) {
287
288 if (selectedCallServiceDescriptors == null || selectedCallServiceDescriptors.isEmpty()) {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800289 attemptNextSelector();
Ben Giladc5b22692014-02-18 20:03:22 -0800290 } else if (mCallServiceDescriptorIterator == null) {
291 mCallServiceDescriptorIterator = selectedCallServiceDescriptors.iterator();
Ben Gilad0bf5b912014-01-28 17:55:57 -0800292 attemptNextCallService();
293 }
294 }
295
296 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800297 * Attempts to place the call using the call service specified by the next call-service
298 * descriptor of mCallServiceDescriptorIterator. If there are no more call services to
299 * attempt, the process continues to the next call-service selector via
300 * {@link #attemptNextSelector}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800301 */
302 private void attemptNextCallService() {
303 if (mIsAborted) {
304 return;
305 }
306
Evan Charltonf02e9882014-03-06 12:54:52 -0800307 if (mCallServiceDescriptorIterator != null && mCallServiceDescriptorIterator.hasNext()) {
Ben Giladc5b22692014-02-18 20:03:22 -0800308 CallServiceDescriptor descriptor = mCallServiceDescriptorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800309 final CallServiceWrapper callService =
310 mCallServicesById.get(descriptor.getCallServiceId());
311
Ben Gilad61925612014-03-11 19:06:36 -0700312 if (callService == null || mAttemptedCallServices.contains(callService)) {
313 // The next call service is either null or has already been attempted, fast forward
314 // to the next.
Santos Cordon681663d2014-01-30 04:32:15 -0800315 attemptNextCallService();
316 } else {
Ben Gilad61925612014-03-11 19:06:36 -0700317 mAttemptedCallServices.add(callService);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800318 mCall.setCallService(callService);
Ben Gilad61925612014-03-11 19:06:36 -0700319 callService.isCompatibleWith(mCall.toCallInfo(), mNextCallServiceCallback);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800320 }
321 } else {
Ben Giladc5b22692014-02-18 20:03:22 -0800322 mCallServiceDescriptorIterator = null;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800323 attemptNextSelector();
324 }
325 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800326}