blob: 5c8d5f5ec5c8133923292085f045a8cf19661bb8 [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 Giladc5b22692014-02-18 20:03:22 -080019import android.telecomm.CallServiceDescriptor;
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080020
Ben Gilad61925612014-03-11 19:06:36 -070021import com.google.android.collect.Sets;
Ben Gilad61925612014-03-11 19:06:36 -070022import com.google.common.collect.Lists;
Santos Cordon766d04f2014-05-06 10:28:25 -070023import com.google.common.collect.Maps;
Ben Gilad0bf5b912014-01-28 17:55:57 -080024
Sailesh Nepal18386a82014-03-19 10:22:40 -070025import java.util.Collection;
Ben Gilad0bf5b912014-01-28 17:55:57 -080026import java.util.Iterator;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30
31/**
32 * Utility class to place a call using the specified set of call-services and ordered selectors.
Ben Giladc5b22692014-02-18 20:03:22 -080033 * Iterates through the selectors and gets a sorted list of supported call-service descriptors
34 * for each selector. Upon receiving each sorted list (one list per selector), each of the
35 * corresponding call services is then attempted until either the outgoing call is placed, the
36 * attempted call is aborted (by the switchboard), or the list is exhausted -- whichever occurs
37 * first.
Ben Gilad0bf5b912014-01-28 17:55:57 -080038 *
Santos Cordon681663d2014-01-30 04:32:15 -080039 * Except for the abort case, all other scenarios should terminate with the switchboard notified
40 * of the result.
Ben Giladd88323b2014-01-29 19:03:19 -080041 *
42 * NOTE(gilad): Currently operating under the assumption that we'll have one timeout per (outgoing)
43 * call attempt. If we (also) like to timeout individual selectors and/or call services, the code
44 * here will need to be re-factored (quite a bit) to support that.
Ben Gilad0bf5b912014-01-28 17:55:57 -080045 */
46final class OutgoingCallProcessor {
47
48 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -080049 * The outgoing call this processor is tasked with placing.
50 */
51 private final Call mCall;
52
53 /**
Ben Giladc5b22692014-02-18 20:03:22 -080054 * The duplicate-free list of currently-available call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -080055 */
Ben Giladc5b22692014-02-18 20:03:22 -080056 private final List<CallServiceDescriptor> mCallServiceDescriptors = Lists.newArrayList();
Ben Gilad0bf5b912014-01-28 17:55:57 -080057
58 /**
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080059 * The map of currently-available call-service implementations keyed by call-service ID.
Ben Gilad0bf5b912014-01-28 17:55:57 -080060 */
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080061 private final Map<String, CallServiceWrapper> mCallServicesById = Maps.newHashMap();
Ben Gilad0bf5b912014-01-28 17:55:57 -080062
63 /**
Ben Gilad61925612014-03-11 19:06:36 -070064 * The set of attempted call services, used to ensure services are attempted at most once per
65 * outgoing-call attempt.
66 */
67 private final Set<CallServiceWrapper> mAttemptedCallServices = Sets.newHashSet();
68
69 /**
70 * The set of incompatible call services, used to suppress unnecessary call switching attempts.
71 */
72 private final Set<CallServiceWrapper> mIncompatibleCallServices = Sets.newHashSet();
73
74 /**
Ben Gilad28e8ad62014-03-06 17:01:54 -080075 * The list of currently-available call-service selector implementations.
Ben Gilad0bf5b912014-01-28 17:55:57 -080076 */
Sailesh Nepal18386a82014-03-19 10:22:40 -070077 private final Collection<CallServiceSelectorWrapper> mSelectors;
Ben Giladd88323b2014-01-29 19:03:19 -080078
Santos Cordon681663d2014-01-30 04:32:15 -080079 /** Manages all outgoing call processors. */
80 private final OutgoingCallsManager mOutgoingCallsManager;
81
82 private final Switchboard mSwitchboard;
83
Ben Gilad61925612014-03-11 19:06:36 -070084 private final Runnable mNextCallServiceCallback = new Runnable() {
85 @Override public void run() {
86 attemptNextCallService();
87 }
88 };
89
Sailesh Nepal18386a82014-03-19 10:22:40 -070090 private final Runnable mNextSelectorCallback = new Runnable() {
91 @Override public void run() {
92 attemptNextSelector();
93 }
94 };
95
Ben Gilad0bf5b912014-01-28 17:55:57 -080096 /**
Ben Giladc5b22692014-02-18 20:03:22 -080097 * The iterator over the currently-selected ordered list of call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -080098 */
Ben Giladc5b22692014-02-18 20:03:22 -080099 private Iterator<CallServiceDescriptor> mCallServiceDescriptorIterator;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800100
Sailesh Nepal18386a82014-03-19 10:22:40 -0700101 private Iterator<CallServiceSelectorWrapper> mSelectorIterator;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800102
103 private boolean mIsAborted = false;
104
105 /**
106 * Persists the specified parameters and iterates through the prioritized list of selectors
107 * passing to each selector (read-only versions of) the call object and all available call-
Ben Giladc5b22692014-02-18 20:03:22 -0800108 * service descriptors. Stops once a matching selector is found. Calls with no matching
109 * selectors will eventually be killed by the cleanup/monitor switchboard handler, which will
110 * in turn call the abort method of this processor via {@link OutgoingCallsManager}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800111 *
112 * @param call The call to place.
113 * @param callServices The available call-service implementations.
114 * @param selectors The available call-service selector implementations.
Santos Cordon681663d2014-01-30 04:32:15 -0800115 * @param outgoingCallsManager Manager of all outgoing call processors.
116 * @param switchboard The switchboard.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800117 */
118 OutgoingCallProcessor(
119 Call call,
Santos Cordonc195e362014-02-11 17:05:31 -0800120 Set<CallServiceWrapper> callServices,
Sailesh Nepal18386a82014-03-19 10:22:40 -0700121 Collection<CallServiceSelectorWrapper> selectors,
Santos Cordon681663d2014-01-30 04:32:15 -0800122 OutgoingCallsManager outgoingCallsManager,
Ben Gilad0bf5b912014-01-28 17:55:57 -0800123 Switchboard switchboard) {
124
Ben Giladd88323b2014-01-29 19:03:19 -0800125 ThreadUtil.checkOnMainThread();
126
Ben Gilad0bf5b912014-01-28 17:55:57 -0800127 mCall = call;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800128 mSelectors = selectors;
Santos Cordon681663d2014-01-30 04:32:15 -0800129 mOutgoingCallsManager = outgoingCallsManager;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800130 mSwitchboard = switchboard;
131
Ben Giladc5b22692014-02-18 20:03:22 -0800132 // Populate the list and map of call-service descriptors. The list is needed since
Ben Gilad0bf5b912014-01-28 17:55:57 -0800133 // it's being passed down to selectors.
Santos Cordonc195e362014-02-11 17:05:31 -0800134 for (CallServiceWrapper callService : callServices) {
Ben Giladc5b22692014-02-18 20:03:22 -0800135 CallServiceDescriptor descriptor = callService.getDescriptor();
136 mCallServiceDescriptors.add(descriptor);
137 mCallServicesById.put(descriptor.getCallServiceId(), callService);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800138 }
139 }
140
141 /**
142 * Initiates the attempt to place the call. No-op beyond the first invocation.
143 */
144 void process() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700145 Log.v(this, "process, mIsAborted: %b", mIsAborted);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800146 if (!mIsAborted) {
147 // Only process un-aborted calls.
148 ThreadUtil.checkOnMainThread();
Ben Giladd88323b2014-01-29 19:03:19 -0800149
Ben Gilad28e8ad62014-03-06 17:01:54 -0800150 if (mSelectorIterator == null) {
151 mSelectorIterator = mSelectors.iterator();
152 attemptNextSelector();
153 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800154 }
155 }
156
157 /**
Ben Gilad61925612014-03-11 19:06:36 -0700158 * Handles the specified compatibility status from the call-service implementation.
159 * TODO(gilad): Consider making this class stateful, potentially rejecting out-of-order/
160 * unexpected invocations (i.e. beyond checking for unexpected call IDs).
161 *
Ben Gilad61925612014-03-11 19:06:36 -0700162 * @param isCompatible True if the call-service is compatible with the corresponding call and
163 * false otherwise.
164 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700165 void setIsCompatibleWith(Call call, boolean isCompatible) {
166 Log.v(this, "setIsCompatibleWith, call: %s, isCompatible: %b", call, isCompatible);
167 if (call != mCall) {
168 Log.wtf(this, "setIsCompatibleWith invoked with unexpected call: %s, expected call: %s",
169 call, mCall);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700170 return;
171 }
Ben Gilad61925612014-03-11 19:06:36 -0700172
Sailesh Nepal810735e2014-03-18 18:15:46 -0700173 if (!mIsAborted) {
174 CallServiceWrapper callService = mCall.getCallService();
175 if (callService != null) {
176 if (isCompatible) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700177 callService.call(mCall);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700178 return;
179 }
180 mIncompatibleCallServices.add(callService);
181 }
182 attemptNextCallService();
183 }
Ben Gilad61925612014-03-11 19:06:36 -0700184 }
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() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700191 Log.v(this, "abort");
Ben Gilad9c234112014-03-04 16:07:33 -0800192 ThreadUtil.checkOnMainThread();
193 if (!mIsAborted) {
Ben Gilad9c234112014-03-04 16:07:33 -0800194 mIsAborted = true;
Sailesh Nepale59bb192014-04-01 18:33:59 -0700195 // This will also clear the call's call service and selector.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700196 mOutgoingCallsManager.handleFailedOutgoingCall(mCall, true /* isAborted */);
Ben Gilad9c234112014-03-04 16:07:33 -0800197 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800198 }
199
200 /**
Santos Cordon681663d2014-01-30 04:32:15 -0800201 * Completes the outgoing call sequence by setting the call service on the call object. This is
202 * invoked when the call service adapter receives positive confirmation that the call service
203 * placed the call.
204 */
205 void handleSuccessfulCallAttempt() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700206 Log.v(this, "handleSuccessfulCallAttempt");
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800207 ThreadUtil.checkOnMainThread();
208
209 if (mIsAborted) {
210 // TODO(gilad): Ask the call service to drop the call?
211 return;
212 }
213
Ben Gilad61925612014-03-11 19:06:36 -0700214 for (CallServiceWrapper callService : mIncompatibleCallServices) {
215 mCall.addIncompatibleCallService(callService);
216 }
Santos Cordon681663d2014-01-30 04:32:15 -0800217 mSwitchboard.handleSuccessfulOutgoingCall(mCall);
218 }
219
220 /**
221 * Attempts the next call service if the specified call service is the one currently being
222 * attempted.
223 *
224 * @param reason The call-service supplied reason for the failed call attempt.
225 */
226 void handleFailedCallAttempt(String reason) {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700227 Log.v(this, "handleFailedCallAttempt");
Ben Gilad28e8ad62014-03-06 17:01:54 -0800228 if (!mIsAborted) {
229 ThreadUtil.checkOnMainThread();
Ben Gilad28e8ad62014-03-06 17:01:54 -0800230 attemptNextCallService();
231 }
Santos Cordon681663d2014-01-30 04:32:15 -0800232 }
233
234 /**
Sailesh Nepal6ab6fb72014-04-01 20:03:19 -0700235 * Persists the ordered-list of call-service descriptor as selected by the current selector and
236 * starts iterating through the corresponding call services continuing the attempt to place the
237 * call.
238 *
239 * @param descriptors The (ordered) list of call-service descriptor.
240 */
241 void processSelectedCallServices(List<CallServiceDescriptor> descriptors) {
242 Log.v(this, "processSelectedCallServices");
243 if (descriptors == null || descriptors.isEmpty()) {
244 attemptNextSelector();
245 } else if (mCallServiceDescriptorIterator == null) {
246 mCallServiceDescriptorIterator = descriptors.iterator();
247 attemptNextCallService();
248 }
249 }
250
251 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800252 * Attempts to place the call using the next selector, no-op if no other selectors
253 * are available.
254 */
255 private void attemptNextSelector() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700256 Log.v(this, "attemptNextSelector, mIsAborted: %b", mIsAborted);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800257 if (mIsAborted) {
258 return;
259 }
260
261 if (mSelectorIterator.hasNext()) {
Sailesh Nepal18386a82014-03-19 10:22:40 -0700262 CallServiceSelectorWrapper selector = mSelectorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800263 mCall.setCallServiceSelector(selector);
Sailesh Nepal6ab6fb72014-04-01 20:03:19 -0700264 selector.select(mCall, mCallServiceDescriptors, mNextSelectorCallback);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800265 } else {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700266 Log.v(this, "attemptNextSelector, no more selectors, failing");
Sailesh Nepale59bb192014-04-01 18:33:59 -0700267 mCall.clearCallServiceSelector();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700268 mOutgoingCallsManager.handleFailedOutgoingCall(mCall, false /* isAborted */);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800269 }
270 }
271
272 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800273 * Attempts to place the call using the call service specified by the next call-service
274 * descriptor of mCallServiceDescriptorIterator. If there are no more call services to
275 * attempt, the process continues to the next call-service selector via
276 * {@link #attemptNextSelector}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800277 */
278 private void attemptNextCallService() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700279 Log.v(this, "attemptNextCallService, mIsAborted: %b", mIsAborted);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800280 if (mIsAborted) {
281 return;
282 }
283
Evan Charltonf02e9882014-03-06 12:54:52 -0800284 if (mCallServiceDescriptorIterator != null && mCallServiceDescriptorIterator.hasNext()) {
Ben Giladc5b22692014-02-18 20:03:22 -0800285 CallServiceDescriptor descriptor = mCallServiceDescriptorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800286 final CallServiceWrapper callService =
287 mCallServicesById.get(descriptor.getCallServiceId());
288
Ben Gilad61925612014-03-11 19:06:36 -0700289 if (callService == null || mAttemptedCallServices.contains(callService)) {
290 // The next call service is either null or has already been attempted, fast forward
291 // to the next.
Santos Cordon681663d2014-01-30 04:32:15 -0800292 attemptNextCallService();
293 } else {
Ben Gilad61925612014-03-11 19:06:36 -0700294 mAttemptedCallServices.add(callService);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800295 mCall.setCallService(callService);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700296 callService.isCompatibleWith(mCall, mNextCallServiceCallback);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800297 }
298 } else {
Ben Giladc5b22692014-02-18 20:03:22 -0800299 mCallServiceDescriptorIterator = null;
Sailesh Nepale59bb192014-04-01 18:33:59 -0700300 mCall.clearCallService();
Ben Gilad0bf5b912014-01-28 17:55:57 -0800301 attemptNextSelector();
302 }
303 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800304}