blob: 22ec3c697a6e857e5336d1a0b152f9cd1c87d892 [file] [log] [blame]
Sailesh Nepal664837f2014-07-14 16:31:51 -07001/*
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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Sailesh Nepal664837f2014-07-14 16:31:51 -070018
Tyler Gunn91d43cf2014-09-17 12:19:39 -070019import android.content.Context;
Tony Mak240656f2015-12-04 11:36:22 +000020import android.os.UserHandle;
Andrew Lee701dc002014-09-11 21:29:12 -070021import android.telecom.DisconnectCause;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070022import android.telecom.ParcelableConnection;
23import android.telecom.PhoneAccount;
24import android.telecom.PhoneAccountHandle;
Sailesh Nepal664837f2014-07-14 16:31:51 -070025
Tyler Gunn91d43cf2014-09-17 12:19:39 -070026// TODO: Needed for move to system service: import com.android.internal.R;
27
Brad Ebingerf1900072015-11-12 17:25:06 -080028import com.android.internal.annotations.VisibleForTesting;
29
Sailesh Nepal664837f2014-07-14 16:31:51 -070030import java.util.ArrayList;
Sailesh Nepal646fa3d2015-01-28 02:55:36 -080031import java.util.Collection;
32import java.util.HashSet;
Sailesh Nepal664837f2014-07-14 16:31:51 -070033import java.util.Iterator;
34import java.util.List;
Ihab Awad293edf22014-07-24 17:52:29 -070035import java.util.Objects;
Sailesh Nepal664837f2014-07-14 16:31:51 -070036
37/**
Yorke Lee9250e5f2014-10-01 13:39:09 -070038 * This class creates connections to place new outgoing calls or to attach to an existing incoming
Sailesh Nepal664837f2014-07-14 16:31:51 -070039 * call. In either case, this class cycles through a set of connection services until:
40 * - a connection service returns a newly created connection in which case the call is displayed
41 * to the user
42 * - a connection service cancels the process, in which case the call is aborted
43 */
Brad Ebingerf1900072015-11-12 17:25:06 -080044@VisibleForTesting
45public class CreateConnectionProcessor implements CreateConnectionResponse {
Ihab Awad293edf22014-07-24 17:52:29 -070046
47 // Describes information required to attempt to make a phone call
48 private static class CallAttemptRecord {
49 // The PhoneAccount describing the target connection service which we will
50 // contact in order to process an attempt
Ihab Awadb78b2762014-07-25 15:16:23 -070051 public final PhoneAccountHandle connectionManagerPhoneAccount;
Ihab Awad293edf22014-07-24 17:52:29 -070052 // The PhoneAccount which we will tell the target connection service to use
53 // for attempting to make the actual phone call
Ihab Awadb78b2762014-07-25 15:16:23 -070054 public final PhoneAccountHandle targetPhoneAccount;
Ihab Awad293edf22014-07-24 17:52:29 -070055
56 public CallAttemptRecord(
Ihab Awadb78b2762014-07-25 15:16:23 -070057 PhoneAccountHandle connectionManagerPhoneAccount,
58 PhoneAccountHandle targetPhoneAccount) {
59 this.connectionManagerPhoneAccount = connectionManagerPhoneAccount;
60 this.targetPhoneAccount = targetPhoneAccount;
Ihab Awad293edf22014-07-24 17:52:29 -070061 }
62
63 @Override
64 public String toString() {
65 return "CallAttemptRecord("
Ihab Awadb78b2762014-07-25 15:16:23 -070066 + Objects.toString(connectionManagerPhoneAccount) + ","
67 + Objects.toString(targetPhoneAccount) + ")";
Ihab Awad293edf22014-07-24 17:52:29 -070068 }
Tyler Gunn6e6f6d12014-08-20 15:22:18 -070069
70 /**
71 * Determines if this instance of {@code CallAttemptRecord} has the same underlying
72 * {@code PhoneAccountHandle}s as another instance.
73 *
74 * @param obj The other instance to compare against.
75 * @return {@code True} if the {@code CallAttemptRecord}s are equal.
76 */
77 @Override
78 public boolean equals(Object obj) {
79 if (obj instanceof CallAttemptRecord) {
80 CallAttemptRecord other = (CallAttemptRecord) obj;
81 return Objects.equals(connectionManagerPhoneAccount,
82 other.connectionManagerPhoneAccount) &&
83 Objects.equals(targetPhoneAccount, other.targetPhoneAccount);
84 }
85 return false;
86 }
Ihab Awad293edf22014-07-24 17:52:29 -070087 }
88
Sailesh Nepal664837f2014-07-14 16:31:51 -070089 private final Call mCall;
90 private final ConnectionServiceRepository mRepository;
Ihab Awad293edf22014-07-24 17:52:29 -070091 private List<CallAttemptRecord> mAttemptRecords;
92 private Iterator<CallAttemptRecord> mAttemptRecordIterator;
Brad Ebingerf1900072015-11-12 17:25:06 -080093 private CreateConnectionResponse mCallResponse;
Andrew Lee701dc002014-09-11 21:29:12 -070094 private DisconnectCause mLastErrorDisconnectCause;
Tyler Gunn91d43cf2014-09-17 12:19:39 -070095 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
96 private final Context mContext;
Sailesh Nepal646fa3d2015-01-28 02:55:36 -080097 private CreateConnectionTimeout mTimeout;
Brad Ebingerf1900072015-11-12 17:25:06 -080098 private ConnectionServiceWrapper mService;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -070099 private int mConnectionAttempt;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700100
Brad Ebingerf1900072015-11-12 17:25:06 -0800101 @VisibleForTesting
102 public CreateConnectionProcessor(
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700103 Call call, ConnectionServiceRepository repository, CreateConnectionResponse response,
104 PhoneAccountRegistrar phoneAccountRegistrar, Context context) {
Ihab Awad8de76912015-02-17 12:25:52 -0800105 Log.v(this, "CreateConnectionProcessor created for Call = %s", call);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700106 mCall = call;
107 mRepository = repository;
Brad Ebingerf1900072015-11-12 17:25:06 -0800108 mCallResponse = response;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700109 mPhoneAccountRegistrar = phoneAccountRegistrar;
110 mContext = context;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700111 mConnectionAttempt = 0;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700112 }
113
Sailesh Nepal752cacb2014-11-06 12:35:26 -0800114 boolean isProcessingComplete() {
Brad Ebingerf1900072015-11-12 17:25:06 -0800115 return mCallResponse == null;
Sailesh Nepal752cacb2014-11-06 12:35:26 -0800116 }
117
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800118 boolean isCallTimedOut() {
119 return mTimeout != null && mTimeout.isCallTimedOut();
120 }
121
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700122 public int getConnectionAttempt() {
123 return mConnectionAttempt;
124 }
125
Brad Ebingerf1900072015-11-12 17:25:06 -0800126 @VisibleForTesting
127 public void process() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700128 Log.v(this, "process");
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800129 clearTimeout();
Ihab Awad293edf22014-07-24 17:52:29 -0700130 mAttemptRecords = new ArrayList<>();
Ihab Awadb78b2762014-07-25 15:16:23 -0700131 if (mCall.getTargetPhoneAccount() != null) {
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700132 mAttemptRecords.add(new CallAttemptRecord(
133 mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
Sailesh Nepal664837f2014-07-14 16:31:51 -0700134 }
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700135 adjustAttemptsForConnectionManager();
Brad Ebingere6c481a2016-05-12 14:13:26 -0700136 adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
Ihab Awad293edf22014-07-24 17:52:29 -0700137 mAttemptRecordIterator = mAttemptRecords.iterator();
Ihab Awad69eb0f52014-07-18 11:20:37 -0700138 attemptNextPhoneAccount();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700139 }
140
Sailesh Nepal752cacb2014-11-06 12:35:26 -0800141 boolean hasMorePhoneAccounts() {
142 return mAttemptRecordIterator.hasNext();
143 }
144
145 void continueProcessingIfPossible(CreateConnectionResponse response,
146 DisconnectCause disconnectCause) {
147 Log.v(this, "continueProcessingIfPossible");
Brad Ebingerf1900072015-11-12 17:25:06 -0800148 mCallResponse = response;
Sailesh Nepal752cacb2014-11-06 12:35:26 -0800149 mLastErrorDisconnectCause = disconnectCause;
150 attemptNextPhoneAccount();
151 }
152
Sailesh Nepal664837f2014-07-14 16:31:51 -0700153 void abort() {
154 Log.v(this, "abort");
155
156 // Clear the response first to prevent attemptNextConnectionService from attempting any
157 // more services.
Brad Ebingerf1900072015-11-12 17:25:06 -0800158 CreateConnectionResponse response = mCallResponse;
159 mCallResponse = null;
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800160 clearTimeout();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700161
162 ConnectionServiceWrapper service = mCall.getConnectionService();
163 if (service != null) {
164 service.abort(mCall);
165 mCall.clearConnectionService();
166 }
167 if (response != null) {
Andrew Lee701dc002014-09-11 21:29:12 -0700168 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.LOCAL));
Sailesh Nepal664837f2014-07-14 16:31:51 -0700169 }
170 }
171
Ihab Awad69eb0f52014-07-18 11:20:37 -0700172 private void attemptNextPhoneAccount() {
173 Log.v(this, "attemptNextPhoneAccount");
Tyler Gunncb59b672014-08-20 09:02:11 -0700174 CallAttemptRecord attempt = null;
175 if (mAttemptRecordIterator.hasNext()) {
176 attempt = mAttemptRecordIterator.next();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700177
Santos Cordon6a212642015-05-08 16:35:23 -0700178 if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700179 attempt.connectionManagerPhoneAccount)) {
Tyler Gunncb59b672014-08-20 09:02:11 -0700180 Log.w(this,
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700181 "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for "
182 + "attempt: %s", attempt);
Tyler Gunncb59b672014-08-20 09:02:11 -0700183 attemptNextPhoneAccount();
184 return;
185 }
186
187 // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700188 // also requires the BIND_TELECOM_CONNECTION_SERVICE permission.
Tyler Gunncb59b672014-08-20 09:02:11 -0700189 if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) &&
Santos Cordon6a212642015-05-08 16:35:23 -0700190 !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
191 attempt.targetPhoneAccount)) {
Tyler Gunncb59b672014-08-20 09:02:11 -0700192 Log.w(this,
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700193 "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for "
194 + "attempt: %s", attempt);
Tyler Gunncb59b672014-08-20 09:02:11 -0700195 attemptNextPhoneAccount();
196 return;
197 }
198 }
199
Brad Ebingerf1900072015-11-12 17:25:06 -0800200 if (mCallResponse != null && attempt != null) {
Ihab Awad293edf22014-07-24 17:52:29 -0700201 Log.i(this, "Trying attempt %s", attempt);
Evan Charlton105d9772014-11-25 14:08:53 -0800202 PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
Brad Ebingerf1900072015-11-12 17:25:06 -0800203 mService = mRepository.getService(phoneAccount.getComponentName(),
204 phoneAccount.getUserHandle());
205 if (mService == null) {
Ihab Awad293edf22014-07-24 17:52:29 -0700206 Log.i(this, "Found no connection service for attempt %s", attempt);
Ihab Awad69eb0f52014-07-18 11:20:37 -0700207 attemptNextPhoneAccount();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700208 } else {
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700209 mConnectionAttempt++;
Ihab Awadb78b2762014-07-25 15:16:23 -0700210 mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
211 mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
Brad Ebingerf1900072015-11-12 17:25:06 -0800212 mCall.setConnectionService(mService);
213 setTimeoutIfNeeded(mService, attempt);
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800214
Brad Ebingerf1900072015-11-12 17:25:06 -0800215 mService.createConnection(mCall, this);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700216 }
217 } else {
Ihab Awad69eb0f52014-07-18 11:20:37 -0700218 Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
Brad Ebingerf1900072015-11-12 17:25:06 -0800219 DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
220 mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
221 notifyCallConnectionFailure(disconnectCause);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700222 }
223 }
224
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800225 private void setTimeoutIfNeeded(ConnectionServiceWrapper service, CallAttemptRecord attempt) {
226 clearTimeout();
227
228 CreateConnectionTimeout timeout = new CreateConnectionTimeout(
229 mContext, mPhoneAccountRegistrar, service, mCall);
230 if (timeout.isTimeoutNeededForCall(getConnectionServices(mAttemptRecords),
231 attempt.connectionManagerPhoneAccount)) {
232 mTimeout = timeout;
233 timeout.registerTimeout();
234 }
235 }
236
237 private void clearTimeout() {
238 if (mTimeout != null) {
239 mTimeout.unregisterTimeout();
240 mTimeout = null;
241 }
242 }
243
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700244 private boolean shouldSetConnectionManager() {
245 if (mAttemptRecords.size() == 0) {
246 return false;
Ihab Awad293edf22014-07-24 17:52:29 -0700247 }
Ihab Awadc17294c2014-08-04 19:23:37 -0700248
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700249 if (mAttemptRecords.size() > 1) {
250 Log.d(this, "shouldSetConnectionManager, error, mAttemptRecords should not have more "
251 + "than 1 record");
252 return false;
253 }
254
Tony Mak240656f2015-12-04 11:36:22 +0000255 PhoneAccountHandle connectionManager =
256 mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall);
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700257 if (connectionManager == null) {
258 return false;
259 }
260
261 PhoneAccountHandle targetPhoneAccountHandle = mAttemptRecords.get(0).targetPhoneAccount;
262 if (Objects.equals(connectionManager, targetPhoneAccountHandle)) {
263 return false;
264 }
265
266 // Connection managers are only allowed to manage SIM subscriptions.
Santos Cordon6a212642015-05-08 16:35:23 -0700267 // TODO: Should this really be checking the "calling user" test for phone account?
Tony Mak240656f2015-12-04 11:36:22 +0000268 PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar
269 .getPhoneAccountUnchecked(targetPhoneAccountHandle);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800270 if (targetPhoneAccount == null) {
271 Log.d(this, "shouldSetConnectionManager, phone account not found");
272 return false;
273 }
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700274 boolean isSimSubscription = (targetPhoneAccount.getCapabilities() &
275 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0;
276 if (!isSimSubscription) {
277 return false;
278 }
279
280 return true;
281 }
282
283 // If there exists a registered connection manager then use it.
284 private void adjustAttemptsForConnectionManager() {
285 if (shouldSetConnectionManager()) {
286 CallAttemptRecord record = new CallAttemptRecord(
Tony Mak240656f2015-12-04 11:36:22 +0000287 mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall),
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700288 mAttemptRecords.get(0).targetPhoneAccount);
mike dooley10a58312014-11-06 13:46:19 -0800289 Log.v(this, "setConnectionManager, changing %s -> %s", mAttemptRecords.get(0), record);
Brad Ebingerf1900072015-11-12 17:25:06 -0800290 mAttemptRecords.add(0, record);
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700291 } else {
292 Log.v(this, "setConnectionManager, not changing");
Ihab Awad293edf22014-07-24 17:52:29 -0700293 }
294 }
295
Sailesh Nepal664837f2014-07-14 16:31:51 -0700296 // If we are possibly attempting to call a local emergency number, ensure that the
Ihab Awad69eb0f52014-07-18 11:20:37 -0700297 // plain PSTN connection services are listed, and nothing else.
Brad Ebingere6c481a2016-05-12 14:13:26 -0700298 private void adjustAttemptsForEmergency(PhoneAccountHandle preferredPAH) {
Tyler Gunn6ffe5312015-08-12 08:19:20 -0700299 if (mCall.isEmergencyCall()) {
Ihab Awad69eb0f52014-07-18 11:20:37 -0700300 Log.i(this, "Emergency number detected");
Ihab Awad293edf22014-07-24 17:52:29 -0700301 mAttemptRecords.clear();
Tony Mak240656f2015-12-04 11:36:22 +0000302 // Phone accounts in profile do not handle emergency call, use phone accounts in
303 // current user.
304 List<PhoneAccount> allAccounts = mPhoneAccountRegistrar
305 .getAllPhoneAccountsOfCurrentUser();
Yorke Leec1272112014-10-16 09:50:00 -0700306
307 if (allAccounts.isEmpty()) {
308 // If the list of phone accounts is empty at this point, it means Telephony hasn't
309 // registered any phone accounts yet. Add a fallback emergency phone account so
310 // that emergency calls can still go through. We create a new ArrayLists here just
311 // in case the implementation of PhoneAccountRegistrar ever returns an unmodifiable
312 // list.
313 allAccounts = new ArrayList<PhoneAccount>();
314 allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount());
315 }
316
Brad Ebingere6c481a2016-05-12 14:13:26 -0700317 // First, possibly add the SIM phone account that the user prefers
318 PhoneAccount preferredPA = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
319 preferredPAH);
320 if (preferredPA != null &&
321 preferredPA.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
322 preferredPA.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
323 Log.i(this, "Will try PSTN account %s for emergency",
324 preferredPA.getAccountHandle());
325 mAttemptRecords.add(new CallAttemptRecord(preferredPAH, preferredPAH));
326 }
327
328 // Next, add all SIM phone accounts which can place emergency calls.
329 TelephonyUtil.sortSimPhoneAccounts(mContext, allAccounts);
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700330 for (PhoneAccount phoneAccount : allAccounts) {
331 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
332 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
Brad Ebingere6c481a2016-05-12 14:13:26 -0700333 PhoneAccountHandle phoneAccountHandle = phoneAccount.getAccountHandle();
334 // Don't add the preferred account since it has already been added previously.
335 if (!phoneAccountHandle.equals(preferredPAH)) {
336 Log.i(this, "Will try PSTN account %s for emergency", phoneAccountHandle);
337 mAttemptRecords.add(new CallAttemptRecord(phoneAccountHandle,
338 phoneAccountHandle));
339 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700340 }
341 }
Tyler Gunn6e6f6d12014-08-20 15:22:18 -0700342
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700343 // Next, add the connection manager account as a backup if it can place emergency calls.
Tony Mak240656f2015-12-04 11:36:22 +0000344 PhoneAccountHandle callManagerHandle =
345 mPhoneAccountRegistrar.getSimCallManagerOfCurrentUser();
Brad Ebingerf1900072015-11-12 17:25:06 -0800346 if (callManagerHandle != null) {
347 // TODO: Should this really be checking the "calling user" test for phone account?
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700348 PhoneAccount callManager = mPhoneAccountRegistrar
Tony Mak240656f2015-12-04 11:36:22 +0000349 .getPhoneAccountUnchecked(callManagerHandle);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800350 if (callManager != null && callManager.hasCapabilities(
351 PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700352 CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
Tony Mak240656f2015-12-04 11:36:22 +0000353 mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
354 mCall.getHandle().getScheme()));
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700355 if (!mAttemptRecords.contains(callAttemptRecord)) {
356 Log.i(this, "Will try Connection Manager account %s for emergency",
357 callManager);
358 mAttemptRecords.add(callAttemptRecord);
359 }
360 }
Tyler Gunn6e6f6d12014-08-20 15:22:18 -0700361 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700362 }
363 }
364
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800365 /** Returns all connection services used by the call attempt records. */
366 private static Collection<PhoneAccountHandle> getConnectionServices(
367 List<CallAttemptRecord> records) {
368 HashSet<PhoneAccountHandle> result = new HashSet<>();
369 for (CallAttemptRecord record : records) {
370 result.add(record.connectionManagerPhoneAccount);
371 }
372 return result;
373 }
374
Tony Mak240656f2015-12-04 11:36:22 +0000375
Brad Ebingerf1900072015-11-12 17:25:06 -0800376 private void notifyCallConnectionFailure(DisconnectCause errorDisconnectCause) {
377 if (mCallResponse != null) {
378 clearTimeout();
379 mCallResponse.handleCreateConnectionFailure(errorDisconnectCause);
380 mCallResponse = null;
381 mCall.clearConnectionService();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700382 }
Brad Ebingerf1900072015-11-12 17:25:06 -0800383 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700384
Brad Ebingerf1900072015-11-12 17:25:06 -0800385 @Override
386 public void handleCreateConnectionSuccess(
387 CallIdMapper idMapper,
388 ParcelableConnection connection) {
389 if (mCallResponse == null) {
390 // Nobody is listening for this connection attempt any longer; ask the responsible
391 // ConnectionService to tear down any resources associated with the call
392 mService.abort(mCall);
393 } else {
394 // Success -- share the good news and remember that we are no longer interested
395 // in hearing about any more attempts
396 mCallResponse.handleCreateConnectionSuccess(idMapper, connection);
397 mCallResponse = null;
398 // If there's a timeout running then don't clear it. The timeout can be triggered
399 // after the call has successfully been created but before it has become active.
Sailesh Nepal664837f2014-07-14 16:31:51 -0700400 }
Brad Ebingerf1900072015-11-12 17:25:06 -0800401 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700402
Brad Ebingerf1900072015-11-12 17:25:06 -0800403 private boolean shouldFailCallIfConnectionManagerFails(DisconnectCause cause) {
404 // Connection Manager does not exist or does not match registered Connection Manager
405 // Since Connection manager is a proxy for SIM, fall back to SIM
406 PhoneAccountHandle handle = mCall.getConnectionManagerPhoneAccount();
407 if (handle == null || !handle.equals(mPhoneAccountRegistrar.getSimCallManagerFromCall(
408 mCall))) {
Sailesh Nepalbafadce2014-11-05 18:40:21 -0800409 return false;
410 }
411
Brad Ebingerf1900072015-11-12 17:25:06 -0800412 // The Call's Connection Service does not exist
413 ConnectionServiceWrapper connectionManager = mCall.getConnectionService();
414 if (connectionManager == null) {
415 return true;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700416 }
Brad Ebingerf1900072015-11-12 17:25:06 -0800417
418 // In this case, fall back to a sim because connection manager declined
419 if (cause.getCode() == DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED) {
420 Log.d(CreateConnectionProcessor.this, "Connection manager declined to handle the "
421 + "call, falling back to not using a connection manager");
422 return false;
423 }
424
425 if (!connectionManager.isServiceValid("createConnection")) {
426 Log.d(CreateConnectionProcessor.this, "Connection manager unbound while trying "
427 + "create a connection, falling back to not using a connection manager");
428 return false;
429 }
430
431 // Do not fall back from connection manager and simply fail call if the failure reason is
432 // other
433 Log.d(CreateConnectionProcessor.this, "Connection Manager denied call with the following " +
434 "error: " + cause.getReason() + ". Not falling back to SIM.");
435 return true;
436 }
437
438 @Override
439 public void handleCreateConnectionFailure(DisconnectCause errorDisconnectCause) {
440 // Failure of some sort; record the reasons for failure and try again if possible
441 Log.d(CreateConnectionProcessor.this, "Connection failed: (%s)", errorDisconnectCause);
Brad Ebingere6c481a2016-05-12 14:13:26 -0700442 if (shouldFailCallIfConnectionManagerFails(errorDisconnectCause)) {
Brad Ebingerf1900072015-11-12 17:25:06 -0800443 notifyCallConnectionFailure(errorDisconnectCause);
444 return;
445 }
Santos Cordon8ff99f32016-02-12 15:30:44 -0800446 mLastErrorDisconnectCause = errorDisconnectCause;
Brad Ebingerf1900072015-11-12 17:25:06 -0800447 attemptNextPhoneAccount();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700448 }
449}