1 /* 2 * Copyright (C) 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 17 package com.android.server.telecom; 18 19 import android.Manifest; 20 import android.app.Activity; 21 import android.app.AppOpsManager; 22 import android.app.BroadcastOptions; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.Trace; 29 import android.os.UserHandle; 30 import android.telecom.GatewayInfo; 31 import android.telecom.Log; 32 import android.telecom.PhoneAccount; 33 import android.telecom.PhoneAccountHandle; 34 import android.telecom.TelecomManager; 35 import android.telecom.VideoProfile; 36 import android.telephony.DisconnectCause; 37 import android.telephony.TelephonyManager; 38 import android.text.TextUtils; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.server.telecom.flags.FeatureFlags; 42 import com.android.server.telecom.callredirection.CallRedirectionProcessor; 43 44 // TODO: Needed for move to system service: import com.android.internal.R; 45 46 /** 47 * OutgoingCallIntentBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the 48 * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which 49 * contains the phone number being dialed. Applications can use this intent to (1) see which numbers 50 * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call 51 * from being placed. 52 * 53 * After the other applications have had a chance to see the ACTION_NEW_OUTGOING_CALL intent, it 54 * finally reaches the {@link NewOutgoingCallBroadcastIntentReceiver}. 55 * 56 * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail 57 * number) are exempt from being broadcast. 58 * 59 * Calls to emergency numbers are still broadcast for informative purposes. The call is placed 60 * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented. 61 */ 62 @VisibleForTesting 63 public class NewOutgoingCallIntentBroadcaster { 64 /** 65 * Legacy string constants used to retrieve gateway provider extras from intents. These still 66 * need to be copied from the source call intent to the destination intent in order to 67 * support third party gateway providers that are still using old string constants in 68 * Telephony. 69 */ 70 public static final String EXTRA_GATEWAY_PROVIDER_PACKAGE = 71 "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE"; 72 public static final String EXTRA_GATEWAY_URI = "com.android.phone.extra.GATEWAY_URI"; 73 74 private final CallsManager mCallsManager; 75 private Call mCall; 76 private final Intent mIntent; 77 private final Context mContext; 78 private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter; 79 private final TelecomSystem.SyncRoot mLock; 80 private final DefaultDialerCache mDefaultDialerCache; 81 private final MmiUtils mMmiUtils; 82 private final FeatureFlags mFeatureFlags; 83 84 /* 85 * Whether or not the outgoing call intent originated from the default phone application. If 86 * so, it will be allowed to make emergency calls, even with the ACTION_CALL intent. 87 */ 88 private final boolean mIsDefaultOrSystemPhoneApp; 89 90 public static class CallDisposition { 91 // True for certain types of numbers that are not intended to be intercepted or modified 92 // by third parties (e.g. emergency numbers). 93 public boolean callImmediately = false; 94 // True for all managed calls, false for self-managed calls. 95 public boolean sendBroadcast = true; 96 // True for requesting call redirection, false for not requesting it. 97 public boolean requestRedirection = true; 98 public int disconnectCause = DisconnectCause.NOT_DISCONNECTED; 99 String number; 100 Uri callingAddress; 101 } 102 103 @VisibleForTesting NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache, MmiUtils mmiUtils, FeatureFlags featureFlags)104 public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, 105 Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 106 boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache, MmiUtils mmiUtils, 107 FeatureFlags featureFlags) { 108 mContext = context; 109 mCallsManager = callsManager; 110 mIntent = intent; 111 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter; 112 mIsDefaultOrSystemPhoneApp = isDefaultPhoneApp; 113 mLock = mCallsManager.getLock(); 114 mDefaultDialerCache = defaultDialerCache; 115 mMmiUtils = mmiUtils; 116 mFeatureFlags = featureFlags; 117 } 118 119 /** 120 * Processes the result of the outgoing call broadcast intent, and performs callbacks to 121 * the OutgoingCallIntentBroadcasterListener as necessary. 122 */ 123 public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver { 124 125 @Override onReceive(Context context, Intent intent)126 public void onReceive(Context context, Intent intent) { 127 try { 128 Log.startSession("NOCBIR.oR"); 129 Trace.beginSection("onReceiveNewOutgoingCallBroadcast"); 130 synchronized (mLock) { 131 Log.v(this, "onReceive: %s", intent); 132 133 // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData is 134 // used as the actual number to call. (If null, no call will be placed.) 135 String resultNumber = getResultData(); 136 Log.i(NewOutgoingCallIntentBroadcaster.this, 137 "Received new-outgoing-call-broadcast for %s with data %s", mCall, 138 Log.pii(resultNumber)); 139 140 boolean endEarly = false; 141 long disconnectTimeout = 142 Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()); 143 if (resultNumber == null) { 144 Log.v(this, "Call cancelled (null number), returning..."); 145 disconnectTimeout = getDisconnectTimeoutFromApp( 146 getResultExtras(false), disconnectTimeout); 147 endEarly = true; 148 } else if (isEmergencyNumber(resultNumber)) { 149 Log.w(this, "Cannot modify outgoing call to emergency number %s.", 150 resultNumber); 151 disconnectTimeout = 0; 152 endEarly = true; 153 } 154 155 if (endEarly) { 156 if (mCall != null) { 157 mCall.disconnect(disconnectTimeout); 158 } 159 return; 160 } 161 162 // If this call is already disconnected then we have nothing more to do. 163 if (mCall.isDisconnected()) { 164 Log.w(this, "Call has already been disconnected," + 165 " ignore the broadcast Call %s", mCall); 166 return; 167 } 168 169 // TODO: Remove the assumption that phone numbers are either SIP or TEL. 170 // This does not impact self-managed ConnectionServices as they do not use the 171 // NewOutgoingCallIntentBroadcaster. 172 Uri resultHandleUri = Uri.fromParts( 173 mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ? 174 PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, 175 resultNumber, null); 176 177 Uri originalUri = mIntent.getData(); 178 179 if (originalUri.getSchemeSpecificPart().equals(resultNumber)) { 180 Log.v(this, "Call number unmodified after" + 181 " new outgoing call intent broadcast."); 182 } else { 183 Log.v(this, "Retrieved modified handle after outgoing call intent" + 184 " broadcast: Original: %s, Modified: %s", 185 Log.pii(originalUri), 186 Log.pii(resultHandleUri)); 187 } 188 189 GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri); 190 placeOutgoingCallImmediately(mCall, resultHandleUri, gatewayInfo, 191 mIntent.getBooleanExtra( 192 TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false), 193 mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 194 VideoProfile.STATE_AUDIO_ONLY)); 195 } 196 } finally { 197 Trace.endSection(); 198 Log.endSession(); 199 } 200 } 201 } 202 203 /** 204 * Processes the supplied intent and starts the outgoing call broadcast process relevant to the 205 * intent. 206 * 207 * This method will handle three kinds of actions: 208 * 209 * - CALL (intent launched by all third party dialers) 210 * - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer) 211 * - CALL_EMERGENCY (intent launched by lock screen emergency dialer) 212 * 213 * @return {@link DisconnectCause#NOT_DISCONNECTED} if the call succeeded, and an appropriate 214 * {@link DisconnectCause} if the call did not, describing why it failed. 215 */ 216 @VisibleForTesting evaluateCall()217 public CallDisposition evaluateCall() { 218 CallDisposition result = new CallDisposition(); 219 220 Intent intent = mIntent; 221 String action = intent.getAction(); 222 final Uri handle = intent.getData(); 223 224 if (handle == null) { 225 Log.w(this, "Empty handle obtained from the call intent."); 226 result.disconnectCause = DisconnectCause.INVALID_NUMBER; 227 return result; 228 } 229 230 boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme()); 231 if (isVoicemailNumber) { 232 if (Intent.ACTION_CALL.equals(action) 233 || Intent.ACTION_CALL_PRIVILEGED.equals(action)) { 234 // Voicemail calls will be handled directly by the telephony connection manager 235 Log.i(this, "Voicemail number dialed. Skipping redirection and broadcast", intent); 236 mIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 237 VideoProfile.STATE_AUDIO_ONLY); 238 result.callImmediately = true; 239 result.requestRedirection = false; 240 result.sendBroadcast = false; 241 result.callingAddress = handle; 242 return result; 243 } else { 244 Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent); 245 result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; 246 return result; 247 } 248 } 249 250 PhoneAccountHandle targetPhoneAccount = mIntent.getParcelableExtra( 251 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); 252 boolean isSelfManaged = false; 253 if (targetPhoneAccount != null) { 254 PhoneAccount phoneAccount = 255 mCallsManager.getPhoneAccountRegistrar().getPhoneAccountUnchecked( 256 targetPhoneAccount); 257 if (phoneAccount != null) { 258 isSelfManaged = phoneAccount.isSelfManaged(); 259 } 260 } 261 262 result.number = ""; 263 result.callingAddress = handle; 264 265 if (isSelfManaged) { 266 // Self-managed call. 267 result.callImmediately = true; 268 result.sendBroadcast = false; 269 result.requestRedirection = false; 270 Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call."); 271 return result; 272 } 273 274 // Placing a managed call 275 String number = getNumberFromCallIntent(intent); 276 result.number = number; 277 if (number == null) { 278 result.disconnectCause = DisconnectCause.NO_PHONE_NUMBER_SUPPLIED; 279 return result; 280 } 281 282 final boolean isEmergencyNumber = isEmergencyNumber(number); 283 Log.v(this, "isEmergencyNumber = %s", isEmergencyNumber); 284 285 action = calculateCallIntentAction(intent, isEmergencyNumber); 286 intent.setAction(action); 287 288 if (Intent.ACTION_CALL.equals(action)) { 289 if (isEmergencyNumber) { 290 if (!mIsDefaultOrSystemPhoneApp) { 291 Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s " 292 + "unless caller is system or default dialer.", number, intent); 293 launchSystemDialer(intent.getData()); 294 result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; 295 return result; 296 } else { 297 result.callImmediately = true; 298 result.requestRedirection = false; 299 } 300 } else if (mMmiUtils.isDangerousMmiOrVerticalCode(intent.getData())) { 301 if (!mIsDefaultOrSystemPhoneApp) { 302 Log.w(this, 303 "Potentially dangerous MMI code %s with CALL Intent %s can only be " 304 + "sent if caller is the system or default dialer", 305 number, intent); 306 launchSystemDialer(intent.getData()); 307 result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; 308 return result; 309 } 310 } 311 } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { 312 if (!isEmergencyNumber) { 313 Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL " 314 + "Intent %s.", number, intent); 315 result.disconnectCause = DisconnectCause.OUTGOING_CANCELED; 316 return result; 317 } 318 result.callImmediately = true; 319 result.requestRedirection = false; 320 } else { 321 Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent); 322 result.disconnectCause = DisconnectCause.INVALID_NUMBER; 323 return result; 324 } 325 326 String scheme = mPhoneNumberUtilsAdapter.isUriNumber(number) 327 ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL; 328 result.callingAddress = Uri.fromParts(scheme, number, null); 329 330 return result; 331 } 332 getNumberFromCallIntent(Intent intent)333 private String getNumberFromCallIntent(Intent intent) { 334 String number = null; 335 336 Uri uri = intent.getData(); 337 if (uri != null) { 338 String scheme = uri.getScheme(); 339 if (scheme != null) { 340 if (scheme.equals("tel") || scheme.equals("sip")) { 341 number = uri.getSchemeSpecificPart(); 342 } 343 } 344 } 345 346 if (TextUtils.isEmpty(number)) { 347 Log.w(this, "Empty number obtained from the call intent."); 348 return null; 349 } 350 351 boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number); 352 if (!isUriNumber) { 353 number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number); 354 number = mPhoneNumberUtilsAdapter.stripSeparators(number); 355 } 356 return number; 357 } 358 processCall(Call call, CallDisposition disposition)359 public void processCall(Call call, CallDisposition disposition) { 360 mCall = call; 361 362 // If the new outgoing call broadast doesn't block, trigger the legacy process call 363 // behavior and exit out here. 364 if (!mFeatureFlags.isNewOutgoingCallBroadcastUnblocking()) { 365 legacyProcessCall(disposition); 366 return; 367 } 368 boolean callRedirectionWithService = false; 369 // Only try to do redirection if it was requested and we're not calling immediately. 370 // We can expect callImmediately to be true for emergency calls and voip calls. 371 if (disposition.requestRedirection && !disposition.callImmediately) { 372 CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor( 373 mContext, mCallsManager, mCall, disposition.callingAddress, 374 mCallsManager.getPhoneAccountRegistrar(), 375 getGateWayInfoFromIntent(mIntent, mIntent.getData()), 376 mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, 377 false), 378 mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 379 VideoProfile.STATE_AUDIO_ONLY)); 380 /** 381 * If there is an available {@link android.telecom.CallRedirectionService}, use the 382 * {@link CallRedirectionProcessor} to perform call redirection instead of using 383 * broadcasting. 384 */ 385 callRedirectionWithService = callRedirectionProcessor 386 .canMakeCallRedirectionWithServiceAsUser(mCall.getAssociatedUser()); 387 if (callRedirectionWithService) { 388 callRedirectionProcessor.performCallRedirection(mCall.getAssociatedUser()); 389 } 390 } 391 392 // If no redirection was kicked off, place the call now. 393 if (!callRedirectionWithService) { 394 callImmediately(disposition); 395 } 396 397 // Finally, send the non-blocking broadcast if we're supposed to (ie for any non-voip call). 398 if (disposition.sendBroadcast) { 399 UserHandle targetUser = mCall.getAssociatedUser(); 400 broadcastIntent(mIntent, disposition.number, false /* receiverRequired */, targetUser); 401 } 402 } 403 404 /** 405 * The legacy non-flagged version of processing a call. Although there is some code duplication 406 * if makes the new flow cleaner to read. 407 * @param disposition 408 */ legacyProcessCall(CallDisposition disposition)409 private void legacyProcessCall(CallDisposition disposition) { 410 if (disposition.callImmediately) { 411 callImmediately(disposition); 412 413 // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast 414 // so that third parties can still inspect (but not intercept) the outgoing call. When 415 // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to 416 // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra. 417 } 418 419 boolean callRedirectionWithService = false; 420 if (disposition.requestRedirection) { 421 CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor( 422 mContext, mCallsManager, mCall, disposition.callingAddress, 423 mCallsManager.getPhoneAccountRegistrar(), 424 getGateWayInfoFromIntent(mIntent, mIntent.getData()), 425 mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, 426 false), 427 mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 428 VideoProfile.STATE_AUDIO_ONLY)); 429 /** 430 * If there is an available {@link android.telecom.CallRedirectionService}, use the 431 * {@link CallRedirectionProcessor} to perform call redirection instead of using 432 * broadcasting. 433 */ 434 callRedirectionWithService = callRedirectionProcessor 435 .canMakeCallRedirectionWithServiceAsUser(mCall.getAssociatedUser()); 436 if (callRedirectionWithService) { 437 callRedirectionProcessor.performCallRedirection(mCall.getAssociatedUser()); 438 } 439 } 440 441 if (disposition.sendBroadcast) { 442 UserHandle targetUser = mCall.getAssociatedUser(); 443 broadcastIntent(mIntent, disposition.number, 444 !disposition.callImmediately && !callRedirectionWithService, targetUser); 445 } 446 } 447 448 /** 449 * Place a call immediately. 450 * @param disposition The disposition; used for retrieving the address of the call. 451 */ callImmediately(CallDisposition disposition)452 private void callImmediately(CallDisposition disposition) { 453 boolean speakerphoneOn = mIntent.getBooleanExtra( 454 TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false); 455 int videoState = mIntent.getIntExtra( 456 TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 457 VideoProfile.STATE_AUDIO_ONLY); 458 placeOutgoingCallImmediately(mCall, disposition.callingAddress, null, 459 speakerphoneOn, videoState); 460 } 461 462 /** 463 * Sends a new outgoing call ordered broadcast so that third party apps can cancel the 464 * placement of the call or redirect it to a different number. 465 * 466 * @param originalCallIntent The original call intent. 467 * @param number Call number that was stored in the original call intent. 468 * @param receiverRequired Whether or not the result from the ordered broadcast should be 469 * processed using a {@link NewOutgoingCallIntentBroadcaster}. 470 * @param targetUser User that the broadcast sent to. 471 */ broadcastIntent( Intent originalCallIntent, String number, boolean receiverRequired, UserHandle targetUser)472 private void broadcastIntent( 473 Intent originalCallIntent, 474 String number, 475 boolean receiverRequired, 476 UserHandle targetUser) { 477 Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); 478 if (number != null) { 479 broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); 480 } 481 Log.v(this, "Broadcasting intent: %s.", broadcastIntent); 482 483 checkAndCopyProviderExtras(originalCallIntent, broadcastIntent); 484 485 if (mFeatureFlags.isNewOutgoingCallBroadcastUnblocking()) { 486 // Where the new outgoing call broadcast is unblocking, do not give receiver FG priority 487 // and do not allow background activity starts. 488 broadcastIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 489 Log.i(this, "broadcastIntent: Sending non-blocking for %s to %s", mCall.getId(), 490 targetUser); 491 if (mFeatureFlags.telecomResolveHiddenDependencies()) { 492 mContext.sendBroadcastAsUser( 493 broadcastIntent, 494 targetUser, 495 Manifest.permission.PROCESS_OUTGOING_CALLS); 496 } else { 497 mContext.sendBroadcastAsUser( 498 broadcastIntent, 499 targetUser, 500 android.Manifest.permission.PROCESS_OUTGOING_CALLS, 501 AppOpsManager.OP_PROCESS_OUTGOING_CALLS); // initialExtras 502 } 503 } else { 504 Log.i(this, "broadcastIntent: Sending ordered for %s to %s, waitForResult=%b", 505 mCall.getId(), targetUser, receiverRequired); 506 final BroadcastOptions options = BroadcastOptions.makeBasic(); 507 options.setBackgroundActivityStartsAllowed(true); 508 // Force receivers of this broadcast intent to run at foreground priority because we 509 // want to finish processing the broadcast intent as soon as possible. 510 broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND 511 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 512 513 mContext.sendOrderedBroadcastAsUser( 514 broadcastIntent, 515 targetUser, 516 android.Manifest.permission.PROCESS_OUTGOING_CALLS, 517 AppOpsManager.OP_PROCESS_OUTGOING_CALLS, 518 options.toBundle(), 519 receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null, 520 null, // scheduler 521 Activity.RESULT_OK, // initialCode 522 number, // initialData: initial value for the result data (number to be 523 // modified) 524 null); // initialExtras 525 } 526 } 527 528 /** 529 * Copy all the expected extras set when a 3rd party gateway provider is to be used, from the 530 * source intent to the destination one. 531 * 532 * @param src Intent which may contain the provider's extras. 533 * @param dst Intent where a copy of the extras will be added if applicable. 534 */ checkAndCopyProviderExtras(Intent src, Intent dst)535 public void checkAndCopyProviderExtras(Intent src, Intent dst) { 536 if (src == null) { 537 return; 538 } 539 if (hasGatewayProviderExtras(src)) { 540 dst.putExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE, 541 src.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE)); 542 dst.putExtra(EXTRA_GATEWAY_URI, 543 src.getStringExtra(EXTRA_GATEWAY_URI)); 544 Log.d(this, "Found and copied gateway provider extras to broadcast intent."); 545 return; 546 } 547 548 Log.d(this, "No provider extras found in call intent."); 549 } 550 551 /** 552 * Check if valid gateway provider information is stored as extras in the intent 553 * 554 * @param intent to check for 555 * @return true if the intent has all the gateway information extras needed. 556 */ hasGatewayProviderExtras(Intent intent)557 private boolean hasGatewayProviderExtras(Intent intent) { 558 final String name = intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE); 559 final String uriString = intent.getStringExtra(EXTRA_GATEWAY_URI); 560 561 return !TextUtils.isEmpty(name) && !TextUtils.isEmpty(uriString); 562 } 563 getGatewayUriFromString(String gatewayUriString)564 private static Uri getGatewayUriFromString(String gatewayUriString) { 565 return TextUtils.isEmpty(gatewayUriString) ? null : Uri.parse(gatewayUriString); 566 } 567 568 /** 569 * Extracts gateway provider information from a provided intent.. 570 * 571 * @param intent to extract gateway provider information from. 572 * @param trueHandle The actual call handle that the user is trying to dial 573 * @return GatewayInfo object containing extracted gateway provider information as well as 574 * the actual handle the user is trying to dial. 575 */ getGateWayInfoFromIntent(Intent intent, Uri trueHandle)576 public static GatewayInfo getGateWayInfoFromIntent(Intent intent, Uri trueHandle) { 577 if (intent == null) { 578 return null; 579 } 580 581 // Check if gateway extras are present. 582 String gatewayPackageName = intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE); 583 Uri gatewayUri = getGatewayUriFromString(intent.getStringExtra(EXTRA_GATEWAY_URI)); 584 if (!TextUtils.isEmpty(gatewayPackageName) && gatewayUri != null) { 585 return new GatewayInfo(gatewayPackageName, gatewayUri, trueHandle); 586 } 587 588 return null; 589 } 590 placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)591 private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo, 592 boolean speakerphoneOn, int videoState) { 593 Log.i(this, 594 "Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver"); 595 // Since we are not going to go through "Outgoing call broadcast", make sure 596 // we mark it as ready. 597 mCall.setNewOutgoingCallIntentBroadcastIsDone(); 598 mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState); 599 } 600 launchSystemDialer(Uri handle)601 private void launchSystemDialer(Uri handle) { 602 Intent systemDialerIntent = new Intent(); 603 systemDialerIntent.setComponent(mDefaultDialerCache.getDialtactsSystemDialerComponent()); 604 systemDialerIntent.setAction(Intent.ACTION_DIAL); 605 systemDialerIntent.setData(handle); 606 systemDialerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 607 Log.v(this, "calling startActivity for default dialer: %s", systemDialerIntent); 608 mContext.startActivityAsUser(systemDialerIntent, UserHandle.CURRENT); 609 } 610 611 /** 612 * Check whether or not this is an emergency number, in order to enforce the restriction 613 * that only the CALL_PRIVILEGED and CALL_EMERGENCY intents are allowed to make emergency 614 * calls. 615 * 616 * @param number number to inspect in order to determine whether or not an emergency number 617 * is being dialed 618 * @return True if the handle is an emergency number. 619 */ isEmergencyNumber(String number)620 private boolean isEmergencyNumber(String number) { 621 Log.v(this, "Checking restrictions for number : %s", Log.pii(number)); 622 if (number == null) return false; 623 try { 624 return mContext.getSystemService(TelephonyManager.class).isEmergencyNumber( 625 number); 626 } catch (UnsupportedOperationException uoe) { 627 Log.w(this, "isEmergencyNumber: Telephony not supported"); 628 return false; 629 } catch (Exception e) { 630 Log.e(this, e, "isEmergencyNumber: Telephony threw an exception."); 631 return false; 632 } 633 } 634 635 /** 636 * Given a call intent and whether or not the number to dial is an emergency number, determine 637 * the appropriate call intent action. 638 * 639 * @param intent Intent to evaluate 640 * @param isEmergencyNumber Whether or not the number is an emergency 641 * number. 642 * @return The appropriate action. 643 */ calculateCallIntentAction(Intent intent, boolean isEmergencyNumber)644 private String calculateCallIntentAction(Intent intent, boolean isEmergencyNumber) { 645 String action = intent.getAction(); 646 647 /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ 648 if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) { 649 if (isEmergencyNumber) { 650 Log.i(this, "ACTION_CALL_PRIVILEGED is used while the number is a" 651 + " emergency number. Using ACTION_CALL_EMERGENCY as an action instead."); 652 action = Intent.ACTION_CALL_EMERGENCY; 653 } else { 654 action = Intent.ACTION_CALL; 655 } 656 Log.v(this, " - updating action from CALL_PRIVILEGED to %s", action); 657 } 658 return action; 659 } 660 getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout)661 private long getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout) { 662 if (resultExtras != null) { 663 long disconnectTimeout = resultExtras.getLong( 664 TelecomManager.EXTRA_NEW_OUTGOING_CALL_CANCEL_TIMEOUT, defaultTimeout); 665 if (disconnectTimeout < 0) { 666 disconnectTimeout = 0; 667 } 668 return Math.min(disconnectTimeout, 669 Timeouts.getMaxNewOutgoingCallCancelMillis(mContext.getContentResolver())); 670 } else { 671 return defaultTimeout; 672 } 673 } 674 } 675