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(UserHandle userHandleForCallRedirection)77 private void process(UserHandle userHandleForCallRedirection) { 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 userHandleForCallRedirection)) { 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 Log.i(this, "notifyTimeout: call redirection has timed out so " 137 + "unbinding the connection"); 138 if (mConnection != null) { 139 // We still need to call unbind even if the service disconnected. 140 mContext.unbindService(mConnection); 141 mConnection = null; 142 } 143 mService = null; 144 } 145 146 private class CallRedirectionServiceConnection implements ServiceConnection { 147 @Override onServiceConnected(ComponentName componentName, IBinder service)148 public void onServiceConnected(ComponentName componentName, IBinder service) { 149 Log.startSession("CRSC.oSC"); 150 try { 151 synchronized (mTelecomLock) { 152 Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED) 153 ? LogUtils.Events.REDIRECTION_BOUND_USER 154 : LogUtils.Events.REDIRECTION_BOUND_CARRIER, componentName); 155 onServiceBound(ICallRedirectionService.Stub.asInterface(service)); 156 } 157 } finally { 158 Log.endSession(); 159 } 160 } 161 162 @Override onServiceDisconnected(ComponentName componentName)163 public void onServiceDisconnected(ComponentName componentName) { 164 Log.startSession("CRSC.oSD"); 165 try { 166 synchronized (mTelecomLock) { 167 finishCallRedirection(); 168 } 169 } finally { 170 Log.endSession(); 171 } 172 } 173 174 @Override onNullBinding(ComponentName componentName)175 public void onNullBinding(ComponentName componentName) { 176 // Make sure we unbind the service if onBind returns null 177 Log.startSession("CRSC.oNB"); 178 try { 179 synchronized (mTelecomLock) { 180 finishCallRedirection(); 181 } 182 } finally { 183 Log.endSession(); 184 } 185 } 186 187 @Override onBindingDied(ComponentName componentName)188 public void onBindingDied(ComponentName componentName) { 189 // Make sure we unbind the service if binding died to avoid background stating 190 // activity leaks 191 Log.startSession("CRSC.oBD"); 192 try { 193 synchronized (mTelecomLock) { 194 finishCallRedirection(); 195 } 196 } finally { 197 Log.endSession(); 198 } 199 } 200 } 201 202 private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub { 203 @Override cancelCall()204 public void cancelCall() { 205 Log.startSession("CRA.cC"); 206 long token = Binder.clearCallingIdentity(); 207 try { 208 synchronized (mTelecomLock) { 209 Log.d(this, "Received cancelCall from " + mServiceType + " call" 210 + " redirection service"); 211 mShouldCancelCall = true; 212 finishCallRedirection(); 213 } 214 } finally { 215 Binder.restoreCallingIdentity(token); 216 Log.endSession(); 217 } 218 } 219 220 @Override placeCallUnmodified()221 public void placeCallUnmodified() { 222 Log.startSession("CRA.pCU"); 223 long token = Binder.clearCallingIdentity(); 224 try { 225 synchronized (mTelecomLock) { 226 Log.d(this, "Received placeCallUnmodified from " + mServiceType + " call" 227 + " redirection service"); 228 finishCallRedirection(); 229 } 230 } finally { 231 Binder.restoreCallingIdentity(token); 232 Log.endSession(); 233 } 234 } 235 236 @Override redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount, boolean confirmFirst)237 public void redirectCall(Uri gatewayUri, PhoneAccountHandle targetPhoneAccount, 238 boolean confirmFirst) { 239 Log.startSession("CRA.rC"); 240 long token = Binder.clearCallingIdentity(); 241 try { 242 synchronized (mTelecomLock) { 243 mRedirectionGatewayInfo = mCallRedirectionProcessorHelper 244 .getGatewayInfoFromGatewayUri(mComponentName.getPackageName(), 245 gatewayUri, mDestinationUri, mPostDialDigits); 246 mPhoneAccountHandle = targetPhoneAccount; 247 // If carrier redirects call, we should skip to notify users about 248 // the user-defined call redirection service. 249 mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED) 250 && mAllowInteractiveResponse) 251 ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : UI_TYPE_NO_ACTION; 252 Log.d(this, "Received redirectCall with [gatewayUri]" 253 + Log.pii(gatewayUri) + " [phoneAccountHandle]" 254 + mPhoneAccountHandle + "[confirmFirst]" + confirmFirst + " from " 255 + mServiceType + " call redirection service"); 256 finishCallRedirection(); 257 } 258 } finally { 259 Binder.restoreCallingIdentity(token); 260 Log.endSession(); 261 } 262 } 263 } 264 } 265 266 private final Context mContext; 267 private final CallsManager mCallsManager; 268 private final Call mCall; 269 private final boolean mAllowInteractiveResponse; 270 private GatewayInfo mRedirectionGatewayInfo; 271 private final boolean mSpeakerphoneOn; 272 private final int mVideoState; 273 private final Timeouts.Adapter mTimeoutsAdapter; 274 private final TelecomSystem.SyncRoot mTelecomLock; 275 private final Handler mHandler = new Handler(Looper.getMainLooper()); 276 277 private CallRedirectionAttempt mAttempt; 278 private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper; 279 280 public static final String SERVICE_TYPE_CARRIER = "carrier"; 281 public static final String SERVICE_TYPE_USER_DEFINED = "user_defined"; 282 public static final String UI_TYPE_NO_ACTION = "no_action"; 283 public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout"; 284 public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM 285 = "user_defined_ask_for_confirm"; 286 287 private PhoneAccountHandle mPhoneAccountHandle; 288 private Uri mDestinationUri; 289 /** 290 * Try to send the implemented service with processed destination uri by formatting it to E.164 291 * and removing post dial digits. 292 */ 293 private Uri mProcessedDestinationUri; 294 295 /** 296 * The post dial digits which were removed from {@link #mDestinationUri} when determining 297 * {@link #mProcessedDestinationUri}. 298 */ 299 private String mPostDialDigits; 300 301 /** 302 * Indicates if Telecom should cancel the call when the whole call redirection finishes. 303 */ 304 private boolean mShouldCancelCall = false; 305 /** 306 * Indicates Telecom should handle different types of UI if need. 307 */ 308 private String mUiAction = UI_TYPE_NO_ACTION; 309 /** 310 * Indicates if Telecom is waiting for a callback from a user-defined 311 * {@link CallRedirectionService}. 312 */ 313 private boolean mIsUserDefinedRedirectionPending = false; 314 /** 315 * Indicates if Telecom is waiting for a callback from a carrier 316 * {@link CallRedirectionService}. 317 */ 318 private boolean mIsCarrierRedirectionPending = false; 319 CallRedirectionProcessor( Context context, CallsManager callsManager, Call call, Uri handle, PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)320 public CallRedirectionProcessor( 321 Context context, 322 CallsManager callsManager, 323 Call call, 324 Uri handle, 325 PhoneAccountRegistrar phoneAccountRegistrar, 326 GatewayInfo gatewayInfo, 327 boolean speakerphoneOn, 328 int videoState) { 329 mContext = context; 330 mCallsManager = callsManager; 331 mCall = call; 332 mDestinationUri = handle; 333 mPhoneAccountHandle = call.getTargetPhoneAccount(); 334 mRedirectionGatewayInfo = gatewayInfo; 335 mSpeakerphoneOn = speakerphoneOn; 336 mVideoState = videoState; 337 mTimeoutsAdapter = callsManager.getTimeoutsAdapter(); 338 mTelecomLock = callsManager.getLock(); 339 /** 340 * The current rule to decide whether the implemented {@link CallRedirectionService} should 341 * allow interactive responses with users is only based on whether it is in car mode. 342 */ 343 mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarModeOrProjectionActive(); 344 mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper( 345 context, callsManager, phoneAccountRegistrar); 346 mProcessedDestinationUri = mCallRedirectionProcessorHelper.formatNumberForRedirection( 347 mDestinationUri); 348 mPostDialDigits = mCallRedirectionProcessorHelper.getPostDialDigits(mDestinationUri); 349 } 350 351 @Override onCallRedirectionComplete(Call call)352 public void onCallRedirectionComplete(Call call) { 353 // synchronized on mTelecomLock to enter into Telecom. 354 mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) { 355 @Override 356 public void loggedRun() { 357 if (mIsUserDefinedRedirectionPending) { 358 Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER); 359 mIsUserDefinedRedirectionPending = false; 360 if (mShouldCancelCall) { 361 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 362 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, 363 mVideoState, mShouldCancelCall, mUiAction); 364 } else { 365 // Use the current user for carrier call redirection 366 performCarrierCallRedirection(UserHandle.CURRENT); 367 } 368 } else if (mIsCarrierRedirectionPending) { 369 Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER); 370 mIsCarrierRedirectionPending = false; 371 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 372 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, 373 mVideoState, mShouldCancelCall, mUiAction); 374 } 375 } 376 }.prepare()); 377 } 378 379 /** 380 * The entry to perform call redirection of the call from (@link CallsManager) 381 */ performCallRedirection(UserHandle userHandleForCallRedirection)382 public void performCallRedirection(UserHandle userHandleForCallRedirection) { 383 // If the Gateway Info is set with intent, only request with carrier call redirection. 384 if (mRedirectionGatewayInfo != null) { 385 // Use the current user for carrier call redirection 386 performCarrierCallRedirection(UserHandle.CURRENT); 387 } else { 388 performUserDefinedCallRedirection(userHandleForCallRedirection); 389 } 390 } 391 performUserDefinedCallRedirection(UserHandle userHandleForCallRedirection)392 private void performUserDefinedCallRedirection(UserHandle userHandleForCallRedirection) { 393 Log.d(this, "performUserDefinedCallRedirection"); 394 ComponentName componentName = 395 mCallRedirectionProcessorHelper. 396 getUserDefinedCallRedirectionService(userHandleForCallRedirection); 397 if (componentName != null) { 398 mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED); 399 mAttempt.process(userHandleForCallRedirection); 400 mIsUserDefinedRedirectionPending = true; 401 processTimeoutForCallRedirection(SERVICE_TYPE_USER_DEFINED); 402 } else { 403 Log.i(this, "There are no user-defined call redirection services installed on this" 404 + " device."); 405 performCarrierCallRedirection(UserHandle.CURRENT); 406 } 407 } 408 performCarrierCallRedirection(UserHandle userHandleForCallRedirection)409 private void performCarrierCallRedirection(UserHandle userHandleForCallRedirection) { 410 Log.d(this, "performCarrierCallRedirection"); 411 ComponentName componentName = 412 mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( 413 mPhoneAccountHandle); 414 if (componentName != null) { 415 mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER); 416 mAttempt.process(userHandleForCallRedirection); 417 mIsCarrierRedirectionPending = true; 418 processTimeoutForCallRedirection(SERVICE_TYPE_CARRIER); 419 } else { 420 Log.i(this, "There are no carrier call redirection services installed on this" 421 + " device."); 422 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 423 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, mVideoState, 424 mShouldCancelCall, mUiAction); 425 } 426 } 427 processTimeoutForCallRedirection(String serviceType)428 private void processTimeoutForCallRedirection(String serviceType) { 429 long timeout = serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? 430 mTimeoutsAdapter.getUserDefinedCallRedirectionTimeoutMillis( 431 mContext.getContentResolver()) : mTimeoutsAdapter 432 .getCarrierCallRedirectionTimeoutMillis(mContext.getContentResolver()); 433 434 mHandler.postDelayed(new Runnable("CRP.pTFCR", null) { 435 @Override 436 public void loggedRun() { 437 boolean isCurrentRedirectionPending = 438 serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? 439 mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending; 440 if (isCurrentRedirectionPending) { 441 Log.i(this, serviceType + " call redirection has timed out."); 442 Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED) 443 ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER 444 : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER); 445 mAttempt.notifyTimeout(); 446 if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) { 447 mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT; 448 mShouldCancelCall = true; 449 } 450 onCallRedirectionComplete(mCall); 451 } 452 } 453 }.prepare(), timeout); 454 } 455 456 /** 457 * Checks if Telecom can make call redirection with any available call redirection service 458 * as the specified user. 459 * 460 * @return {@code true} if it can; {@code false} otherwise. 461 */ canMakeCallRedirectionWithServiceAsUser( UserHandle userHandleForCallRedirection)462 public boolean canMakeCallRedirectionWithServiceAsUser( 463 UserHandle userHandleForCallRedirection) { 464 boolean canMakeCallRedirectionWithServiceAsUser = 465 mCallRedirectionProcessorHelper 466 .getUserDefinedCallRedirectionService(userHandleForCallRedirection) != null 467 || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( 468 mPhoneAccountHandle) != null; 469 Log.i(this, "Can make call redirection with any " 470 + "available service as user (" + userHandleForCallRedirection 471 + ") : " + canMakeCallRedirectionWithServiceAsUser); 472 return canMakeCallRedirectionWithServiceAsUser; 473 } 474 475 /** 476 * Returns the handler, for testing purposes. 477 */ 478 @VisibleForTesting getHandler()479 public Handler getHandler() { 480 return mHandler; 481 } 482 483 /** 484 * Set CallRedirectionProcessorHelper for testing purposes. 485 */ 486 @VisibleForTesting setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper)487 public void setCallRedirectionServiceHelper( 488 CallRedirectionProcessorHelper callRedirectionProcessorHelper) { 489 mCallRedirectionProcessorHelper = callRedirectionProcessorHelper; 490 } 491 } 492