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 '" + number529                        + "' with CALL Intent " + intent + ".");530                Log.i(TAG, "Launching default dialer instead...");531532                Intent invokeFrameworkDialer = new Intent();533534                // TwelveKeyDialer is in a tab so we really want535                // DialtactsActivity.  Build the intent 'manually' to536                // use the java resolver to find the dialer class (as537                // opposed to a Context which look up known android538                // 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();429430        // 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        }436437        String action = intent.getAction();438        String number = PhoneNumberUtils.getNumberFromIntent(intent, this);439        // Check the number, don't convert for sip uri440        // TODO put uriNumber under PhoneNumberUtils441        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        }449450        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        }469470        // If true, this flag will indicate that the current call is a special kind471        // of call (most likely an emergency number) that 3rd parties aren't allowed472        // to intercept or affect in any way.  (In that case, we start the call473        // immediately rather than going through the NEW_OUTGOING_CALL sequence.)474        boolean callNow;475476        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 that479            // 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        }485486        // Check whether or not this is an emergency number, in order to487        // enforce the restriction that only the CALL_PRIVILEGED and488        // CALL_EMERGENCY intents are allowed to make emergency calls.489        //490        // (Note that the ACTION_CALL check below depends on the result of491        // isPotentialLocalEmergencyNumber() rather than just plain492        // isLocalEmergencyNumber(), to be 100% certain that we *don't*493        // allow 3rd party apps to make emergency calls by passing in an494        // "invalid" number like "9111234" that isn't technically an495        // emergency number but might still result in an emergency call496        // 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        }506507        /* 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 came511            // from a trusted source (like the built-in dialer.)  So even a number512            // that's *potentially* an emergency number can safely be promoted to513            // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from514            // 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 '" + number529                        + "' with CALL Intent " + intent + ".");530                Log.i(TAG, "Launching default dialer instead...");531532                Intent invokeFrameworkDialer = new Intent();533534                // TwelveKeyDialer is in a tab so we really want535                // DialtactsActivity.  Build the intent 'manually' to536                // use the java resolver to find the dialer class (as537                // opposed to a Context which look up known android538                // 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_PRIVILEGED554            // intent that we just turned into a CALL_EMERGENCY intent (see555            // above), or else it really is an CALL_EMERGENCY intent that556            // came directly from some other app (e.g. the EmergencyDialer557            // activity built in to the Phone app.)558            // Make sure it's at least *possible* that this is really an559            // emergency number.560            if (!isPotentialEmergencyNumber) {561                Log.w(TAG, "Cannot call non-potential-emergency number " + number562                        + " 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        }573574        // Make sure the screen is turned on.  This is probably the right575        // thing to do, and more importantly it works around an issue in the576        // activity manager where we will not launch activities consistently577        // when the screen is off (since it is trying to keep them paused578        // and has...  issues).579        //580        // Also, this ensures the device stays awake while doing the following581        // broadcast; technically we should be holding a wake lock here582        // as well.583        PhoneGlobals.getInstance().wakeUpScreen();584585        // 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 no587        // 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        }599600        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.604605            Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent);606607            // Initiate the outgoing call, and simultaneously launch the608            // InCallScreen to display the in-call UI:609            PhoneGlobals.getInstance().callController.placeCall(intent);610611            // Note we do *not* "return" here, but instead continue and612            // send the ACTION_NEW_OUTGOING_CALL broadcast like for any613            // other outgoing call.  (But when the broadcast finally614            // reaches the OutgoingCallReceiver, we'll know not to615            // initiate the call again because of the presence of the616            // EXTRA_ALREADY_CALLED extra.)617        }618619        // For now, SIP calls will be processed directly without a620        // NEW_OUTGOING_CALL broadcast.621        //622        // TODO: In the future, though, 3rd party apps *should* be allowed to623        // intercept outgoing calls to SIP addresses as well.  To do this, we should624        // (1) update the NEW_OUTGOING_CALL intent documentation to explain this625        // case, and (2) pass the outgoing SIP address by *not* overloading the626        // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold627        // the outgoing SIP address.  (Be sure to document whether it's a URI or just628        // 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;636637            // TODO: if there's ever a way for SIP calls to trigger a638            // "callNow=true" case (see above), we'll need to handle that639            // case here too (most likely by just doing nothing at all.)640        }641 当去拨打一个电话(紧急电话或常规号码,不包括SIP call)后,暴露给外部一个ACTION_NEW_OUTGOING_CALL的intent642        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 app650        // to intercept the outgoing call.651        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);652        if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + ".");653654        // 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 the657        // timeout.658        mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,659                OUTGOING_CALL_TIMEOUT_THRESHOLD);660        sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER,661                PERMISSION, new OutgoingCallReceiver(),662                null,  // scheduler663                Activity.RESULT_OK,  // initialCode664                number,  // initialData: initial value for the result data665                null);  // initialExtras666    }

更多相关文章

  1. Android消息推送接收后,通知栏的显示
  2. Android应用程序键盘(Keyboard)消息处理机制分析(17)
  3. android 拨打电话与发送短信
  4. android 入门学习笔记 判断网页地址 邮箱 电话号码 符合要求的加
  5. android 获取本机SMI卡号码
  6. android向联系人中添加头像以及获得电话记录
  7. android 判断手机号码格式
  8. android通过查询电话号码获取联系人头像

随机推荐

  1. android recovery模式选项中索引改进
  2. Android(安卓)强制横屏
  3. android中去掉button的边框和EditText中
  4. Android(安卓)各种工具类 图片下载工具类
  5. 【Arcgis for android】Error inflating
  6. Android(安卓)获取 IP 地址
  7. Android以TelephonyManager取得話機狀態
  8. Android各版本占比信息统计
  9. android 拖动图片移动效果
  10. android输入法控制显示