outgoing call 有三类, 分别用三种intent 去标记 三种intent 定义如下
* This method will handle three kinds of actions: * * - CALL (action for usual outgoing voice calls) * - CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth) * - CALL_EMERGENCY (from the EmergencyDialer that's reachable from the lockscreen.)
紧急电话的几种调用方式
拨打Emergency call 是不需要卡的。应该走的是一种特殊的链路。
Emergency call 被两种Intent所触发(Intent.ACTION_CALL_EMERGENCY 和 Intent.ACTION_CALL_PRIVILEGED). CALL_PRIVILEGED是被系统级的应用所发起的,而CALL_EMERGENCY Intent 是从紧急呼叫的拨号盘里发起的,就是手机在没有SIM卡的模式下显示的拨号盘。CALL_PRIVILEGED这个intent 接到后会检查号码是否是emergency number, 如果是紧急电话的号码就会把intent的action 替换为ACTION_CALL_EMERGENCY, 如果不是就替换成ACTION_CALL。由此可看,虽然我们有三种call的intent action, 但是我们实际有效果的就是上述两种。CALL_PRIVILEGED这种action的call的作用的是能把紧急电话的号码带给phone app去处理,而普通的call action的intent是不可以的。 processIntent实现里用一个flag变量callNow来控制是否拨打电话, callNow在满足拨打emengency call的时候被置为true,正常call会被在startSipCallOptionHandler 这里到类SipCallOptionHandler去处理。 3rd App是没有权限拨打Emergency call的, 如果3rd App在发起一个Intent.ACTION_CALL里带有Emergency number,会被阻止.OutgoingCallBroadcaster 类里的processIntent函数承担这部分的主要工作。 下面是google的代码
526 if (Intent.ACTION_CALL.equals(action)) { 527 if (isPotentialEmergencyNumber) { 528 Log.w(TAG, "Cannot call potential emergency number '" + number 529 + "' with CALL Intent " + intent + "."); 530 Log.i(TAG, "Launching default dialer instead..."); 531 532 Intent invokeFrameworkDialer = new Intent(); 533 534 // TwelveKeyDialer is in a tab so we really want 535 // DialtactsActivity. Build the intent 'manually' to 536 // use the java resolver to find the dialer class (as 537 // opposed to a Context which look up known android 538 // packages only) 539 final Resources resources = getResources(); 540 invokeFrameworkDialer.setClassName( 541 resources.getString(R.string.ui_default_package), 542 resources.getString(R.string.dialer_default_class)); 543 invokeFrameworkDialer.setAction(Intent.ACTION_DIAL); 544 invokeFrameworkDialer.setData(intent.getData()); 545 if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: " 546 + invokeFrameworkDialer); 547 startActivity(invokeFrameworkDialer); 548 finish(); 549 return; 550 } 551 callNow = false;
拨打紧急电话与modem的互动关系
我们所注意到的是在拨打紧急电话的时候,当我们发现是一个紧急电话并且modem 在power off的模式下,会开启一个消息队列去开启modem startEmergencyCallFromAirplaneModeSequence这里面会有个timeout 的操作, 如果modem超时没有被开启,还会等待modem开启中, 继续往这个队列里放入消息。TIME_BETWEEN_RETRIES = 5000; // msec
具体的实现关键点中发现timeout这个retry机制是发一个timeout的消息到队列里去处理 sendEmptyMessageDelayed(RETRY_TIMEOUTTIME_BETWEEN_RETRIES);
timeout 的时间是5s ,类EmergencyCallHelper承担这部分消息处理的逻辑以及收到消息后采取的措施,是继续开启modem还是拨打紧急电话。第三方应用是不能影响,阻止拨打紧急电话的。
不论是紧急电话还是正常的电话,第三方应用是在系统中拨出一个电话后是可以接到ACTION_NEW_OUTGOING_CALL 这样的一个intent的,SIP call除外; 系统中拨打的电话包括contacts / voice dialer / bluetooth / dialer 这里理解的系统中拨的电话就是上面所说的拨打电话中在code中调用到的CALL_PRIVILEGED 和 CALL_EMERGENCY这两种Intent。 但不同的是紧急电话不能被阻止,常规的号码是可以被第三方阻止的,第三方应该可以会接到这样一个通知。紧急电话是直接播出去后再发这个intent.
目前SIP call还不能被第三方拦截,也就是说上面提到的拨打一个SIP call之后直接return, 不会再去发一个ACTION_NEW_OUTGOING_CALL给外部,google在代码注释上写未来需要支持。 在processIntent的实现里用一个flag变量callNo来控制是否拨打电话, callNow在满足拨打emengency call的时候被置为true,正常call会被在startSipCallOptionHandler 这里到类SipCallOptionHandler去处理。具体实现参照packages/services/Telephony/src/com/android/phone/OutgoingCallBroadcaster.java424 private void processIntent(Intent intent) { 425 if (DBG) { 426 Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread()); 427 } 428 final Configuration configuration = getResources().getConfiguration(); 429 430 // Outgoing phone calls are only allowed on "voice-capable" devices. 431 if (!PhoneGlobals.sVoiceCapable) { 432 Log.i(TAG, "This device is detected as non-voice-capable device."); 433 handleNonVoiceCapable(intent); 434 return; 435 } 436 437 String action = intent.getAction(); 438 String number = PhoneNumberUtils.getNumberFromIntent(intent, this); 439 // Check the number, don't convert for sip uri 440 // TODO put uriNumber under PhoneNumberUtils 441 if (number != null) { 442 if (!PhoneNumberUtils.isUriNumber(number)) { 443 number = PhoneNumberUtils.convertKeypadLettersToDigits(number); 444 number = PhoneNumberUtils.stripSeparators(number); 445 } 446 } else { 447 Log.w(TAG, "The number obtained from Intent is null."); 448 } 449 450 AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); 451 int launchedFromUid; 452 String launchedFromPackage; 453 try { 454 launchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid( 455 getActivityToken()); 456 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage( 457 getActivityToken()); 458 } catch (RemoteException e) { 459 launchedFromUid = -1; 460 launchedFromPackage = null; 461 } 462 if (appOps.noteOp(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage) 463 != AppOpsManager.MODE_ALLOWED) { 464 Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package " 465 + launchedFromPackage); 466 finish(); 467 return; 468 } 469 470 // If true, this flag will indicate that the current call is a special kind 471 // of call (most likely an emergency number) that 3rd parties aren't allowed 472 // to intercept or affect in any way. (In that case, we start the call 473 // immediately rather than going through the NEW_OUTGOING_CALL sequence.) 474 boolean callNow; 475 476 if (getClass().getName().equals(intent.getComponent().getClassName())) { 477 // If we were launched directly from the OutgoingCallBroadcaster, 478 // not one of its more privileged aliases, then make sure that 479 // only the non-privileged actions are allowed. 480 if (!Intent.ACTION_CALL.equals(intent.getAction())) { 481 Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL"); 482 intent.setAction(Intent.ACTION_CALL); 483 } 484 } 485 486 // Check whether or not this is an emergency number, in order to 487 // enforce the restriction that only the CALL_PRIVILEGED and 488 // CALL_EMERGENCY intents are allowed to make emergency calls. 489 // 490 // (Note that the ACTION_CALL check below depends on the result of 491 // isPotentialLocalEmergencyNumber() rather than just plain 492 // isLocalEmergencyNumber(), to be 100% certain that we *don't* 493 // allow 3rd party apps to make emergency calls by passing in an 494 // "invalid" number like "9111234" that isn't technically an 495 // emergency number but might still result in an emergency call 496 // with some networks.) 497 final boolean isExactEmergencyNumber = 498 (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(number, this); 499 final boolean isPotentialEmergencyNumber = 500 (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, this); 501 if (VDBG) { 502 Log.v(TAG, " - Checking restrictions for number '" + number + "':"); 503 Log.v(TAG, " isExactEmergencyNumber = " + isExactEmergencyNumber); 504 Log.v(TAG, " isPotentialEmergencyNumber = " + isPotentialEmergencyNumber); 505 } 506 507 /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ 508 // TODO: This code is redundant with some code in InCallScreen: refactor. 509 if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) { 510 // We're handling a CALL_PRIVILEGED intent, so we know this request came 511 // from a trusted source (like the built-in dialer.) So even a number 512 // that's *potentially* an emergency number can safely be promoted to 513 // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from 514 // the dialer if you really want to.) 515 if (isPotentialEmergencyNumber) { 516 Log.i(TAG, "ACTION_CALL_PRIVILEGED is used while the number is a potential" 517 + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead."); 518 action = Intent.ACTION_CALL_EMERGENCY; 519 } else { 520 action = Intent.ACTION_CALL; 521 } 522 if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action); 523 intent.setAction(action); 524 } 525 当第三方App去拨打紧急电话时,会launch一个缺省的dialer然后直接return.也不会再去发Intent.ACTION_NEW_OUTGOING_CALL intent给外部使用 526 if (Intent.ACTION_CALL.equals(action)) { 527 if (isPotentialEmergencyNumber) { 528 Log.w(TAG, "Cannot call potential emergency number '" + number 529 + "' with CALL Intent " + intent + "."); 530 Log.i(TAG, "Launching default dialer instead..."); 531 532 Intent invokeFrameworkDialer = new Intent(); 533 534 // TwelveKeyDialer is in a tab so we really want 535 // DialtactsActivity. Build the intent 'manually' to 536 // use the java resolver to find the dialer class (as 537 // opposed to a Context which look up known android 538 // packages only) 539 final Resources resources = getResources(); 540 invokeFrameworkDialer.setClassName( 541 resources.getString(R.string.ui_default_package), 542 resources.getString(R.string.dialer_default_class)); 543 invokeFrameworkDialer.setAction(Intent.ACTION_DIAL); 544 invokeFrameworkDialer.setData(intent.getData()); 545 if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: " 546 + invokeFrameworkDialer); 547 startActivity(invokeFrameworkDialer); 548 finish(); 549 return; 550 } 551 callNow = false; 552 } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { 553 // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED 554 // intent that we just turned into a CALL_EMERGENCY intent (see 555 // above), or else it really is an CALL_EMERGENCY intent that 556 // came directly from some other app (e.g. the EmergencyDialer 557 // activity built in to the Phone app.) 558 // Make sure it's at least *possible* that this is really an 559 // emergency number. 560 if (!isPotentialEmergencyNumber) { 561 Log.w(TAG, "Cannot call non-potential-emergency number " + number 562 + " with EMERGENCY_CALL Intent " + intent + "." 563 + " Finish the Activity immediately."); 564 finish(); 565 return; 566 } 567 callNow = true; 568 } else { 569 Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately."); 570 finish(); 571 return; 572 } 573 574 // Make sure the screen is turned on. This is probably the right 575 // thing to do, and more importantly it works around an issue in the 576 // activity manager where we will not launch activities consistently 577 // when the screen is off (since it is trying to keep them paused 578 // and has... issues). 579 // 580 // Also, this ensures the device stays awake while doing the following 581 // broadcast; technically we should be holding a wake lock here 582 // as well. 583 PhoneGlobals.getInstance().wakeUpScreen(); 584 585 // If number is null, we're probably trying to call a non-existent voicemail number, 586 // send an empty flash or something else is fishy. Whatever the problem, there's no 587 // number, so there's no point in allowing apps to modify the number. 588 if (TextUtils.isEmpty(number)) { 589 if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) { 590 Log.i(TAG, "onCreate: SEND_EMPTY_FLASH..."); 591 PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone()); 592 finish(); 593 return; 594 } else { 595 Log.i(TAG, "onCreate: null or empty number, setting callNow=true..."); 596 callNow = true; 597 } 598 } 599 600 if (callNow) { 601 // This is a special kind of call (most likely an emergency number) 602 // that 3rd parties aren't allowed to intercept or affect in any way. 603 // So initiate the outgoing call immediately. 604 605 Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent); 606 607 // Initiate the outgoing call, and simultaneously launch the 608 // InCallScreen to display the in-call UI: 609 PhoneGlobals.getInstance().callController.placeCall(intent); 610 611 // Note we do *not* "return" here, but instead continue and 612 // send the ACTION_NEW_OUTGOING_CALL broadcast like for any 613 // other outgoing call. (But when the broadcast finally 614 // reaches the OutgoingCallReceiver, we'll know not to 615 // initiate the call again because of the presence of the 616 // EXTRA_ALREADY_CALLED extra.) 617 } 618 619 // For now, SIP calls will be processed directly without a 620 // NEW_OUTGOING_CALL broadcast. 621 // 622 // TODO: In the future, though, 3rd party apps *should* be allowed to 623 // intercept outgoing calls to SIP addresses as well. To do this, we should 624 // (1) update the NEW_OUTGOING_CALL intent documentation to explain this 625 // case, and (2) pass the outgoing SIP address by *not* overloading the 626 // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold 627 // the outgoing SIP address. (Be sure to document whether it's a URI or just 628 // a plain address, whether it could be a tel: URI, etc.) 629 Uri uri = intent.getData(); 630 String scheme = uri.getScheme(); 631 if (Constants.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) { 632 Log.i(TAG, "The requested number was detected as SIP call."); 633 startSipCallOptionHandler(this, intent, uri, number); 634 finish(); 635 return; 636 637 // TODO: if there's ever a way for SIP calls to trigger a 638 // "callNow=true" case (see above), we'll need to handle that 639 // case here too (most likely by just doing nothing at all.) 640 } 641 当去拨打一个电话(紧急电话或常规号码,不包括SIP call)后,暴露给外部一个ACTION_NEW_OUTGOING_CALL的intent 642 Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); 643 if (number != null) { 644 broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); 645 } 646 CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent); 647 broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow); 648 broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString()); 649 // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app 650 // to intercept the outgoing call. 651 broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 652 if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + "."); 653 654 // Set a timer so that we can prepare for unexpected delay introduced by the broadcast. 655 // If it takes too much time, the timer will show "waiting" spinner. 656 // This message will be removed when OutgoingCallReceiver#onReceive() is called before the 657 // timeout. 658 mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT, 659 OUTGOING_CALL_TIMEOUT_THRESHOLD); 660 sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER, 661 PERMISSION, new OutgoingCallReceiver(), 662 null, // scheduler 663 Activity.RESULT_OK, // initialCode 664 number, // initialData: initial value for the result data 665 null); // initialExtras 666 }