1 /* 2 * Copyright (C) 2018 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 17 package com.android.server.telecom.callredirection; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.net.Uri; 24 import android.os.Binder; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Looper; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.telecom.CallRedirectionService; 31 import android.telecom.GatewayInfo; 32 import android.telecom.Log; 33 import android.telecom.Logging.Runnable; 34 import android.telecom.PhoneAccountHandle; 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.telecom.ICallRedirectionAdapter; 37 import com.android.internal.telecom.ICallRedirectionService; 38 import com.android.server.telecom.Call; 39 import com.android.server.telecom.CallsManager; 40 import com.android.server.telecom.LogUtils; 41 import com.android.server.telecom.PhoneAccountRegistrar; 42 import com.android.server.telecom.TelecomSystem; 43 import com.android.server.telecom.Timeouts; 44 45 /** 46 * A single instance of call redirection processor that handles the call redirection with 47 * user-defined {@link CallRedirectionService} and carrier {@link CallRedirectionService} for a 48 * single call. 49 * 50 * A user-defined call redirection will be performed firstly and a carrier call redirection will be 51 * performed after that; there will be a total of two call redirection cycles. 52 * 53 * A call redirection cycle is a cycle: 54 * 1) Telecom requests a call redirection of a call with a specific {@link CallRedirectionService}, 55 * 2) Telecom receives the response either from a specific {@link CallRedirectionService} or from 56 * the timeout. 57 * 58 * Telecom should return to {@link CallsManager} at the end of current call redirection 59 * cycle, if 60 * 1) {@link CallRedirectionService} sends {@link CallRedirectionService#cancelCall()} response 61 * before timeout; 62 * or 2) Telecom finishes call redirection with carrier {@link CallRedirectionService}. 63 */ 64 public class CallRedirectionProcessor implements CallRedirectionCallback { 65 66 private class CallRedirectionAttempt { 67 private final ComponentName mComponentName; 68 private final String mServiceType; 69 private ServiceConnection mConnection; 70 private ICallRedirectionService mService; 71 CallRedirectionAttempt(ComponentName componentName, String serviceType)72 private CallRedirectionAttempt(ComponentName componentName, String serviceType) { 73 mComponentName = componentName; 74 mServiceType = serviceType; 75 } 76 process()77 private void process() { 78 Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE) 79 .setComponent(mComponentName); 80 ServiceConnection connection = new CallRedirectionServiceConnection(); 81 if (mContext.bindServiceAsUser( 82 intent, 83 connection, 84 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE 85 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, 86 UserHandle.CURRENT)) { 87 Log.d(this, "bindService, found " + mServiceType + " call redirection service," 88 + " waiting for it to connect"); 89 mConnection = connection; 90 } 91 } 92 onServiceBound(ICallRedirectionService service)93 private void onServiceBound(ICallRedirectionService service) { 94 mService = service; 95 try { 96 // Telecom does not perform user interactions for carrier call redirection. 97 mService.placeCall(new CallRedirectionAdapter(), mProcessedDestinationUri, 98 mPhoneAccountHandle, mAllowInteractiveResponse 99 && mServiceType.equals(SERVICE_TYPE_USER_DEFINED)); 100 Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED) 101 ? LogUtils.Events.REDIRECTION_SENT_USER 102 : LogUtils.Events.REDIRECTION_SENT_CARRIER, mComponentName); 103 Log.d(this, "Requested placeCall with [Destination Uri] " 104 + Log.pii(mProcessedDestinationUri) 105 + " [phoneAccountHandle]" + mPhoneAccountHandle); 106 } catch (RemoteException e) { 107 Log.e(this, e, "Failed to request with the found " + mServiceType + " call" 108 + " redirection service"); 109 finishCallRedirection(); 110 } 111 } 112 finishCallRedirection()113 private void finishCallRedirection() { 114 if (((mServiceType.equals(SERVICE_TYPE_CARRIER)) && mIsCarrierRedirectionPending) 115 || ((mServiceType.equals(SERVICE_TYPE_USER_DEFINED)) 116 && mIsUserDefinedRedirectionPending)) { 117 if (mConnection != null) { 118 // We still need to call unbind even if the service disconnected. 119 mContext.unbindService(mConnection); 120 mConnection = null; 121 } 122 mService = null; 123 onCallRedirectionComplete(mCall); 124 } 125 } 126 notifyTimeout()127 public void notifyTimeout() { 128 if (mService != null) { 129 try { 130 mService.notifyTimeout(); 131 } catch (RemoteException e) { 132 Log.e(this, e, "Failed to notify call redirection timed out to " 133 + mServiceType + " call redirection service"); 134 } 135 } 136 } 137 138 private class CallRedirectionServiceConnection implements ServiceConnection { 139 @Override onServiceConnected(ComponentName componentName, IBinder service)140 public void onServiceConnected(ComponentName componentName, IBinder service) { 141 Log.startSession("CRSC.oSC"); 142 try { 143 synchronized (mTelecomLock) { 144 Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED) 145 ? LogUtils.Events.REDIRECTION_BOUND_USER 146 : LogUtils.Events.REDIRECTION_BOUND_CARRIER, componentName); 147 onServiceBound(ICallRedirectionService.Stub.asInterface(service)); 148 } 149 } finally { 150 Log.endSession(); 151 } 152 } 153 154 @Override onServiceDisconnected(ComponentName componentName)155 public void onServiceDisconnected(ComponentName componentName) { 156 Log.startSession("CRSC.oSD"); 157 try { 158 synchronized (mTelecomLock) { 159 finishCallRedirection(); 160 } 161 } finally { 162 Log.endSession(); 163 } 164 } 165 166 @Override onNullBinding(ComponentName componentName)167 public void onNullBinding(ComponentName componentName) { 168 // Make sure we unbind the service if onBind returns null 169 Log.startSession("CRSC.oNB"); 170 try { 171 synchronized (mTelecomLock) { 172 finishCallRedirection(); 173 } 174 } finally { 175 Log.endSession(); 176 } 177 } 178 } 179 180 private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub { 181 @Override cancelCall()182 public void cancelCall() { 183 Log.startSession("CRA.cC"); 184 long token = Binder.clearCallingIdentity(); 185 try { 186 synchronized (mTelecomLock) { 187 Log.d(this, "Received cancelCall from " + mServiceType + " call" 188 + " redirection service"); 189 mShouldCancelCall = true; 190 finishCallRedirection(); 191 } 192 } finally { 193 Binder.restoreCallingIdentity(token); 194 Log.endSession(); 195 } 196 } 197 198 @Override placeCallUnmodified()199 public void placeCallUnmodified() { 200 Log.startSession("CRA.pCU"); 201 long token = Binder.clearCallingIdentity(); 202 try { 203 synchronized (mTelecomLock) { 204 Log.d(this, "Received placeCallUnmodified from " + mServiceType + " call" 205 + " redirection service"); 206 finishCallRedirection(); 207 } 208 } finally { 209 Binder.restoreCallingIdentity(token); 210 Log.endSession(); 211 } 212 } 213 214 @Override redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount, boolean confirmFirst)215 public void redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount, 216 boolean confirmFirst) { 217 Log.startSession("CRA.rC"); 218 long token = Binder.clearCallingIdentity(); 219 try { 220 synchronized (mTelecomLock) { 221 mRedirectionGatewayInfo = mCallRedirectionProcessorHelper 222 .getGatewayInfoFromGatewayUri(mComponentName.getPackageName(), 223 gatewayUri, mDestinationUri, mPostDialDigits); 224 mPhoneAccountHandle = targetPhoneAccount; 225 // If carrier redirects call, we should skip to notify users about 226 // the user-defined call redirection service. 227 mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED) 228 && mAllowInteractiveResponse) 229 ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : UI_TYPE_NO_ACTION; 230 Log.d(this, "Received redirectCall with [gatewayUri]" 231 + Log.pii(gatewayUri) + " [phoneAccountHandle]" 232 + mPhoneAccountHandle + "[confirmFirst]" + confirmFirst + " from " 233 + mServiceType + " call redirection service"); 234 finishCallRedirection(); 235 } 236 } finally { 237 Binder.restoreCallingIdentity(token); 238 Log.endSession(); 239 } 240 } 241 } 242 } 243 244 private final Context mContext; 245 private final CallsManager mCallsManager; 246 private final Call mCall; 247 private final boolean mAllowInteractiveResponse; 248 private GatewayInfo mRedirectionGatewayInfo; 249 private final boolean mSpeakerphoneOn; 250 private final int mVideoState; 251 private final Timeouts.Adapter mTimeoutsAdapter; 252 private final TelecomSystem.SyncRoot mTelecomLock; 253 private final Handler mHandler = new Handler(Looper.getMainLooper()); 254 255 private CallRedirectionAttempt mAttempt; 256 private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper; 257 258 public static final String SERVICE_TYPE_CARRIER = "carrier"; 259 public static final String SERVICE_TYPE_USER_DEFINED = "user_defined"; 260 public static final String UI_TYPE_NO_ACTION = "no_action"; 261 public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout"; 262 public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM 263 = "user_defined_ask_for_confirm"; 264 265 private PhoneAccountHandle mPhoneAccountHandle; 266 private Uri mDestinationUri; 267 /** 268 * Try to send the implemented service with processed destination uri by formatting it to E.164 269 * and removing post dial digits. 270 */ 271 private Uri mProcessedDestinationUri; 272 273 /** 274 * The post dial digits which were removed from {@link #mDestinationUri} when determining 275 * {@link #mProcessedDestinationUri}. 276 */ 277 private String mPostDialDigits; 278 279 /** 280 * Indicates if Telecom should cancel the call when the whole call redirection finishes. 281 */ 282 private boolean mShouldCancelCall = false; 283 /** 284 * Indicates Telecom should handle different types of UI if need. 285 */ 286 private String mUiAction = UI_TYPE_NO_ACTION; 287 /** 288 * Indicates if Telecom is waiting for a callback from a user-defined 289 * {@link CallRedirectionService}. 290 */ 291 private boolean mIsUserDefinedRedirectionPending = false; 292 /** 293 * Indicates if Telecom is waiting for a callback from a carrier 294 * {@link CallRedirectionService}. 295 */ 296 private boolean mIsCarrierRedirectionPending = false; 297 CallRedirectionProcessor( Context context, CallsManager callsManager, Call call, Uri handle, PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)298 public CallRedirectionProcessor( 299 Context context, 300 CallsManager callsManager, 301 Call call, 302 Uri handle, 303 PhoneAccountRegistrar phoneAccountRegistrar, 304 GatewayInfo gatewayInfo, 305 boolean speakerphoneOn, 306 int videoState) { 307 mContext = context; 308 mCallsManager = callsManager; 309 mCall = call; 310 mDestinationUri = handle; 311 mPhoneAccountHandle = call.getTargetPhoneAccount(); 312 mRedirectionGatewayInfo = gatewayInfo; 313 mSpeakerphoneOn = speakerphoneOn; 314 mVideoState = videoState; 315 mTimeoutsAdapter = callsManager.getTimeoutsAdapter(); 316 mTelecomLock = callsManager.getLock(); 317 /** 318 * The current rule to decide whether the implemented {@link CallRedirectionService} should 319 * allow interactive responses with users is only based on whether it is in car mode. 320 */ 321 mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarModeOrProjectionActive(); 322 mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper( 323 context, callsManager, phoneAccountRegistrar); 324 mProcessedDestinationUri = mCallRedirectionProcessorHelper.formatNumberForRedirection( 325 mDestinationUri); 326 mPostDialDigits = mCallRedirectionProcessorHelper.getPostDialDigits(mDestinationUri); 327 } 328 329 @Override onCallRedirectionComplete(Call call)330 public void onCallRedirectionComplete(Call call) { 331 // synchronized on mTelecomLock to enter into Telecom. 332 mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) { 333 @Override 334 public void loggedRun() { 335 if (mIsUserDefinedRedirectionPending) { 336 Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER); 337 mIsUserDefinedRedirectionPending = false; 338 if (mShouldCancelCall) { 339 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 340 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, 341 mVideoState, mShouldCancelCall, mUiAction); 342 } else { 343 performCarrierCallRedirection(); 344 } 345 } 346 if (mIsCarrierRedirectionPending) { 347 Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER); 348 mIsCarrierRedirectionPending = false; 349 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 350 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, 351 mVideoState, mShouldCancelCall, mUiAction); 352 } 353 } 354 }.prepare()); 355 } 356 357 /** 358 * The entry to perform call redirection of the call from (@link CallsManager) 359 */ performCallRedirection()360 public void performCallRedirection() { 361 // If the Gateway Info is set with intent, only request with carrier call redirection. 362 if (mRedirectionGatewayInfo != null) { 363 performCarrierCallRedirection(); 364 } else { 365 performUserDefinedCallRedirection(); 366 } 367 } 368 performUserDefinedCallRedirection()369 private void performUserDefinedCallRedirection() { 370 Log.d(this, "performUserDefinedCallRedirection"); 371 ComponentName componentName = 372 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService(); 373 if (componentName != null) { 374 mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED); 375 mAttempt.process(); 376 mIsUserDefinedRedirectionPending = true; 377 processTimeoutForCallRedirection(SERVICE_TYPE_USER_DEFINED); 378 } else { 379 Log.i(this, "There are no user-defined call redirection services installed on this" 380 + " device."); 381 performCarrierCallRedirection(); 382 } 383 } 384 performCarrierCallRedirection()385 private void performCarrierCallRedirection() { 386 Log.d(this, "performCarrierCallRedirection"); 387 ComponentName componentName = 388 mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( 389 mPhoneAccountHandle); 390 if (componentName != null) { 391 mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER); 392 mAttempt.process(); 393 mIsCarrierRedirectionPending = true; 394 processTimeoutForCallRedirection(SERVICE_TYPE_CARRIER); 395 } else { 396 Log.i(this, "There are no carrier call redirection services installed on this" 397 + " device."); 398 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 399 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, mVideoState, 400 mShouldCancelCall, mUiAction); 401 } 402 } 403 processTimeoutForCallRedirection(String serviceType)404 private void processTimeoutForCallRedirection(String serviceType) { 405 long timeout = serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? 406 mTimeoutsAdapter.getUserDefinedCallRedirectionTimeoutMillis( 407 mContext.getContentResolver()) : mTimeoutsAdapter 408 .getCarrierCallRedirectionTimeoutMillis(mContext.getContentResolver()); 409 410 mHandler.postDelayed(new Runnable("CRP.pTFCR", null) { 411 @Override 412 public void loggedRun() { 413 boolean isCurrentRedirectionPending = 414 serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? 415 mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending; 416 if (isCurrentRedirectionPending) { 417 Log.i(this, serviceType + " call redirection has timed out."); 418 Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED) 419 ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER 420 : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER); 421 mAttempt.notifyTimeout(); 422 if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) { 423 mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT; 424 mShouldCancelCall = true; 425 } 426 onCallRedirectionComplete(mCall); 427 } 428 } 429 }.prepare(), timeout); 430 } 431 432 /** 433 * Checks if Telecom can make call redirection with any available call redirection service. 434 * 435 * @return {@code true} if it can; {@code false} otherwise. 436 */ canMakeCallRedirectionWithService()437 public boolean canMakeCallRedirectionWithService() { 438 boolean canMakeCallRedirectionWithService = 439 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null 440 || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( 441 mPhoneAccountHandle) != null; 442 Log.i(this, "Can make call redirection with any available service: " 443 + canMakeCallRedirectionWithService); 444 return canMakeCallRedirectionWithService; 445 } 446 447 /** 448 * Returns the handler, for testing purposes. 449 */ 450 @VisibleForTesting getHandler()451 public Handler getHandler() { 452 return mHandler; 453 } 454 455 /** 456 * Set CallRedirectionProcessorHelper for testing purposes. 457 */ 458 @VisibleForTesting setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper)459 public void setCallRedirectionServiceHelper( 460 CallRedirectionProcessorHelper callRedirectionProcessorHelper) { 461 mCallRedirectionProcessorHelper = callRedirectionProcessorHelper; 462 } 463 } 464