blob: 3d41fe7a71398c49246bc8a790af9f9de54d9c70 [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 Nepal18386a82014-03-19 10:22:40 -070024import android.telecomm.CallServiceSelector.CallServiceSelectionResponse;
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080025
Ben Gilad61925612014-03-11 19:06:36 -070026import com.google.android.collect.Sets;
27import com.google.common.collect.Maps;
28import com.google.common.collect.Lists;
Ben Gilad0bf5b912014-01-28 17:55:57 -080029
Sailesh Nepal18386a82014-03-19 10:22:40 -070030import java.util.Collection;
Ben Gilad0bf5b912014-01-28 17:55:57 -080031import 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 */
Sailesh Nepal18386a82014-03-19 10:22:40 -070082 private final Collection<CallServiceSelectorWrapper> mSelectors;
Ben Giladd88323b2014-01-29 19:03:19 -080083
Santos Cordon681663d2014-01-30 04:32:15 -080084 /** Manages all outgoing call processors. */
85 private final OutgoingCallsManager mOutgoingCallsManager;
86
87 private final Switchboard mSwitchboard;
88
Ben Gilad61925612014-03-11 19:06:36 -070089 private final Runnable mNextCallServiceCallback = new Runnable() {
90 @Override public void run() {
91 attemptNextCallService();
92 }
93 };
94
Sailesh Nepal18386a82014-03-19 10:22:40 -070095 private final Runnable mNextSelectorCallback = new Runnable() {
96 @Override public void run() {
97 attemptNextSelector();
98 }
99 };
100
Ben Gilad0bf5b912014-01-28 17:55:57 -0800101 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800102 * The iterator over the currently-selected ordered list of call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800103 */
Ben Giladc5b22692014-02-18 20:03:22 -0800104 private Iterator<CallServiceDescriptor> mCallServiceDescriptorIterator;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800105
Sailesh Nepal18386a82014-03-19 10:22:40 -0700106 private Iterator<CallServiceSelectorWrapper> mSelectorIterator;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800107
108 private boolean mIsAborted = false;
109
110 /**
111 * Persists the specified parameters and iterates through the prioritized list of selectors
112 * passing to each selector (read-only versions of) the call object and all available call-
Ben Giladc5b22692014-02-18 20:03:22 -0800113 * service descriptors. Stops once a matching selector is found. Calls with no matching
114 * selectors will eventually be killed by the cleanup/monitor switchboard handler, which will
115 * in turn call the abort method of this processor via {@link OutgoingCallsManager}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800116 *
117 * @param call The call to place.
118 * @param callServices The available call-service implementations.
119 * @param selectors The available call-service selector implementations.
Santos Cordon681663d2014-01-30 04:32:15 -0800120 * @param outgoingCallsManager Manager of all outgoing call processors.
121 * @param switchboard The switchboard.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800122 */
123 OutgoingCallProcessor(
124 Call call,
Santos Cordonc195e362014-02-11 17:05:31 -0800125 Set<CallServiceWrapper> callServices,
Sailesh Nepal18386a82014-03-19 10:22:40 -0700126 Collection<CallServiceSelectorWrapper> selectors,
Santos Cordon681663d2014-01-30 04:32:15 -0800127 OutgoingCallsManager outgoingCallsManager,
Ben Gilad0bf5b912014-01-28 17:55:57 -0800128 Switchboard switchboard) {
129
Ben Giladd88323b2014-01-29 19:03:19 -0800130 ThreadUtil.checkOnMainThread();
131
Ben Gilad0bf5b912014-01-28 17:55:57 -0800132 mCall = call;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800133 mSelectors = selectors;
Santos Cordon681663d2014-01-30 04:32:15 -0800134 mOutgoingCallsManager = outgoingCallsManager;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800135 mSwitchboard = switchboard;
136
Ben Giladc5b22692014-02-18 20:03:22 -0800137 // Populate the list and map of call-service descriptors. The list is needed since
Ben Gilad0bf5b912014-01-28 17:55:57 -0800138 // it's being passed down to selectors.
Santos Cordonc195e362014-02-11 17:05:31 -0800139 for (CallServiceWrapper callService : callServices) {
Ben Giladc5b22692014-02-18 20:03:22 -0800140 CallServiceDescriptor descriptor = callService.getDescriptor();
141 mCallServiceDescriptors.add(descriptor);
142 mCallServicesById.put(descriptor.getCallServiceId(), callService);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800143 }
144 }
145
146 /**
147 * Initiates the attempt to place the call. No-op beyond the first invocation.
148 */
149 void process() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700150 Log.v(this, "process, mIsAborted: %b", mIsAborted);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800151 if (!mIsAborted) {
152 // Only process un-aborted calls.
153 ThreadUtil.checkOnMainThread();
Ben Giladd88323b2014-01-29 19:03:19 -0800154
Ben Gilad28e8ad62014-03-06 17:01:54 -0800155 if (mSelectorIterator == null) {
156 mSelectorIterator = mSelectors.iterator();
157 attemptNextSelector();
158 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800159 }
160 }
161
162 /**
Ben Gilad61925612014-03-11 19:06:36 -0700163 * Handles the specified compatibility status from the call-service implementation.
164 * TODO(gilad): Consider making this class stateful, potentially rejecting out-of-order/
165 * unexpected invocations (i.e. beyond checking for unexpected call IDs).
166 *
167 * @param callId The ID of the call.
168 * @param isCompatible True if the call-service is compatible with the corresponding call and
169 * false otherwise.
170 */
171 void setIsCompatibleWith(String callId, boolean isCompatible) {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700172 Log.v(this, "setIsCompatibleWith, callId: %s, isCompatible: %b", callId, isCompatible);
173 if (!callId.equals(mCall.getId())) {
174 Log.wtf(this, "setIsCompatibleWith invoked with unexpected call ID: %s - expected call"
Yorke Leeadee12d2014-03-13 12:08:30 -0700175 + " ID: %s", callId, mCall.getId());
Sailesh Nepal810735e2014-03-18 18:15:46 -0700176 return;
177 }
Ben Gilad61925612014-03-11 19:06:36 -0700178
Sailesh Nepal810735e2014-03-18 18:15:46 -0700179 if (!mIsAborted) {
180 CallServiceWrapper callService = mCall.getCallService();
181 if (callService != null) {
182 if (isCompatible) {
183 callService.call(mCall.toCallInfo(), mNextCallServiceCallback);
184 return;
185 }
186 mIncompatibleCallServices.add(callService);
187 }
188 attemptNextCallService();
189 }
Ben Gilad61925612014-03-11 19:06:36 -0700190 }
191
192 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800193 * Aborts the attempt to place the relevant call. Intended to be invoked by
Santos Cordon681663d2014-01-30 04:32:15 -0800194 * switchboard through the outgoing-calls manager.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800195 */
196 void abort() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700197 Log.v(this, "abort");
Ben Gilad9c234112014-03-04 16:07:33 -0800198 ThreadUtil.checkOnMainThread();
199 if (!mIsAborted) {
Ben Gilad9c234112014-03-04 16:07:33 -0800200 mIsAborted = true;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700201 mOutgoingCallsManager.handleFailedOutgoingCall(mCall, true /* isAborted */);
Ben Gilad9c234112014-03-04 16:07:33 -0800202 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800203 }
204
205 /**
Santos Cordon681663d2014-01-30 04:32:15 -0800206 * Completes the outgoing call sequence by setting the call service on the call object. This is
207 * invoked when the call service adapter receives positive confirmation that the call service
208 * placed the call.
209 */
210 void handleSuccessfulCallAttempt() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700211 Log.v(this, "handleSuccessfulCallAttempt");
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800212 ThreadUtil.checkOnMainThread();
213
214 if (mIsAborted) {
215 // TODO(gilad): Ask the call service to drop the call?
216 return;
217 }
218
Ben Gilad61925612014-03-11 19:06:36 -0700219 for (CallServiceWrapper callService : mIncompatibleCallServices) {
220 mCall.addIncompatibleCallService(callService);
221 }
Santos Cordon681663d2014-01-30 04:32:15 -0800222 mSwitchboard.handleSuccessfulOutgoingCall(mCall);
223 }
224
225 /**
226 * Attempts the next call service if the specified call service is the one currently being
227 * attempted.
228 *
229 * @param reason The call-service supplied reason for the failed call attempt.
230 */
231 void handleFailedCallAttempt(String reason) {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700232 Log.v(this, "handleFailedCallAttempt");
Ben Gilad28e8ad62014-03-06 17:01:54 -0800233 if (!mIsAborted) {
234 ThreadUtil.checkOnMainThread();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800235
Ben Gilad28e8ad62014-03-06 17:01:54 -0800236 mCall.clearCallService();
237 mCall.clearCallServiceSelector();
238 attemptNextCallService();
239 }
Santos Cordon681663d2014-01-30 04:32:15 -0800240 }
241
242 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800243 * Attempts to place the call using the next selector, no-op if no other selectors
244 * are available.
245 */
246 private void attemptNextSelector() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700247 Log.v(this, "attemptNextSelector, mIsAborted: %b", mIsAborted);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800248 if (mIsAborted) {
249 return;
250 }
251
252 if (mSelectorIterator.hasNext()) {
Sailesh Nepal18386a82014-03-19 10:22:40 -0700253 CallServiceSelectorWrapper selector = mSelectorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800254 mCall.setCallServiceSelector(selector);
255
Sailesh Nepal18386a82014-03-19 10:22:40 -0700256 CallServiceSelectionResponse responseCallback = new CallServiceSelectionResponse() {
257 @Override
258 public void setSelectedCallServices(List<CallServiceDescriptor> callServices) {
259 processSelectedCallServiceDescriptors(callServices);
260 }
261 };
262 selector.select(mCall.toCallInfo(), mCallServiceDescriptors, responseCallback,
263 mNextSelectorCallback);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800264 } else {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700265 Log.v(this, "attemptNextSelector, no more selectors, failing");
266 mOutgoingCallsManager.handleFailedOutgoingCall(mCall, false /* isAborted */);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800267 }
268 }
269
270 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800271 * Persists the ordered-list of call-service descriptor as selected by the current selector and
272 * starts iterating through the corresponding call services continuing the attempt to place the
273 * call.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800274 *
Ben Giladc5b22692014-02-18 20:03:22 -0800275 * @selectedCallServiceDescriptors The (ordered) list of call-service descriptor.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800276 */
Ben Giladc5b22692014-02-18 20:03:22 -0800277 private void processSelectedCallServiceDescriptors(
278 List<CallServiceDescriptor> selectedCallServiceDescriptors) {
279
Sailesh Nepal810735e2014-03-18 18:15:46 -0700280 Log.v(this, "processSelectedCallServiceDescriptors");
Ben Giladc5b22692014-02-18 20:03:22 -0800281 if (selectedCallServiceDescriptors == null || selectedCallServiceDescriptors.isEmpty()) {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800282 attemptNextSelector();
Ben Giladc5b22692014-02-18 20:03:22 -0800283 } else if (mCallServiceDescriptorIterator == null) {
284 mCallServiceDescriptorIterator = selectedCallServiceDescriptors.iterator();
Ben Gilad0bf5b912014-01-28 17:55:57 -0800285 attemptNextCallService();
286 }
287 }
288
289 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800290 * Attempts to place the call using the call service specified by the next call-service
291 * descriptor of mCallServiceDescriptorIterator. If there are no more call services to
292 * attempt, the process continues to the next call-service selector via
293 * {@link #attemptNextSelector}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800294 */
295 private void attemptNextCallService() {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700296 Log.v(this, "attemptNextCallService, mIsAborted: %b", mIsAborted);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800297 if (mIsAborted) {
298 return;
299 }
300
Evan Charltonf02e9882014-03-06 12:54:52 -0800301 if (mCallServiceDescriptorIterator != null && mCallServiceDescriptorIterator.hasNext()) {
Ben Giladc5b22692014-02-18 20:03:22 -0800302 CallServiceDescriptor descriptor = mCallServiceDescriptorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800303 final CallServiceWrapper callService =
304 mCallServicesById.get(descriptor.getCallServiceId());
305
Ben Gilad61925612014-03-11 19:06:36 -0700306 if (callService == null || mAttemptedCallServices.contains(callService)) {
307 // The next call service is either null or has already been attempted, fast forward
308 // to the next.
Santos Cordon681663d2014-01-30 04:32:15 -0800309 attemptNextCallService();
310 } else {
Ben Gilad61925612014-03-11 19:06:36 -0700311 mAttemptedCallServices.add(callService);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800312 mCall.setCallService(callService);
Ben Gilad61925612014-03-11 19:06:36 -0700313 callService.isCompatibleWith(mCall.toCallInfo(), mNextCallServiceCallback);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800314 }
315 } else {
Ben Giladc5b22692014-02-18 20:03:22 -0800316 mCallServiceDescriptorIterator = null;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800317 attemptNextSelector();
318 }
319 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800320}