Emergency Call 分析

来源:互联网 发布:大腕精神病预言 知乎 编辑:程序博客网 时间:2024/05/22 09:03
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    }


0 0
原创粉丝点击