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); 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 * Indicates if Telecom should cancel the call when the whole call redirection finishes. 251 */ 252 private boolean mShouldCancelCall = false; 253 /** 254 * Indicates Telecom should handle different types of UI if need. 255 */ 256 private String mUiAction = UI_TYPE_NO_ACTION; 257 /** 258 * Indicates if Telecom is waiting for a callback from a user-defined 259 * {@link CallRedirectionService}. 260 */ 261 private boolean mIsUserDefinedRedirectionPending = false; 262 /** 263 * Indicates if Telecom is waiting for a callback from a carrier 264 * {@link CallRedirectionService}. 265 */ 266 private boolean mIsCarrierRedirectionPending = false; 267 CallRedirectionProcessor( Context context, CallsManager callsManager, Call call, Uri handle, PhoneAccountRegistrar phoneAccountRegistrar, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)268 public CallRedirectionProcessor( 269 Context context, 270 CallsManager callsManager, 271 Call call, 272 Uri handle, 273 PhoneAccountRegistrar phoneAccountRegistrar, 274 GatewayInfo gatewayInfo, 275 boolean speakerphoneOn, 276 int videoState) { 277 mContext = context; 278 mCallsManager = callsManager; 279 mCall = call; 280 mDestinationUri = handle; 281 mPhoneAccountHandle = call.getTargetPhoneAccount(); 282 mRedirectionGatewayInfo = gatewayInfo; 283 mSpeakerphoneOn = speakerphoneOn; 284 mVideoState = videoState; 285 mTimeoutsAdapter = callsManager.getTimeoutsAdapter(); 286 mTelecomLock = callsManager.getLock(); 287 /** 288 * The current rule to decide whether the implemented {@link CallRedirectionService} should 289 * allow interactive responses with users is only based on whether it is in car mode. 290 */ 291 mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarMode(); 292 mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper( 293 context, callsManager, phoneAccountRegistrar); 294 mProcessedDestinationUri = mCallRedirectionProcessorHelper.formatNumberForRedirection( 295 mDestinationUri); 296 } 297 298 @Override onCallRedirectionComplete(Call call)299 public void onCallRedirectionComplete(Call call) { 300 // synchronized on mTelecomLock to enter into Telecom. 301 mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) { 302 @Override 303 public void loggedRun() { 304 if (mIsUserDefinedRedirectionPending) { 305 Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER); 306 mIsUserDefinedRedirectionPending = false; 307 if (mShouldCancelCall) { 308 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 309 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, 310 mVideoState, mShouldCancelCall, mUiAction); 311 } else { 312 performCarrierCallRedirection(); 313 } 314 } 315 if (mIsCarrierRedirectionPending) { 316 Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER); 317 mIsCarrierRedirectionPending = false; 318 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 319 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, 320 mVideoState, mShouldCancelCall, mUiAction); 321 } 322 } 323 }.prepare()); 324 } 325 326 /** 327 * The entry to perform call redirection of the call from (@link CallsManager) 328 */ performCallRedirection()329 public void performCallRedirection() { 330 // If the Gateway Info is set with intent, only request with carrier call redirection. 331 if (mRedirectionGatewayInfo != null) { 332 performCarrierCallRedirection(); 333 } else { 334 performUserDefinedCallRedirection(); 335 } 336 } 337 performUserDefinedCallRedirection()338 private void performUserDefinedCallRedirection() { 339 Log.d(this, "performUserDefinedCallRedirection"); 340 ComponentName componentName = 341 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService(); 342 if (componentName != null) { 343 mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED); 344 mAttempt.process(); 345 mIsUserDefinedRedirectionPending = true; 346 processTimeoutForCallRedirection(SERVICE_TYPE_USER_DEFINED); 347 } else { 348 Log.i(this, "There are no user-defined call redirection services installed on this" 349 + " device."); 350 performCarrierCallRedirection(); 351 } 352 } 353 performCarrierCallRedirection()354 private void performCarrierCallRedirection() { 355 Log.d(this, "performCarrierCallRedirection"); 356 ComponentName componentName = 357 mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( 358 mPhoneAccountHandle); 359 if (componentName != null) { 360 mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER); 361 mAttempt.process(); 362 mIsCarrierRedirectionPending = true; 363 processTimeoutForCallRedirection(SERVICE_TYPE_CARRIER); 364 } else { 365 Log.i(this, "There are no carrier call redirection services installed on this" 366 + " device."); 367 mCallsManager.onCallRedirectionComplete(mCall, mDestinationUri, 368 mPhoneAccountHandle, mRedirectionGatewayInfo, mSpeakerphoneOn, mVideoState, 369 mShouldCancelCall, mUiAction); 370 } 371 } 372 processTimeoutForCallRedirection(String serviceType)373 private void processTimeoutForCallRedirection(String serviceType) { 374 long timeout = serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? 375 mTimeoutsAdapter.getUserDefinedCallRedirectionTimeoutMillis( 376 mContext.getContentResolver()) : mTimeoutsAdapter 377 .getCarrierCallRedirectionTimeoutMillis(mContext.getContentResolver()); 378 379 mHandler.postDelayed(new Runnable("CRP.pTFCR", null) { 380 @Override 381 public void loggedRun() { 382 boolean isCurrentRedirectionPending = 383 serviceType.equals(SERVICE_TYPE_USER_DEFINED) ? 384 mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending; 385 if (isCurrentRedirectionPending) { 386 Log.i(this, serviceType + " call redirection has timed out."); 387 Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED) 388 ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER 389 : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER); 390 if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) { 391 mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT; 392 mShouldCancelCall = true; 393 } 394 onCallRedirectionComplete(mCall); 395 } 396 } 397 }.prepare(), timeout); 398 } 399 400 /** 401 * Checks if Telecom can make call redirection with any available call redirection service. 402 * 403 * @return {@code true} if it can; {@code false} otherwise. 404 */ canMakeCallRedirectionWithService()405 public boolean canMakeCallRedirectionWithService() { 406 boolean canMakeCallRedirectionWithService = 407 mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null 408 || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService( 409 mPhoneAccountHandle) != null; 410 Log.i(this, "Can make call redirection with any available service: " 411 + canMakeCallRedirectionWithService); 412 return canMakeCallRedirectionWithService; 413 } 414 415 /** 416 * Returns the handler, for testing purposes. 417 */ 418 @VisibleForTesting getHandler()419 public Handler getHandler() { 420 return mHandler; 421 } 422 423 /** 424 * Set CallRedirectionProcessorHelper for testing purposes. 425 */ 426 @VisibleForTesting setCallRedirectionServiceHelper( CallRedirectionProcessorHelper callRedirectionProcessorHelper)427 public void setCallRedirectionServiceHelper( 428 CallRedirectionProcessorHelper callRedirectionProcessorHelper) { 429 mCallRedirectionProcessorHelper = callRedirectionProcessorHelper; 430 } 431 } 432