blob: d6d24bccd01f3b4d9e285c3516b8018e5437abc6 [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;
21import com.google.common.collect.Sets;
22
23import android.os.RemoteException;
24import android.telecomm.CallInfo;
25import android.telecomm.ICallService;
26import android.telecomm.ICallServiceSelector;
27
28import java.util.Iterator;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32
33/**
34 * Utility class to place a call using the specified set of call-services and ordered selectors.
35 * Iterates through the selectors and gets a sorted list of supported call service IDs for each
36 * selector. Upon receiving each sorted list (one list per selector), each of the corresponding
37 * call services is then attempted until either the outgoing call is placed, the attempted call
38 * is aborted (by the switchboard), or the list is exhausted -- whichever occurs first.
39 *
40 * Except for the abort case, all other scenarios should terminate with the switchboard notified.
41 */
42final class OutgoingCallProcessor {
43
44 /**
45 * The (singleton) Telecomm switchboard instance.
46 */
47 private final Switchboard mSwitchboard;
48
49 /**
50 * The outgoing call this processor is tasked with placing.
51 */
52 private final Call mCall;
53
54 /**
55 * The (read-only) object derived from mCall above to pass through outside of the Telecomm
56 * package.
57 */
58 private final CallInfo mCallInfo;
59
60 /**
61 * The set of currently-available call-service IDs.
62 */
63 private final Set<String> mCallServiceIds = Sets.newHashSet();
64
65 /**
66 * The map of currently-available call-service implementations keyed by call-service ID.
67 */
68 private final Map<String, ICallService> mCallServicesById = Maps.newHashMap();
69
70 /**
71 * The set of currently-available call-service selector implementations.
72 */
73 private final List<ICallServiceSelector> mSelectors;
74
75 /**
76 * The iterator over the currently-selected ordered list of call-service IDs.
77 */
78 private Iterator<String> mCallServiceIdIterator;
79
80 private Iterator<ICallServiceSelector> mSelectorIterator;
81
82 private boolean mIsAborted = false;
83
84 /**
85 * Persists the specified parameters and iterates through the prioritized list of selectors
86 * passing to each selector (read-only versions of) the call object and all available call-
87 * service IDs. Stops once a matching selector is found. Calls with no matching selectors
88 * will eventually be killed by the cleanup/monitor switchboard handler, which will in turn
89 * call the abort method of this processor.
90 *
91 * @param call The call to place.
92 * @param callServices The available call-service implementations.
93 * @param selectors The available call-service selector implementations.
94 * @param switchboard The switchboard for this processor to work against.
95 */
96 OutgoingCallProcessor(
97 Call call,
98 Set<ICallService> callServices,
99 List<ICallServiceSelector> selectors,
100 Switchboard switchboard) {
101
102 Preconditions.checkNotNull(callServices);
103 Preconditions.checkNotNull(selectors);
104
105 mCall = call;
106 mCallInfo = new CallInfo(call.getId(), call.getHandle());
107 mSelectors = selectors;
108 mSwitchboard = switchboard;
109
110 // Populate the list and map of call-service IDs. The list is needed since
111 // it's being passed down to selectors.
112 for (ICallService callService : callServices) {
113 // TOOD(gilad): Implement callService.getId() and use that instead.
114 String id = "xyz";
115 mCallServiceIds.add(id);
116 mCallServicesById.put(id, callService);
117 }
118 }
119
120 /**
121 * Initiates the attempt to place the call. No-op beyond the first invocation.
122 */
123 void process() {
124 if (mSelectors.isEmpty() || mCallServiceIds.isEmpty()) {
125 // TODO(gilad): Consider adding a failure message/type to differentiate the various
126 // cases, or potentially throw an exception in this case.
127 mSwitchboard.handleFailedOutgoingCall(mCall);
128 } else if (mSelectorIterator == null) {
129 mSelectorIterator = mSelectors.iterator();
130 attemptNextSelector();
131 }
132 }
133
134 /**
135 * Aborts the attempt to place the relevant call. Intended to be invoked by
136 * switchboard.
137 */
138 void abort() {
139 mIsAborted = true;
140 }
141
142 /**
143 * Attempts to place the call using the next selector, no-op if no other selectors
144 * are available.
145 */
146 private void attemptNextSelector() {
147 if (mIsAborted) {
148 return;
149 }
150
151 if (mSelectorIterator.hasNext()) {
152 ICallServiceSelector selector = mSelectorIterator.next();
153
154 // TODO(gilad): Refactor to pass (CallInfo, List<String>, response) passing
155 // mCallInfo as the 1st and mCallServiceIds (or an immutable version thereof)
156 // as the 2nd parameter.
157 // selector.select(handle, callServiceBinders, response);
158
159 // TODO(gilad): Get the list of call-service IDs (asynchronically), store it
160 // (in setSelectedCallServiceIds), and then invoke attemptNextCallService.
161
162 // NOTE(gilad): Currently operating under the assumption that we'll have one timeout
163 // per (outgoing) call attempt. If we (also) like to timeout individual selectors
164 // and/or call services, the code here will need to be refactored (quite a bit) to
165 // support that.
166
167 } else {
168 mSwitchboard.handleFailedOutgoingCall(mCall);
169 }
170 }
171
172 /**
173 * Persists the ordered-list of call service IDs as selected by the current selector and
174 * starts iterating through the corresponding call services in the continuing attempt to
175 * place the call.
176 * TODO(gilad): Get this to be invoked upon selector.select responses.
177 *
178 * @selectedCallServiceIds The (ordered) list of call service IDs.
179 */
180 private void setSelectedCallServiceIds(List<String> selectedCallServiceIds) {
181 if (selectedCallServiceIds == null || selectedCallServiceIds.isEmpty()) {
182 attemptNextSelector();
183 } else if (mCallServiceIdIterator == null) {
184 mCallServiceIdIterator = selectedCallServiceIds.iterator();
185 attemptNextCallService();
186 }
187 }
188
189 /**
190 * TODO(gilad): Add comment.
191 */
192 private void attemptNextCallService() {
193 if (mIsAborted) {
194 return;
195 }
196
197 if (mCallServiceIdIterator.hasNext()) {
198 String id = mCallServiceIdIterator.next();
199 ICallService callService = mCallServicesById.get(id);
200 if (callService != null) {
201 try {
202 // TODO(gilad): Refactor to pass a CallInfo object instead.
203 callService.call(mCallInfo);
204 } catch (RemoteException e) {
205 // TODO(gilad): Log etc.
206 attemptNextCallService();
207 }
208 }
209 } else {
210 mCallServiceIdIterator = null;
211 attemptNextSelector();
212 }
213 }
214
215 /**
216 * Handles the successful outgoing-call case.
217 */
218 private void handleSuccessfulOutgoingCall() {
219 // TODO(gilad): More here?
220
221 abort(); // Shouldn't be necessary but better safe than sorry.
222 mSwitchboard.handleSuccessfulOutgoingCall(mCall);
223 }
224
225 /**
226 * Handles the failed outgoing-call case.
227 */
228 private void handleFailedOutgoingCall() {
229 // TODO(gilad): Implement.
230 attemptNextCallService();
231 }
232}