Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 1 | /* |
| 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 Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 17 | package com.android.server.telecom; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 18 | |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 19 | import android.content.Context; |
Andrew Lee | 701dc00 | 2014-09-11 21:29:12 -0700 | [diff] [blame] | 20 | import android.telecom.DisconnectCause; |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 21 | import android.telecom.Log; |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 22 | import android.telecom.ParcelableConnection; |
| 23 | import android.telecom.PhoneAccount; |
| 24 | import android.telecom.PhoneAccountHandle; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 25 | |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 26 | // TODO: Needed for move to system service: import com.android.internal.R; |
| 27 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 28 | import com.android.internal.annotations.VisibleForTesting; |
| 29 | |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 30 | import java.util.ArrayList; |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 31 | import java.util.Collection; |
| 32 | import java.util.HashSet; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 33 | import java.util.Iterator; |
| 34 | import java.util.List; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 35 | import java.util.Objects; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 36 | |
| 37 | /** |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 38 | * This class creates connections to place new outgoing calls or to attach to an existing incoming |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 39 | * 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 Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 44 | @VisibleForTesting |
| 45 | public class CreateConnectionProcessor implements CreateConnectionResponse { |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 46 | |
| 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 Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 51 | public final PhoneAccountHandle connectionManagerPhoneAccount; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 52 | // The PhoneAccount which we will tell the target connection service to use |
| 53 | // for attempting to make the actual phone call |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 54 | public final PhoneAccountHandle targetPhoneAccount; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 55 | |
| 56 | public CallAttemptRecord( |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 57 | PhoneAccountHandle connectionManagerPhoneAccount, |
| 58 | PhoneAccountHandle targetPhoneAccount) { |
| 59 | this.connectionManagerPhoneAccount = connectionManagerPhoneAccount; |
| 60 | this.targetPhoneAccount = targetPhoneAccount; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | @Override |
| 64 | public String toString() { |
| 65 | return "CallAttemptRecord(" |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 66 | + Objects.toString(connectionManagerPhoneAccount) + "," |
| 67 | + Objects.toString(targetPhoneAccount) + ")"; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 68 | } |
Tyler Gunn | 6e6f6d1 | 2014-08-20 15:22:18 -0700 | [diff] [blame] | 69 | |
| 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 Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 87 | } |
| 88 | |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 89 | private final Call mCall; |
| 90 | private final ConnectionServiceRepository mRepository; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 91 | private List<CallAttemptRecord> mAttemptRecords; |
| 92 | private Iterator<CallAttemptRecord> mAttemptRecordIterator; |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 93 | private CreateConnectionResponse mCallResponse; |
Andrew Lee | 701dc00 | 2014-09-11 21:29:12 -0700 | [diff] [blame] | 94 | private DisconnectCause mLastErrorDisconnectCause; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 95 | private final PhoneAccountRegistrar mPhoneAccountRegistrar; |
| 96 | private final Context mContext; |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 97 | private CreateConnectionTimeout mTimeout; |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 98 | private ConnectionServiceWrapper mService; |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 99 | private int mConnectionAttempt; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 100 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 101 | @VisibleForTesting |
| 102 | public CreateConnectionProcessor( |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 103 | Call call, ConnectionServiceRepository repository, CreateConnectionResponse response, |
| 104 | PhoneAccountRegistrar phoneAccountRegistrar, Context context) { |
Ihab Awad | 8de7691 | 2015-02-17 12:25:52 -0800 | [diff] [blame] | 105 | Log.v(this, "CreateConnectionProcessor created for Call = %s", call); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 106 | mCall = call; |
| 107 | mRepository = repository; |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 108 | mCallResponse = response; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 109 | mPhoneAccountRegistrar = phoneAccountRegistrar; |
| 110 | mContext = context; |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 111 | mConnectionAttempt = 0; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 112 | } |
| 113 | |
Sailesh Nepal | 752cacb | 2014-11-06 12:35:26 -0800 | [diff] [blame] | 114 | boolean isProcessingComplete() { |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 115 | return mCallResponse == null; |
Sailesh Nepal | 752cacb | 2014-11-06 12:35:26 -0800 | [diff] [blame] | 116 | } |
| 117 | |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 118 | boolean isCallTimedOut() { |
| 119 | return mTimeout != null && mTimeout.isCallTimedOut(); |
| 120 | } |
| 121 | |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 122 | public int getConnectionAttempt() { |
| 123 | return mConnectionAttempt; |
| 124 | } |
| 125 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 126 | @VisibleForTesting |
| 127 | public void process() { |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 128 | Log.v(this, "process"); |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 129 | clearTimeout(); |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 130 | mAttemptRecords = new ArrayList<>(); |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 131 | if (mCall.getTargetPhoneAccount() != null) { |
Sailesh Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 132 | mAttemptRecords.add(new CallAttemptRecord( |
| 133 | mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount())); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 134 | } |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 135 | if (!mCall.isSelfManaged()) { |
| 136 | adjustAttemptsForConnectionManager(); |
| 137 | adjustAttemptsForEmergency(mCall.getTargetPhoneAccount()); |
| 138 | } |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 139 | mAttemptRecordIterator = mAttemptRecords.iterator(); |
Ihab Awad | 69eb0f5 | 2014-07-18 11:20:37 -0700 | [diff] [blame] | 140 | attemptNextPhoneAccount(); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 141 | } |
| 142 | |
Sailesh Nepal | 752cacb | 2014-11-06 12:35:26 -0800 | [diff] [blame] | 143 | boolean hasMorePhoneAccounts() { |
| 144 | return mAttemptRecordIterator.hasNext(); |
| 145 | } |
| 146 | |
| 147 | void continueProcessingIfPossible(CreateConnectionResponse response, |
| 148 | DisconnectCause disconnectCause) { |
| 149 | Log.v(this, "continueProcessingIfPossible"); |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 150 | mCallResponse = response; |
Sailesh Nepal | 752cacb | 2014-11-06 12:35:26 -0800 | [diff] [blame] | 151 | mLastErrorDisconnectCause = disconnectCause; |
| 152 | attemptNextPhoneAccount(); |
| 153 | } |
| 154 | |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 155 | void abort() { |
| 156 | Log.v(this, "abort"); |
| 157 | |
| 158 | // Clear the response first to prevent attemptNextConnectionService from attempting any |
| 159 | // more services. |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 160 | CreateConnectionResponse response = mCallResponse; |
| 161 | mCallResponse = null; |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 162 | clearTimeout(); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 163 | |
| 164 | ConnectionServiceWrapper service = mCall.getConnectionService(); |
| 165 | if (service != null) { |
| 166 | service.abort(mCall); |
| 167 | mCall.clearConnectionService(); |
| 168 | } |
| 169 | if (response != null) { |
Andrew Lee | 701dc00 | 2014-09-11 21:29:12 -0700 | [diff] [blame] | 170 | response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.LOCAL)); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 171 | } |
| 172 | } |
| 173 | |
Ihab Awad | 69eb0f5 | 2014-07-18 11:20:37 -0700 | [diff] [blame] | 174 | private void attemptNextPhoneAccount() { |
| 175 | Log.v(this, "attemptNextPhoneAccount"); |
Tyler Gunn | cb59b67 | 2014-08-20 09:02:11 -0700 | [diff] [blame] | 176 | CallAttemptRecord attempt = null; |
| 177 | if (mAttemptRecordIterator.hasNext()) { |
| 178 | attempt = mAttemptRecordIterator.next(); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 179 | |
Santos Cordon | 6a21264 | 2015-05-08 16:35:23 -0700 | [diff] [blame] | 180 | if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission( |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 181 | attempt.connectionManagerPhoneAccount)) { |
Tyler Gunn | cb59b67 | 2014-08-20 09:02:11 -0700 | [diff] [blame] | 182 | Log.w(this, |
Yorke Lee | 7bb8ce9 | 2015-05-13 16:28:29 -0700 | [diff] [blame] | 183 | "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for " |
| 184 | + "attempt: %s", attempt); |
Tyler Gunn | cb59b67 | 2014-08-20 09:02:11 -0700 | [diff] [blame] | 185 | attemptNextPhoneAccount(); |
| 186 | return; |
| 187 | } |
| 188 | |
| 189 | // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it |
Yorke Lee | 7bb8ce9 | 2015-05-13 16:28:29 -0700 | [diff] [blame] | 190 | // also requires the BIND_TELECOM_CONNECTION_SERVICE permission. |
Tyler Gunn | cb59b67 | 2014-08-20 09:02:11 -0700 | [diff] [blame] | 191 | if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) && |
Santos Cordon | 6a21264 | 2015-05-08 16:35:23 -0700 | [diff] [blame] | 192 | !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission( |
| 193 | attempt.targetPhoneAccount)) { |
Tyler Gunn | cb59b67 | 2014-08-20 09:02:11 -0700 | [diff] [blame] | 194 | Log.w(this, |
Yorke Lee | 7bb8ce9 | 2015-05-13 16:28:29 -0700 | [diff] [blame] | 195 | "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for " |
| 196 | + "attempt: %s", attempt); |
Tyler Gunn | cb59b67 | 2014-08-20 09:02:11 -0700 | [diff] [blame] | 197 | attemptNextPhoneAccount(); |
| 198 | return; |
| 199 | } |
| 200 | } |
| 201 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 202 | if (mCallResponse != null && attempt != null) { |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 203 | Log.i(this, "Trying attempt %s", attempt); |
Evan Charlton | 105d977 | 2014-11-25 14:08:53 -0800 | [diff] [blame] | 204 | PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount; |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 205 | mService = mRepository.getService(phoneAccount.getComponentName(), |
| 206 | phoneAccount.getUserHandle()); |
| 207 | if (mService == null) { |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 208 | Log.i(this, "Found no connection service for attempt %s", attempt); |
Ihab Awad | 69eb0f5 | 2014-07-18 11:20:37 -0700 | [diff] [blame] | 209 | attemptNextPhoneAccount(); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 210 | } else { |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 211 | mConnectionAttempt++; |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 212 | mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount); |
| 213 | mCall.setTargetPhoneAccount(attempt.targetPhoneAccount); |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 214 | mCall.setConnectionService(mService); |
| 215 | setTimeoutIfNeeded(mService, attempt); |
Pengquan Meng | 606ff7f | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 216 | 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 Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 232 | |
Pengquan Meng | 606ff7f | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 233 | } |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 234 | } |
| 235 | } else { |
Ihab Awad | 69eb0f5 | 2014-07-18 11:20:37 -0700 | [diff] [blame] | 236 | Log.v(this, "attemptNextPhoneAccount, no more accounts, failing"); |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 237 | DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ? |
| 238 | mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR); |
| 239 | notifyCallConnectionFailure(disconnectCause); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 240 | } |
| 241 | } |
| 242 | |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 243 | 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 Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 262 | private boolean shouldSetConnectionManager() { |
| 263 | if (mAttemptRecords.size() == 0) { |
| 264 | return false; |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 265 | } |
Ihab Awad | c17294c | 2014-08-04 19:23:37 -0700 | [diff] [blame] | 266 | |
Sailesh Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 267 | 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 Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 273 | PhoneAccountHandle connectionManager = |
| 274 | mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall); |
Sailesh Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 275 | 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 Cordon | 6a21264 | 2015-05-08 16:35:23 -0700 | [diff] [blame] | 285 | // TODO: Should this really be checking the "calling user" test for phone account? |
Tony Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 286 | PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar |
| 287 | .getPhoneAccountUnchecked(targetPhoneAccountHandle); |
Sailesh Nepal | 91fc809 | 2015-02-14 15:44:55 -0800 | [diff] [blame] | 288 | if (targetPhoneAccount == null) { |
| 289 | Log.d(this, "shouldSetConnectionManager, phone account not found"); |
| 290 | return false; |
| 291 | } |
Sailesh Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 292 | 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 Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 305 | mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall), |
Sailesh Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 306 | mAttemptRecords.get(0).targetPhoneAccount); |
mike dooley | 10a5831 | 2014-11-06 13:46:19 -0800 | [diff] [blame] | 307 | Log.v(this, "setConnectionManager, changing %s -> %s", mAttemptRecords.get(0), record); |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 308 | mAttemptRecords.add(0, record); |
Sailesh Nepal | 7957f9c | 2014-08-09 17:13:19 -0700 | [diff] [blame] | 309 | } else { |
| 310 | Log.v(this, "setConnectionManager, not changing"); |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 311 | } |
| 312 | } |
| 313 | |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 314 | // If we are possibly attempting to call a local emergency number, ensure that the |
Ihab Awad | 69eb0f5 | 2014-07-18 11:20:37 -0700 | [diff] [blame] | 315 | // plain PSTN connection services are listed, and nothing else. |
Brad Ebinger | e6c481a | 2016-05-12 14:13:26 -0700 | [diff] [blame] | 316 | private void adjustAttemptsForEmergency(PhoneAccountHandle preferredPAH) { |
Tyler Gunn | 6ffe531 | 2015-08-12 08:19:20 -0700 | [diff] [blame] | 317 | if (mCall.isEmergencyCall()) { |
Ihab Awad | 69eb0f5 | 2014-07-18 11:20:37 -0700 | [diff] [blame] | 318 | Log.i(this, "Emergency number detected"); |
Ihab Awad | 293edf2 | 2014-07-24 17:52:29 -0700 | [diff] [blame] | 319 | mAttemptRecords.clear(); |
Tony Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 320 | // Phone accounts in profile do not handle emergency call, use phone accounts in |
| 321 | // current user. |
| 322 | List<PhoneAccount> allAccounts = mPhoneAccountRegistrar |
| 323 | .getAllPhoneAccountsOfCurrentUser(); |
Yorke Lee | c127211 | 2014-10-16 09:50:00 -0700 | [diff] [blame] | 324 | |
| 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 Ebinger | e6c481a | 2016-05-12 14:13:26 -0700 | [diff] [blame] | 335 | // 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 Chintala | a67450d | 2017-10-13 16:50:51 +0530 | [diff] [blame] | 348 | |
| 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 Ebinger | e6c481a | 2016-05-12 14:13:26 -0700 | [diff] [blame] | 357 | Log.i(this, "Will try PSTN account %s for emergency", phoneAccountHandle); |
| 358 | mAttemptRecords.add(new CallAttemptRecord(phoneAccountHandle, |
| 359 | phoneAccountHandle)); |
Srikanth Chintala | a67450d | 2017-10-13 16:50:51 +0530 | [diff] [blame] | 360 | // Add only one emergency SIM PhoneAccount to the attempt list. |
| 361 | break; |
Brad Ebinger | e6c481a | 2016-05-12 14:13:26 -0700 | [diff] [blame] | 362 | } |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 363 | } |
| 364 | } |
Tyler Gunn | 6e6f6d1 | 2014-08-20 15:22:18 -0700 | [diff] [blame] | 365 | |
Tyler Gunn | 8e0fef4 | 2014-09-08 18:34:44 -0700 | [diff] [blame] | 366 | // Next, add the connection manager account as a backup if it can place emergency calls. |
Tony Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 367 | PhoneAccountHandle callManagerHandle = |
| 368 | mPhoneAccountRegistrar.getSimCallManagerOfCurrentUser(); |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 369 | if (callManagerHandle != null) { |
| 370 | // TODO: Should this really be checking the "calling user" test for phone account? |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 371 | PhoneAccount callManager = mPhoneAccountRegistrar |
Tony Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 372 | .getPhoneAccountUnchecked(callManagerHandle); |
Sailesh Nepal | 91fc809 | 2015-02-14 15:44:55 -0800 | [diff] [blame] | 373 | if (callManager != null && callManager.hasCapabilities( |
| 374 | PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) { |
Tyler Gunn | 8e0fef4 | 2014-09-08 18:34:44 -0700 | [diff] [blame] | 375 | CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle, |
Tony Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 376 | mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( |
Hall Liu | 62630f7 | 2018-05-10 17:48:59 -0700 | [diff] [blame] | 377 | mCall.getHandle() == null |
| 378 | ? null : mCall.getHandle().getScheme())); |
Tyler Gunn | 8e0fef4 | 2014-09-08 18:34:44 -0700 | [diff] [blame] | 379 | 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 Gunn | 6e6f6d1 | 2014-08-20 15:22:18 -0700 | [diff] [blame] | 385 | } |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 386 | } |
| 387 | } |
| 388 | |
Sailesh Nepal | 646fa3d | 2015-01-28 02:55:36 -0800 | [diff] [blame] | 389 | /** 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 Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 399 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 400 | private void notifyCallConnectionFailure(DisconnectCause errorDisconnectCause) { |
| 401 | if (mCallResponse != null) { |
| 402 | clearTimeout(); |
| 403 | mCallResponse.handleCreateConnectionFailure(errorDisconnectCause); |
| 404 | mCallResponse = null; |
| 405 | mCall.clearConnectionService(); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 406 | } |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 407 | } |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 408 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 409 | @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 Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 424 | } |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 425 | } |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 426 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 427 | 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 Nepal | bafadce | 2014-11-05 18:40:21 -0800 | [diff] [blame] | 433 | return false; |
| 434 | } |
| 435 | |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 436 | // The Call's Connection Service does not exist |
| 437 | ConnectionServiceWrapper connectionManager = mCall.getConnectionService(); |
| 438 | if (connectionManager == null) { |
| 439 | return true; |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 440 | } |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 441 | |
| 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 Ebinger | e6c481a | 2016-05-12 14:13:26 -0700 | [diff] [blame] | 466 | if (shouldFailCallIfConnectionManagerFails(errorDisconnectCause)) { |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 467 | notifyCallConnectionFailure(errorDisconnectCause); |
| 468 | return; |
| 469 | } |
Santos Cordon | 8ff99f3 | 2016-02-12 15:30:44 -0800 | [diff] [blame] | 470 | mLastErrorDisconnectCause = errorDisconnectCause; |
Brad Ebinger | f190007 | 2015-11-12 17:25:06 -0800 | [diff] [blame] | 471 | attemptNextPhoneAccount(); |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 472 | } |
| 473 | } |