blob: 7eb3801ec923aa493af4b1429bb1e55f84c8b24f [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;
Andrew Lee701dc002014-09-11 21:29:12 -070020import android.telecom.DisconnectCause;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070021import android.telecom.Log;
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 }
Tyler Gunna90ba732017-01-26 07:24:08 -0800135 if (!mCall.isSelfManaged()) {
136 adjustAttemptsForConnectionManager();
137 adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
138 }
Ihab Awad293edf22014-07-24 17:52:29 -0700139 mAttemptRecordIterator = mAttemptRecords.iterator();
Ihab Awad69eb0f52014-07-18 11:20:37 -0700140 attemptNextPhoneAccount();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700141 }
142
Sailesh Nepal752cacb2014-11-06 12:35:26 -0800143 boolean hasMorePhoneAccounts() {
144 return mAttemptRecordIterator.hasNext();
145 }
146
147 void continueProcessingIfPossible(CreateConnectionResponse response,
148 DisconnectCause disconnectCause) {
149 Log.v(this, "continueProcessingIfPossible");
Brad Ebingerf1900072015-11-12 17:25:06 -0800150 mCallResponse = response;
Sailesh Nepal752cacb2014-11-06 12:35:26 -0800151 mLastErrorDisconnectCause = disconnectCause;
152 attemptNextPhoneAccount();
153 }
154
Sailesh Nepal664837f2014-07-14 16:31:51 -0700155 void abort() {
156 Log.v(this, "abort");
157
158 // Clear the response first to prevent attemptNextConnectionService from attempting any
159 // more services.
Brad Ebingerf1900072015-11-12 17:25:06 -0800160 CreateConnectionResponse response = mCallResponse;
161 mCallResponse = null;
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800162 clearTimeout();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700163
164 ConnectionServiceWrapper service = mCall.getConnectionService();
165 if (service != null) {
166 service.abort(mCall);
167 mCall.clearConnectionService();
168 }
169 if (response != null) {
Andrew Lee701dc002014-09-11 21:29:12 -0700170 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.LOCAL));
Sailesh Nepal664837f2014-07-14 16:31:51 -0700171 }
172 }
173
Ihab Awad69eb0f52014-07-18 11:20:37 -0700174 private void attemptNextPhoneAccount() {
175 Log.v(this, "attemptNextPhoneAccount");
Tyler Gunncb59b672014-08-20 09:02:11 -0700176 CallAttemptRecord attempt = null;
177 if (mAttemptRecordIterator.hasNext()) {
178 attempt = mAttemptRecordIterator.next();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700179
Santos Cordon6a212642015-05-08 16:35:23 -0700180 if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700181 attempt.connectionManagerPhoneAccount)) {
Tyler Gunncb59b672014-08-20 09:02:11 -0700182 Log.w(this,
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700183 "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for "
184 + "attempt: %s", attempt);
Tyler Gunncb59b672014-08-20 09:02:11 -0700185 attemptNextPhoneAccount();
186 return;
187 }
188
189 // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700190 // also requires the BIND_TELECOM_CONNECTION_SERVICE permission.
Tyler Gunncb59b672014-08-20 09:02:11 -0700191 if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) &&
Santos Cordon6a212642015-05-08 16:35:23 -0700192 !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
193 attempt.targetPhoneAccount)) {
Tyler Gunncb59b672014-08-20 09:02:11 -0700194 Log.w(this,
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700195 "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for "
196 + "attempt: %s", attempt);
Tyler Gunncb59b672014-08-20 09:02:11 -0700197 attemptNextPhoneAccount();
198 return;
199 }
200 }
201
Brad Ebingerf1900072015-11-12 17:25:06 -0800202 if (mCallResponse != null && attempt != null) {
Ihab Awad293edf22014-07-24 17:52:29 -0700203 Log.i(this, "Trying attempt %s", attempt);
Evan Charlton105d9772014-11-25 14:08:53 -0800204 PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
Brad Ebingerf1900072015-11-12 17:25:06 -0800205 mService = mRepository.getService(phoneAccount.getComponentName(),
206 phoneAccount.getUserHandle());
207 if (mService == null) {
Ihab Awad293edf22014-07-24 17:52:29 -0700208 Log.i(this, "Found no connection service for attempt %s", attempt);
Ihab Awad69eb0f52014-07-18 11:20:37 -0700209 attemptNextPhoneAccount();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700210 } else {
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700211 mConnectionAttempt++;
Ihab Awadb78b2762014-07-25 15:16:23 -0700212 mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
213 mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
Brad Ebingerf1900072015-11-12 17:25:06 -0800214 mCall.setConnectionService(mService);
215 setTimeoutIfNeeded(mService, attempt);
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800216 if (mCall.isIncoming()) {
217 mService.createConnection(mCall, CreateConnectionProcessor.this);
218 } else {
219 // Start to create the connection for outgoing call after the ConnectionService
220 // of the call has gained the focus.
221 mCall.getConnectionServiceFocusManager().requestFocus(
222 mCall,
223 new CallsManager.RequestCallback(new CallsManager.PendingAction() {
224 @Override
225 public void performAction() {
226 Log.d(this, "perform create connection");
227 mService.createConnection(
228 mCall,
229 CreateConnectionProcessor.this);
230 }
231 }));
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800232
Pengquan Meng606ff7f2017-12-20 16:13:04 -0800233 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700234 }
235 } else {
Ihab Awad69eb0f52014-07-18 11:20:37 -0700236 Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
Brad Ebingerf1900072015-11-12 17:25:06 -0800237 DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
238 mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
239 notifyCallConnectionFailure(disconnectCause);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700240 }
241 }
242
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800243 private void setTimeoutIfNeeded(ConnectionServiceWrapper service, CallAttemptRecord attempt) {
244 clearTimeout();
245
246 CreateConnectionTimeout timeout = new CreateConnectionTimeout(
247 mContext, mPhoneAccountRegistrar, service, mCall);
248 if (timeout.isTimeoutNeededForCall(getConnectionServices(mAttemptRecords),
249 attempt.connectionManagerPhoneAccount)) {
250 mTimeout = timeout;
251 timeout.registerTimeout();
252 }
253 }
254
255 private void clearTimeout() {
256 if (mTimeout != null) {
257 mTimeout.unregisterTimeout();
258 mTimeout = null;
259 }
260 }
261
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700262 private boolean shouldSetConnectionManager() {
263 if (mAttemptRecords.size() == 0) {
264 return false;
Ihab Awad293edf22014-07-24 17:52:29 -0700265 }
Ihab Awadc17294c2014-08-04 19:23:37 -0700266
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700267 if (mAttemptRecords.size() > 1) {
268 Log.d(this, "shouldSetConnectionManager, error, mAttemptRecords should not have more "
269 + "than 1 record");
270 return false;
271 }
272
Tony Mak240656f2015-12-04 11:36:22 +0000273 PhoneAccountHandle connectionManager =
274 mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall);
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700275 if (connectionManager == null) {
276 return false;
277 }
278
279 PhoneAccountHandle targetPhoneAccountHandle = mAttemptRecords.get(0).targetPhoneAccount;
280 if (Objects.equals(connectionManager, targetPhoneAccountHandle)) {
281 return false;
282 }
283
284 // Connection managers are only allowed to manage SIM subscriptions.
Santos Cordon6a212642015-05-08 16:35:23 -0700285 // TODO: Should this really be checking the "calling user" test for phone account?
Tony Mak240656f2015-12-04 11:36:22 +0000286 PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar
287 .getPhoneAccountUnchecked(targetPhoneAccountHandle);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800288 if (targetPhoneAccount == null) {
289 Log.d(this, "shouldSetConnectionManager, phone account not found");
290 return false;
291 }
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700292 boolean isSimSubscription = (targetPhoneAccount.getCapabilities() &
293 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0;
294 if (!isSimSubscription) {
295 return false;
296 }
297
298 return true;
299 }
300
301 // If there exists a registered connection manager then use it.
302 private void adjustAttemptsForConnectionManager() {
303 if (shouldSetConnectionManager()) {
304 CallAttemptRecord record = new CallAttemptRecord(
Tony Mak240656f2015-12-04 11:36:22 +0000305 mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall),
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700306 mAttemptRecords.get(0).targetPhoneAccount);
mike dooley10a58312014-11-06 13:46:19 -0800307 Log.v(this, "setConnectionManager, changing %s -> %s", mAttemptRecords.get(0), record);
Brad Ebingerf1900072015-11-12 17:25:06 -0800308 mAttemptRecords.add(0, record);
Sailesh Nepal7957f9c2014-08-09 17:13:19 -0700309 } else {
310 Log.v(this, "setConnectionManager, not changing");
Ihab Awad293edf22014-07-24 17:52:29 -0700311 }
312 }
313
Sailesh Nepal664837f2014-07-14 16:31:51 -0700314 // If we are possibly attempting to call a local emergency number, ensure that the
Ihab Awad69eb0f52014-07-18 11:20:37 -0700315 // plain PSTN connection services are listed, and nothing else.
Brad Ebingere6c481a2016-05-12 14:13:26 -0700316 private void adjustAttemptsForEmergency(PhoneAccountHandle preferredPAH) {
Tyler Gunn6ffe5312015-08-12 08:19:20 -0700317 if (mCall.isEmergencyCall()) {
Ihab Awad69eb0f52014-07-18 11:20:37 -0700318 Log.i(this, "Emergency number detected");
Ihab Awad293edf22014-07-24 17:52:29 -0700319 mAttemptRecords.clear();
Tony Mak240656f2015-12-04 11:36:22 +0000320 // Phone accounts in profile do not handle emergency call, use phone accounts in
321 // current user.
322 List<PhoneAccount> allAccounts = mPhoneAccountRegistrar
323 .getAllPhoneAccountsOfCurrentUser();
Yorke Leec1272112014-10-16 09:50:00 -0700324
325 if (allAccounts.isEmpty()) {
326 // If the list of phone accounts is empty at this point, it means Telephony hasn't
327 // registered any phone accounts yet. Add a fallback emergency phone account so
328 // that emergency calls can still go through. We create a new ArrayLists here just
329 // in case the implementation of PhoneAccountRegistrar ever returns an unmodifiable
330 // list.
331 allAccounts = new ArrayList<PhoneAccount>();
332 allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount());
333 }
334
Brad Ebingere6c481a2016-05-12 14:13:26 -0700335 // First, possibly add the SIM phone account that the user prefers
336 PhoneAccount preferredPA = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
337 preferredPAH);
338 if (preferredPA != null &&
339 preferredPA.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
340 preferredPA.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
341 Log.i(this, "Will try PSTN account %s for emergency",
342 preferredPA.getAccountHandle());
343 mAttemptRecords.add(new CallAttemptRecord(preferredPAH, preferredPAH));
344 }
345
346 // Next, add all SIM phone accounts which can place emergency calls.
347 TelephonyUtil.sortSimPhoneAccounts(mContext, allAccounts);
Srikanth Chintalaa67450d2017-10-13 16:50:51 +0530348
349 // If preferredPA already has an emergency PhoneAccount, do not add others since the
350 // emergency call be redialed in Telephony.
351 if (mAttemptRecords.isEmpty()) {
352 for (PhoneAccount phoneAccount : allAccounts) {
353 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
354 && phoneAccount.hasCapabilities(
355 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
356 PhoneAccountHandle phoneAccountHandle = phoneAccount.getAccountHandle();
Brad Ebingere6c481a2016-05-12 14:13:26 -0700357 Log.i(this, "Will try PSTN account %s for emergency", phoneAccountHandle);
358 mAttemptRecords.add(new CallAttemptRecord(phoneAccountHandle,
359 phoneAccountHandle));
Srikanth Chintalaa67450d2017-10-13 16:50:51 +0530360 // Add only one emergency SIM PhoneAccount to the attempt list.
361 break;
Brad Ebingere6c481a2016-05-12 14:13:26 -0700362 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700363 }
364 }
Tyler Gunn6e6f6d12014-08-20 15:22:18 -0700365
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700366 // Next, add the connection manager account as a backup if it can place emergency calls.
Tony Mak240656f2015-12-04 11:36:22 +0000367 PhoneAccountHandle callManagerHandle =
368 mPhoneAccountRegistrar.getSimCallManagerOfCurrentUser();
Brad Ebingerf1900072015-11-12 17:25:06 -0800369 if (callManagerHandle != null) {
370 // TODO: Should this really be checking the "calling user" test for phone account?
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700371 PhoneAccount callManager = mPhoneAccountRegistrar
Tony Mak240656f2015-12-04 11:36:22 +0000372 .getPhoneAccountUnchecked(callManagerHandle);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800373 if (callManager != null && callManager.hasCapabilities(
374 PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700375 CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
Tony Mak240656f2015-12-04 11:36:22 +0000376 mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
Hall Liu62630f72018-05-10 17:48:59 -0700377 mCall.getHandle() == null
378 ? null : mCall.getHandle().getScheme()));
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700379 if (!mAttemptRecords.contains(callAttemptRecord)) {
380 Log.i(this, "Will try Connection Manager account %s for emergency",
381 callManager);
382 mAttemptRecords.add(callAttemptRecord);
383 }
384 }
Tyler Gunn6e6f6d12014-08-20 15:22:18 -0700385 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700386 }
387 }
388
Sailesh Nepal646fa3d2015-01-28 02:55:36 -0800389 /** Returns all connection services used by the call attempt records. */
390 private static Collection<PhoneAccountHandle> getConnectionServices(
391 List<CallAttemptRecord> records) {
392 HashSet<PhoneAccountHandle> result = new HashSet<>();
393 for (CallAttemptRecord record : records) {
394 result.add(record.connectionManagerPhoneAccount);
395 }
396 return result;
397 }
398
Tony Mak240656f2015-12-04 11:36:22 +0000399
Brad Ebingerf1900072015-11-12 17:25:06 -0800400 private void notifyCallConnectionFailure(DisconnectCause errorDisconnectCause) {
401 if (mCallResponse != null) {
402 clearTimeout();
403 mCallResponse.handleCreateConnectionFailure(errorDisconnectCause);
404 mCallResponse = null;
405 mCall.clearConnectionService();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700406 }
Brad Ebingerf1900072015-11-12 17:25:06 -0800407 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700408
Brad Ebingerf1900072015-11-12 17:25:06 -0800409 @Override
410 public void handleCreateConnectionSuccess(
411 CallIdMapper idMapper,
412 ParcelableConnection connection) {
413 if (mCallResponse == null) {
414 // Nobody is listening for this connection attempt any longer; ask the responsible
415 // ConnectionService to tear down any resources associated with the call
416 mService.abort(mCall);
417 } else {
418 // Success -- share the good news and remember that we are no longer interested
419 // in hearing about any more attempts
420 mCallResponse.handleCreateConnectionSuccess(idMapper, connection);
421 mCallResponse = null;
422 // If there's a timeout running then don't clear it. The timeout can be triggered
423 // after the call has successfully been created but before it has become active.
Sailesh Nepal664837f2014-07-14 16:31:51 -0700424 }
Brad Ebingerf1900072015-11-12 17:25:06 -0800425 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700426
Brad Ebingerf1900072015-11-12 17:25:06 -0800427 private boolean shouldFailCallIfConnectionManagerFails(DisconnectCause cause) {
428 // Connection Manager does not exist or does not match registered Connection Manager
429 // Since Connection manager is a proxy for SIM, fall back to SIM
430 PhoneAccountHandle handle = mCall.getConnectionManagerPhoneAccount();
431 if (handle == null || !handle.equals(mPhoneAccountRegistrar.getSimCallManagerFromCall(
432 mCall))) {
Sailesh Nepalbafadce2014-11-05 18:40:21 -0800433 return false;
434 }
435
Brad Ebingerf1900072015-11-12 17:25:06 -0800436 // The Call's Connection Service does not exist
437 ConnectionServiceWrapper connectionManager = mCall.getConnectionService();
438 if (connectionManager == null) {
439 return true;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700440 }
Brad Ebingerf1900072015-11-12 17:25:06 -0800441
442 // In this case, fall back to a sim because connection manager declined
443 if (cause.getCode() == DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED) {
444 Log.d(CreateConnectionProcessor.this, "Connection manager declined to handle the "
445 + "call, falling back to not using a connection manager");
446 return false;
447 }
448
449 if (!connectionManager.isServiceValid("createConnection")) {
450 Log.d(CreateConnectionProcessor.this, "Connection manager unbound while trying "
451 + "create a connection, falling back to not using a connection manager");
452 return false;
453 }
454
455 // Do not fall back from connection manager and simply fail call if the failure reason is
456 // other
457 Log.d(CreateConnectionProcessor.this, "Connection Manager denied call with the following " +
458 "error: " + cause.getReason() + ". Not falling back to SIM.");
459 return true;
460 }
461
462 @Override
463 public void handleCreateConnectionFailure(DisconnectCause errorDisconnectCause) {
464 // Failure of some sort; record the reasons for failure and try again if possible
465 Log.d(CreateConnectionProcessor.this, "Connection failed: (%s)", errorDisconnectCause);
Brad Ebingere6c481a2016-05-12 14:13:26 -0700466 if (shouldFailCallIfConnectionManagerFails(errorDisconnectCause)) {
Brad Ebingerf1900072015-11-12 17:25:06 -0800467 notifyCallConnectionFailure(errorDisconnectCause);
468 return;
469 }
Santos Cordon8ff99f32016-02-12 15:30:44 -0800470 mLastErrorDisconnectCause = errorDisconnectCause;
Brad Ebingerf1900072015-11-12 17:25:06 -0800471 attemptNextPhoneAccount();
Sailesh Nepal664837f2014-07-14 16:31:51 -0700472 }
473}