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