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开启中, 继续往这个队列里放入消息。
具体的实现关键点中发现timeout这个retry机制是发一个timeout的消息到队列里去处理 sendEmptyMessageDelayed(RETRY_TIMEOUTTIME_BETWEEN_RETRIES);
timeout 的时间是5s ,
TIME_BETWEEN_RETRIES =
5000;
// msec
类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.java
424 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 }