blob: c5dd7ac4adcdf9a9bdd993de1c28a1d5a7beb3cc [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
19import com.google.common.base.Preconditions;
20import com.google.common.collect.Maps;
Ben Giladd88323b2014-01-29 19:03:19 -080021import com.google.common.collect.Lists;
Ben Gilad0bf5b912014-01-28 17:55:57 -080022
Ben Giladd88323b2014-01-29 19:03:19 -080023import android.os.Handler;
24import android.os.Looper;
Ben Gilad0bf5b912014-01-28 17:55:57 -080025import android.os.RemoteException;
Santos Cordon681663d2014-01-30 04:32:15 -080026import android.telecomm.CallState;
Ben Giladc5b22692014-02-18 20:03:22 -080027import android.telecomm.CallServiceDescriptor;
Ben Giladd88323b2014-01-29 19:03:19 -080028import android.telecomm.ICallServiceSelectionResponse;
Ben Gilad0bf5b912014-01-28 17:55:57 -080029import android.telecomm.ICallServiceSelector;
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080030
31import com.android.telecomm.ServiceBinder.BindCallback;
Ben Gilad0bf5b912014-01-28 17:55:57 -080032
33import java.util.Iterator;
34import java.util.List;
35import java.util.Map;
36import java.util.Set;
37
38/**
39 * Utility class to place a call using the specified set of call-services and ordered selectors.
Ben Giladc5b22692014-02-18 20:03:22 -080040 * Iterates through the selectors and gets a sorted list of supported call-service descriptors
41 * for each selector. Upon receiving each sorted list (one list per selector), each of the
42 * corresponding call services is then attempted until either the outgoing call is placed, the
43 * attempted call is aborted (by the switchboard), or the list is exhausted -- whichever occurs
44 * first.
Ben Gilad0bf5b912014-01-28 17:55:57 -080045 *
Santos Cordon681663d2014-01-30 04:32:15 -080046 * Except for the abort case, all other scenarios should terminate with the switchboard notified
47 * of the result.
Ben Giladd88323b2014-01-29 19:03:19 -080048 *
49 * NOTE(gilad): Currently operating under the assumption that we'll have one timeout per (outgoing)
50 * call attempt. If we (also) like to timeout individual selectors and/or call services, the code
51 * here will need to be re-factored (quite a bit) to support that.
Ben Gilad0bf5b912014-01-28 17:55:57 -080052 */
53final class OutgoingCallProcessor {
54
55 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -080056 * The outgoing call this processor is tasked with placing.
57 */
58 private final Call mCall;
59
60 /**
Ben Giladc5b22692014-02-18 20:03:22 -080061 * The duplicate-free list of currently-available call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -080062 */
Ben Giladc5b22692014-02-18 20:03:22 -080063 private final List<CallServiceDescriptor> mCallServiceDescriptors = Lists.newArrayList();
Ben Gilad0bf5b912014-01-28 17:55:57 -080064
65 /**
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080066 * The map of currently-available call-service implementations keyed by call-service ID.
Ben Gilad0bf5b912014-01-28 17:55:57 -080067 */
Sailesh Nepalb6141ae2014-02-18 08:45:26 -080068 private final Map<String, CallServiceWrapper> mCallServicesById = Maps.newHashMap();
Ben Gilad0bf5b912014-01-28 17:55:57 -080069
70 /**
71 * The set of currently-available call-service selector implementations.
72 */
73 private final List<ICallServiceSelector> mSelectors;
74
Ben Giladd88323b2014-01-29 19:03:19 -080075 /** Used to run code (e.g. messages, Runnables) on the main (UI) thread. */
76 private final Handler mHandler = new Handler(Looper.getMainLooper());
77
Santos Cordon681663d2014-01-30 04:32:15 -080078 /** Manages all outgoing call processors. */
79 private final OutgoingCallsManager mOutgoingCallsManager;
80
81 private final Switchboard mSwitchboard;
82
Ben Gilad0bf5b912014-01-28 17:55:57 -080083 /**
Ben Giladc5b22692014-02-18 20:03:22 -080084 * The iterator over the currently-selected ordered list of call-service descriptors.
Ben Gilad0bf5b912014-01-28 17:55:57 -080085 */
Ben Giladc5b22692014-02-18 20:03:22 -080086 private Iterator<CallServiceDescriptor> mCallServiceDescriptorIterator;
Ben Gilad0bf5b912014-01-28 17:55:57 -080087
88 private Iterator<ICallServiceSelector> mSelectorIterator;
89
90 private boolean mIsAborted = false;
91
92 /**
93 * Persists the specified parameters and iterates through the prioritized list of selectors
94 * passing to each selector (read-only versions of) the call object and all available call-
Ben Giladc5b22692014-02-18 20:03:22 -080095 * service descriptors. Stops once a matching selector is found. Calls with no matching
96 * selectors will eventually be killed by the cleanup/monitor switchboard handler, which will
97 * in turn call the abort method of this processor via {@link OutgoingCallsManager}.
Ben Gilad0bf5b912014-01-28 17:55:57 -080098 *
99 * @param call The call to place.
100 * @param callServices The available call-service implementations.
101 * @param selectors The available call-service selector implementations.
Santos Cordon681663d2014-01-30 04:32:15 -0800102 * @param outgoingCallsManager Manager of all outgoing call processors.
103 * @param switchboard The switchboard.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800104 */
105 OutgoingCallProcessor(
106 Call call,
Santos Cordonc195e362014-02-11 17:05:31 -0800107 Set<CallServiceWrapper> callServices,
Ben Gilad0bf5b912014-01-28 17:55:57 -0800108 List<ICallServiceSelector> selectors,
Santos Cordon681663d2014-01-30 04:32:15 -0800109 OutgoingCallsManager outgoingCallsManager,
Ben Gilad0bf5b912014-01-28 17:55:57 -0800110 Switchboard switchboard) {
111
Ben Giladd88323b2014-01-29 19:03:19 -0800112 ThreadUtil.checkOnMainThread();
113
Ben Gilad0bf5b912014-01-28 17:55:57 -0800114 Preconditions.checkNotNull(callServices);
115 Preconditions.checkNotNull(selectors);
116
117 mCall = call;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800118 mSelectors = selectors;
Santos Cordon681663d2014-01-30 04:32:15 -0800119 mOutgoingCallsManager = outgoingCallsManager;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800120 mSwitchboard = switchboard;
121
Ben Giladc5b22692014-02-18 20:03:22 -0800122 // Populate the list and map of call-service descriptors. The list is needed since
Ben Gilad0bf5b912014-01-28 17:55:57 -0800123 // it's being passed down to selectors.
Santos Cordonc195e362014-02-11 17:05:31 -0800124 for (CallServiceWrapper callService : callServices) {
Ben Giladc5b22692014-02-18 20:03:22 -0800125 CallServiceDescriptor descriptor = callService.getDescriptor();
126 mCallServiceDescriptors.add(descriptor);
127 mCallServicesById.put(descriptor.getCallServiceId(), callService);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800128 }
129 }
130
131 /**
132 * Initiates the attempt to place the call. No-op beyond the first invocation.
133 */
134 void process() {
Ben Giladd88323b2014-01-29 19:03:19 -0800135 ThreadUtil.checkOnMainThread();
136
Ben Giladc5b22692014-02-18 20:03:22 -0800137 if (mSelectors.isEmpty() || mCallServiceDescriptors.isEmpty()) {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800138 // TODO(gilad): Consider adding a failure message/type to differentiate the various
139 // cases, or potentially throw an exception in this case.
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800140 // TODO(gilad): Perform this check all the way up in switchboard to short-circuit
141 // the current detour.
Santos Cordon681663d2014-01-30 04:32:15 -0800142 mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800143 } else if (mSelectorIterator == null) {
144 mSelectorIterator = mSelectors.iterator();
145 attemptNextSelector();
146 }
147 }
148
149 /**
150 * Aborts the attempt to place the relevant call. Intended to be invoked by
Santos Cordon681663d2014-01-30 04:32:15 -0800151 * switchboard through the outgoing-calls manager.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800152 */
153 void abort() {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800154 mCall.abort();
155
156 // TODO(gilad): Add logic to notify the relevant call service and/or selector.
157
Ben Gilad0bf5b912014-01-28 17:55:57 -0800158 mIsAborted = true;
159 }
160
161 /**
Santos Cordon681663d2014-01-30 04:32:15 -0800162 * Completes the outgoing call sequence by setting the call service on the call object. This is
163 * invoked when the call service adapter receives positive confirmation that the call service
164 * placed the call.
165 */
166 void handleSuccessfulCallAttempt() {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800167 ThreadUtil.checkOnMainThread();
168
169 if (mIsAborted) {
170 // TODO(gilad): Ask the call service to drop the call?
171 return;
172 }
173
Santos Cordon681663d2014-01-30 04:32:15 -0800174 mCall.setState(CallState.DIALING);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800175
176 // TODO(gilad): Seems better/clearer not to invoke "abort" on successfully-connected calls.
Sailesh Nepal155ab512014-02-20 18:11:40 -0800177 abort();
Santos Cordon681663d2014-01-30 04:32:15 -0800178
179 mSwitchboard.handleSuccessfulOutgoingCall(mCall);
180 }
181
182 /**
183 * Attempts the next call service if the specified call service is the one currently being
184 * attempted.
185 *
186 * @param reason The call-service supplied reason for the failed call attempt.
187 */
188 void handleFailedCallAttempt(String reason) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800189 ThreadUtil.checkOnMainThread();
190
191 mCall.clearCallService();
192 mCall.clearCallServiceSelector();
Santos Cordon681663d2014-01-30 04:32:15 -0800193 attemptNextCallService();
194 }
195
196 /**
Ben Gilad0bf5b912014-01-28 17:55:57 -0800197 * Attempts to place the call using the next selector, no-op if no other selectors
198 * are available.
199 */
200 private void attemptNextSelector() {
201 if (mIsAborted) {
202 return;
203 }
204
205 if (mSelectorIterator.hasNext()) {
206 ICallServiceSelector selector = mSelectorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800207 mCall.setCallServiceSelector(selector);
208
Ben Giladd88323b2014-01-29 19:03:19 -0800209 ICallServiceSelectionResponse.Stub response = createSelectionResponse();
210 try {
Ben Giladc5b22692014-02-18 20:03:22 -0800211 selector.select(mCall.toCallInfo(), mCallServiceDescriptors, response);
Ben Giladd88323b2014-01-29 19:03:19 -0800212 } catch (RemoteException e) {
213 attemptNextSelector();
214 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800215
216 } else {
Santos Cordon681663d2014-01-30 04:32:15 -0800217 mOutgoingCallsManager.handleFailedOutgoingCall(mCall);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800218 }
219 }
220
221 /**
Ben Giladd88323b2014-01-29 19:03:19 -0800222 * @return A new selection-response object that's wired to run on the main (UI) thread.
223 */
224 private ICallServiceSelectionResponse.Stub createSelectionResponse() {
225 return new ICallServiceSelectionResponse.Stub() {
Ben Giladc5b22692014-02-18 20:03:22 -0800226 @Override public void setSelectedCallServiceDescriptors(
227 final List<CallServiceDescriptor> selectedCallServiceDescriptors) {
Ben Giladd88323b2014-01-29 19:03:19 -0800228
229 Runnable runnable = new Runnable() {
230 @Override public void run() {
Ben Giladc5b22692014-02-18 20:03:22 -0800231 processSelectedCallServiceDescriptors(selectedCallServiceDescriptors);
Ben Giladd88323b2014-01-29 19:03:19 -0800232 }
233 };
234
235 mHandler.post(runnable);
236 }
237 };
238 }
239
240 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800241 * Persists the ordered-list of call-service descriptor as selected by the current selector and
242 * starts iterating through the corresponding call services continuing the attempt to place the
243 * call.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800244 *
Ben Giladc5b22692014-02-18 20:03:22 -0800245 * @selectedCallServiceDescriptors The (ordered) list of call-service descriptor.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800246 */
Ben Giladc5b22692014-02-18 20:03:22 -0800247 private void processSelectedCallServiceDescriptors(
248 List<CallServiceDescriptor> selectedCallServiceDescriptors) {
249
250 if (selectedCallServiceDescriptors == null || selectedCallServiceDescriptors.isEmpty()) {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800251 attemptNextSelector();
Ben Giladc5b22692014-02-18 20:03:22 -0800252 } else if (mCallServiceDescriptorIterator == null) {
253 mCallServiceDescriptorIterator = selectedCallServiceDescriptors.iterator();
Ben Gilad0bf5b912014-01-28 17:55:57 -0800254 attemptNextCallService();
255 }
256 }
257
258 /**
Ben Giladc5b22692014-02-18 20:03:22 -0800259 * Attempts to place the call using the call service specified by the next call-service
260 * descriptor of mCallServiceDescriptorIterator. If there are no more call services to
261 * attempt, the process continues to the next call-service selector via
262 * {@link #attemptNextSelector}.
Ben Gilad0bf5b912014-01-28 17:55:57 -0800263 */
264 private void attemptNextCallService() {
265 if (mIsAborted) {
266 return;
267 }
268
Ben Giladc5b22692014-02-18 20:03:22 -0800269 if (mCallServiceDescriptorIterator.hasNext()) {
270 CallServiceDescriptor descriptor = mCallServiceDescriptorIterator.next();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800271 final CallServiceWrapper callService =
272 mCallServicesById.get(descriptor.getCallServiceId());
273
274 if (callService == null) {
Santos Cordon681663d2014-01-30 04:32:15 -0800275 attemptNextCallService();
276 } else {
Sailesh Nepalb6141ae2014-02-18 08:45:26 -0800277 BindCallback callback = new BindCallback() {
278 @Override public void onSuccess() {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800279 callService.call(mCall.toCallInfo());
Sailesh Nepalb6141ae2014-02-18 08:45:26 -0800280 }
281 @Override public void onFailure() {
Ben Giladb07a9d52014-02-24 12:12:25 -0800282 attemptNextCallService();
Sailesh Nepalb6141ae2014-02-18 08:45:26 -0800283 }
284 };
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800285
286 mCall.setCallService(callService);
287
Santos Cordon493e8f22014-02-19 03:15:12 -0800288 // TODO(santoscordon): Consider making bind private to CallServiceWrapper and having
289 // CSWrapper.call() do the bind automatically.
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800290 callService.bind(callback);
Ben Gilad0bf5b912014-01-28 17:55:57 -0800291 }
292 } else {
Ben Giladc5b22692014-02-18 20:03:22 -0800293 mCallServiceDescriptorIterator = null;
Ben Gilad0bf5b912014-01-28 17:55:57 -0800294 attemptNextSelector();
295 }
296 }
Ben Gilad0bf5b912014-01-28 17:55:57 -0800297}