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