Android4.4 电话呼叫源码流程分析

528 阅读13分钟

一、前言

Android4.4源码中,拨号过程简单说就是电话应用通过Telephony FrameworksRIL发起拨号请求,RIL接收到拨号请求后向Modem发起ATD拨号命令,Modem执行ATD命令控制Radio无线通信设备发起实际的拨号请求。

拨号操作会调用到RIL并使用AT指令发送给Modem,最终Modem与硬件交互后向基站发起通话请求,本次流程分析从APP-RILJ

Android作为一个通用的移动平台,其首要的功能就是通话、短信以及上网等通信功能。那么,从系统的角度来看,Android究竟是怎么实现与网络的交互的了?这次来看下Android中负责通信功能的Telephony中间层,通常也被称之为RIL(Radio Interface Layer)的具体实现原理与架构。

Android手机要实现与网络端的通信,需要跨越两个层:

  • RIL Java(RILJ):负责将上层APP的通信请求发送给HAL层;
  • RIL C++(RILD): 系统守护进程,负责将RILJ的请求命令发送给CP(Communication Processor)

二、环境

本地代码有略微修改,可能与Google原生AOSP源码略有出入

三、RIL结构简介

下图是一个Android RIL的一个结构图。整个通信过程有四个层:

  • 最上层的是应用程序,如通话,短信以及SIM卡管理,它们主要负责将用户的指令发送到RIL Framework(以后统称RILJ);
  • RILJ为上层提供了通用的API,如TelephonyManager(包括通话,网络状态; SubscriptionManager(卡状态)以及SmsManager等,同时RILJ还负责维持与RILD的通信,并将上层的请求发送给RILD
  • RILD是系统的守护进程,对于支持通话功能的移动平台是必不可少的。RILD的功能主要功能是将RILJ发送过来的请求继续传递给CP,同时会及时将CP的状态变化发送给RILJ
  • Linux驱动层:kernel驱动层接受到数据后,将指令传给CP,最后由CP发送给网络端,等网络返回结果后,CP将传回给RILD

Pasted image 20241117225720.png

四、拨号流程APP层

  1. 拨号界面 首先要找到拨号的代码入口,可以通过log找到DialtactsActivity 是拨号盘界面的Activity ,可以看到onClick() 方法中可以切换到到拨号界面DialpadFragment

    // /packages/apps/Dialer/src/com/android/dialer/DialtactsActivity.java
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.dialpad_button:
                showDialpadFragment(true);
                break;
    
    private void showDialpadFragment(boolean animate) {
        mDialpadFragment.setAdjustTranslationForAnimation(animate);
        final FragmentTransaction ft = getFragmentManager().beginTransaction();
        if (animate) {
            ft.setCustomAnimations(R.anim.slide_in, 0);
        } else {
            mDialpadFragment.setYFraction(0);
        }
        ft.show(mDialpadFragment);
        ft.commit();
    }
    

    进入DialpadFragment就可以直接去onClick()找拨号按钮了,可以看到进入了dialButtonPressed()中,主要判断号码为空或者是否是特殊号码

    // /packages/apps/Dialer/src/com/android/dialer/dialpad/DialpadFragment.java
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
    				...
            case R.id.dialButton: {
                dialButtonPressed();
                return;
            }
    		...
    }
    
    * In most cases, when the dial button is pressed, there is a
    * number in digits area. Pack it in the intent, start the
    * outgoing call broadcast as a separate task and finish this
    * activity.
    public void dialButtonPressed() {
        if (isDigitsEmpty()) { // No number entered.
            handleDialButtonClickWithEmptyDigits();
        } else {
            final String number = mDigits.getText().toString();
    
            // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
            // test equipment.
            // TODO: clean it up.
            if (number != null
                    && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
                    && number.matches(mProhibitedPhoneNumberRegexp)
                    && (SystemProperties.getInt("persist.radio.otaspdial", 0) != 1)) {
                Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
                if (getActivity() != null) {
                    DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
                            R.string.dialog_phone_call_prohibited_message);
                    dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
                }
    
                // Clear the digits just in case.
                mDigits.getText().clear();
            } else {
    						// 根据拨号的电话号码创建拨号传递的Intent相关参数
                final Intent intent = CallUtil.getCallIntent(number,
                        (getActivity() instanceof DialtactsActivity ?
                                ((DialtactsActivity) getActivity()).getCallOrigin() : null));
                startActivity(intent);
                hideAndClearDialpad();
            }
        }
    }
    
    // /packages/apps/ContactsCommon/src/com/android/contacts/common/CallUtil.java
    public static final ComponentName CALL_INTENT_DESTINATION = new ComponentName(
                "com.android.phone", "com.android.phone.PrivilegedOutgoingCallBroadcaster");
    /**
     * A variant of {@link #getCallIntent(String)} but also accept a call origin. For more
     * information about call origin, see comments in Phone package (PhoneApp).
     */
    public static Intent getCallIntent(String number, String callOrigin) {
        return getCallIntent(getCallUri(number), callOrigin);
    }
    
    /**
     * A variant of {@link #getCallIntent(android.net.Uri)} but also accept a call origin. For more
     * information about call origin, see comments in Phone package (PhoneApp).
     */
    public static Intent getCallIntent(Uri uri, String callOrigin) {
        final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (callOrigin != null) {
            intent.putExtra(PhoneConstants.EXTRA_CALL_ORIGIN, callOrigin);
        }
    
        // Set phone as an explicit component of CALL_PRIVILEGED intent.
        // Setting destination explicitly prevents other apps from capturing this Intent since,
        // unlike SendBroadcast, there is no API for specifying a permission on startActivity.
        intent.setComponent(CALL_INTENT_DESTINATION);
    
        return intent;
    }
    
    /**
     * Return Uri with an appropriate scheme, accepting Voicemail, SIP, and usual phone call
     * numbers.
     */
    public static Uri getCallUri(String number) {
        if (PhoneNumberUtils.isUriNumber(number)) {
             return Uri.fromParts(SCHEME_SIP, number, null);
        }
        return Uri.fromParts(SCHEME_TEL, number, null);
     }
    

    CallUtil.java中,把号码包装成Uri ,创建拨号传递的Intent和相关参数,我们可以跟踪IntentACTION_CALL_PRIVILEGED的字符串常量“android.intent.action.CALL_PRIVILEGED”,通过隐式Intent调用通过全局搜索,可以在packages/apps/Phone应用的AndroidManifest.xml中找到

    // /packages/services/Telephony/AndroidManifest.xml
    <activity-alias android:name="PrivilegedOutgoingCallBroadcaster"
            android:targetActivity="OutgoingCallBroadcaster"
            android:screenOrientation="nosensor"
            android:permission="android.permission.CALL_PRIVILEGED">
        <intent-filter android:priority="1000">
            <action android:name="android.intent.action.CALL_PRIVILEGED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="tel" />
        </intent-filter>
        <intent-filter android:icon="@drawable/ic_launcher_sip_call"
                android:priority="1000">
            <action android:name="android.intent.action.CALL_PRIVILEGED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="sip" />
        </intent-filter>
        <intent-filter android:priority="1000">
            <action android:name="android.intent.action.CALL_PRIVILEGED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="voicemail" />
        </intent-filter>
        <intent-filter android:priority="1000">
            <action android:name="android.intent.action.CALL_PRIVILEGED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="vnd.android.cursor.item/phone" />
            <data android:mimeType="vnd.android.cursor.item/phone_v2" />
            <data android:mimeType="vnd.android.cursor.item/person" />
        </intent-filter>
    </activity-alias>
    

需要注意两点:

  • activity-aliasActivity的别名PrivilegedOutgoingCallBroadcaster ,特权的拨号请求,比如在联系人、短彩信、浏览器等应用中,都采用此Activity入口
  • targetActivity:实际处理的ActivityOutgoingCallBroadcaster 由此可以确定在dialButtonPressed方法启动的目标Activity便为OutgoingCallBroadcaster

找到OutgoingCallBroadcaster.java#onCreate()方法中,调用processIntentMSMS() ,如果不是紧急号码会在该方法中最后发送一个广播Action = Intent.ACTION_NEW_OUTGOING_CALL ,本类中内部类OutgoingCallReceiver 继承BroadcastReceiver,到doReceive进行处理,调用startSipCallOptionHandler() ,将当前的Intent拷贝,进入SipCallOptionHandler处理

    // /packages/services/Telephony/src/com/android/phone/OutgoingCallBroadcaster.java
    private void processIntentMSMS(Intent intent) {
    		// 从intent取出号码,判断号码合法性和判断是不是紧急号码
    		String action = intent.getAction();
    		// 根据是否是紧急号码更新aciton
    		// 如果是紧急号码,callNow = true,直接发起拨号
    		if (callNow) {
            // 前面对intent相关参数进行验证,验证通过后接着发起Intent.ACTION_CALL_EMERGENCY或
            PhoneGlobals.getInstance().callController.placeCall(intent);
        }
    
    	  // 发送ACTION_NEW_OUTGOING_CALL广播
    		Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
    		mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
                    OUTGOING_CALL_TIMEOUT_THRESHOLD);
        sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER,
                    PERMISSION, new OutgoingCallReceiver(),
                    null,  // scheduler
                    Activity.RESULT_OK,  // initialCode
                    number,  // initialData: initial value for the result data
                    null);  // initialExtras
    }
    
    public boolean doReceive(Context context, Intent intent) {
    		startSipCallOptionHandler(context, intent, uri, number);
    }
    
    private void startSipCallOptionHandler(Context context, Intent intent, Uri uri, String number) {
    		Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
    		PhoneUtils.putLinkId(newIntent, mLinkId);
    		newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL, number);
    
    		Intent selectPhoneIntent = new Intent(ACTION_SIP_SELECT_PHONE, uri);
	        selectPhoneIntent.setClass(context, SipCallOptionHandler.class);
	        selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT, newIntent);
    		selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    		context.startActivity(selectPhoneIntent);
    }
    // packages/services/Telephony/src/com/android/phone/SipCallOptionHandler.java
    private void setResultAndFinish() {
        runOnUiThread(new Runnable() {
            public void run() {
    						// 判断呼出的配置文件是否存在
                // 判断是不是sip电话,否则走下面进入CallController.placeCall()
                if (mUseSipPhone && mOutgoingSipProfile == null) {
                    showDialog(DIALOG_START_SIP_SETTINGS);
                    return;
                } else {
                    // Woo hoo -- it's finally OK to initiate the outgoing call!
                    PhoneGlobals.getInstance().callController.placeCall(mIntent);
                }
            }
        });
    }

获取到PhoneGlobals的实例(是在Phone进程中运行时的全局状态),它是Phone电话应用的入口Java类;根据其调用方法可以知道它是一个单例,直接看CallController中的placeCall()拨号方法

    // packages\services\Telephony\src\com\android\phone\CallController.java
    public void placeCall(Intent intent) {
    		// 判断intent和intent中的信息
    		...
    		// 判断是否是FDN号码
        CallStatusCode status = checkFdnNumberIfNeeded(intent);
        if (status != CallStatusCode.FDN_CHECKING){
            status =  placeCallInternal(intent);
        }
    }

调用了CallController.placeCallInternal(intent) ,从中取得号码,通话类型,判断电话号码是否是紧急号码等,执行下一步

    // packages\services\Telephony\src\com\android\phone\CallController.java
    int callStatus = PhoneUtils.placeCall(mApp,
    			                                phone,
    			                                number,
    			                                contactUri,
    			                                (isEmergencyNumber || isEmergencyIntent),
    			                                rawGatewayInfo,
    			                                mCallGatewayManager,
    			                                callType,
    			                                extras);

继续跟踪PhoneUtils.java的静态placeCall()方法,关键处理逻辑如下:

    try {
    		// 继续调用拨号方法
        connections = app.mCM.dial(phone, numberToDial, callType, extras);
    } catch (CallStateException ex) {
    		return CALL_STTUS_FAILED;
    }
    // 后面会对拨号请求返回的connection判断,生成拨号状态,从而返回拨号结果

总结拨号流程在DialerPhone应用的过程如下图所示:

Dialer到Phone应用层拨号处理流程.png

说明:

  1. 进入拨号盘,首先进入DialtactsActivity界面,包括不同的Tab,找到拨号界面的FragmentDialpadFragment
  2. Phone电话应用中,主要包括OutgoingCallBroadcaster.javaSipCallOptionHandler.javaCallController.javaPhoneUtils.java源代码,可以发现它们之间的交互均采用直接调用方法的方式。
  3. 拨号流程中PhoneUtils.java中的placeCall方法作为应用层与Telephony Frameworks层的分界点
    // /frameworks/opt/telephony/src/java/com/android/internal/telephony/CallManager.java
    public Connection dial(Phone phone, String dialString) throws CallStateException {
        Phone basePhone = getPhoneBase(phone);
        Connection result;
    
        if (VDBG) {
            Rlog.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
            Rlog.d(LOG_TAG, toString());
        }
    
        if (!canDial(phone)) {
            throw new CallStateException("cannot dial in current state");
        }
    
        if ( hasActiveFgCall() ) {
            Phone activePhone = getActiveFgCall().getPhone();
            boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
    
            if (DBG) {
                Rlog.d(LOG_TAG, "hasBgCall: " + hasBgCall + " sameChannel:" + (activePhone == basePhone));
            }
    
            if (activePhone != basePhone) {
                if (hasBgCall) {
                    Rlog.d(LOG_TAG, "Hangup");
                    getActiveFgCall().hangup();
                } else {
                    Rlog.d(LOG_TAG, "Switch");
                    activePhone.switchHoldingAndActive();
                }
            }
        }
    
        result = basePhone.dial(dialString);
    
        if (VDBG) {
            Rlog.d(LOG_TAG, "End dial(" + basePhone + ", "+ dialString + ")");
            Rlog.d(LOG_TAG, toString());
        }
    
        return result;
    }
    

五、Telephony Frameworks层

CallManager.java#dial会执行basePhone.dial(dialString, callType, extras); Phone是一个接口,有好几个类实现了它,我们可以直接跟GSMPhone走,因为它是默认使用的手机网络制式,所以进入GSMPhonedial方法

GSMPhone.java#dial 中判断如果是imsPhone的话会走imsPhone.dial(),否则会走本类中的dial()方法

    // frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm\GSMPhone.java
    @Override
    public Connection
    dial (String dialString, UUSInfo uusInfo) throws CallStateException {
        // Need to make sure dialString gets parsed properly
        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
    
        // handle in-call MMI first if applicable
        if (handleInCallMmiCommands(newDialString)) {
            return null;
        }
    
        // Only look at the Network portion for mmi
        String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
        GsmMmiCode mmi =
                GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
        if (LOCAL_DEBUG) Rlog.d(LOG_TAG,
                               "dialing w/ mmi '" + mmi + "'...");
    
        if (mmi == null) {
            return mCT.dial(newDialString, uusInfo);
        } else if (mmi.isTemporaryModeCLIR()) {
            return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo);
        } else {
            mPendingMMIs.add(mmi);
            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
            mmi.processCode();
    
            // FIXME should this return null or something else?
            return null;
        }
    }

从代码看会走mCT.dial() 发起拨号请求,mCTGsmCallTracker ,往上看可以看到是在GSMPhone的构造方法中赋值的,并且把GSMPhone传到了GsmCallTracker

    // frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm\GSMPhone.java
    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode, int linkId) {
    	  mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);
    	  mCT = new GsmCallTracker(this);
    	  mSST = new GsmServiceStateTracker (this);

接下来继续看GsmCallTracker中的dial()方法

    // frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
    synchronized Connection
    dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {
    		...
    		// 创建connection连接
        mPendingMO = new GsmConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString), this, mForegroundCall);
    		// 验证拨号请求的号码是否正确
        if (mPendingMO.mAddress == null || mPendingMO.mAddress.length() == 0
            || mPendingMO.mAddress.indexOf(PhoneNumberUtils.WILD) >= 0
        ) {
            // Phone number is invalid
            mPendingMO.mCause = Connection.DisconnectCause.INVALID_NUMBER;
    
            // handlePollCalls() will notice this call not present
            // and will mark it as dropped.
            pollCallsWhenSafe();
        } 
        else {
            // 取消静音设置
            setMute(false);
            mCi.dial(mPendingMO.mAddress, clirMode, uusInfo, obtainCompleteMessage());
        }
    		// 更新Phone状态
        updatePhoneState();
    		// 发起Phone状态变化通知
        mPhone.notifyPreciseCallStateChanged();
    		// 返回通话连接
        return mPendingMO;
    }

该方法中前面会判断手机状态、通话信息等的判断和处理,最后通过mCi.dial()mCi 的定义是CommandsInterface,是个接口,在GsmCallTracker的构造方法中通过mCi = phone.mCi获取

需要注意: 拨号过程中,通话连接Connection,它在CallTracker的子类的拨号请求方法中创建,应用层接收到Telephony Frameworks层的拨号请求返回后,根据此对象判断并处理拨号请求的返回结果

接下来我们要在构建方法传递过来的phone中找mCi是怎么来的,先找这个属性是哪里的 GSMPhone中只有mCi 的调用,并没有定义和创建部分,再找GSMPhone的父类PhoneBase ,可以看到是再PhoneBase 的构建方法中传递进来的CommandsInterface ,接下来如果想走进dial()方法中必须要找到这个CommandsInterface是哪里来的

PhoneFactorymakeDefaultPhone函数中,首先构造一个DefaultPhoneNotifier对象和RIL对象,然后从数据库中读取网络模式,根据网络模式得到对应的电话类型,从而构造对应的Phone对象,并为该Phone对象创建一个PhoneProxy代理对象。对于GSM网络,会构造一个GSMPhone对象。 在构造GSMPhone对象时,首先使用父类的成员变量CommandsInterfac mCM设置电话类型,由于RIL类实现了CommandsInterface接口,因此mCM引用RIL对象。在构造GSMPhone对象时通过参数传入并设置父类PhoneBase的成员变量mCM

所以回到GsmCallTracker 去找找构造方法中的这个phone到底是个什么玩意儿

通过搜索哪里创建的GSMPhone最终定位到了PhoneFactory.java#makeDefaultPhone() 方法,摘取关键代码:

    // frameworks\opt\telephony\src\java\com\android\internal\telephony\PhoneFactory.java
    public static void makeDefaultPhone(Context context) {
    		sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
    		...
    		int phoneType = TelephonyManager.getPhoneType(networkMode);
    		if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    		    Rlog.i(LOG_TAG, "Creating GSMPhone");
    		    //only creat one phone
    		    sProxyPhone = new PhoneProxy(new GSMPhone(context,
    		            sCommandsInterface, sPhoneNotifier, false, 0), 0);
    		
    		    sProxyPhoneList.add(0, sProxyPhone);
    }

在创建Phone对象时,传入sCommandsInterface RIL对象作为CommandsInterface mCi的参数,就终于知道在GsmCallTrackerdial()方法中使用mCi.dial()进行拨号请求调用,实际时调用了RIL.javadial()方法

PhoneBasemCi属性为RIL对象,GSMPhone作为PhoneBase的子类继承了PhoneBasemCi属性,而GsmCallTrackermCi属性是GSMPhone的对象通过this 传递给GsmCallTracker,在GsmCallTracker的构造方法中有mCi = phone.mCi ,即GsmCallTrackermCi就是RIL对象,(并且与Phone对象的mCM均指向同一个对象?)

PhoneFactory的静态方法makeDefaultPhone,创建Phone对象的工厂方法,什么时候会创建Phone对象呢?答案是在PhoneApp中,手机开机过程中会加载Phone.apk应用,便会调用PhoneApponCreate方法;所以在手机启动时,PhoneCallTracker等对象已经创建好了,在应用中只管使用即可。

Telephony Frameworks框架到RIL拨号流程总结.png 到此为止,拨号请求流程已经跟踪到RIL层,RIL负责与BP Modem交互,由Modem通过移动网络发起实际的拨号请求;Modem拨号操作成功后电话状态便为接通或通话中,或是无法接通等状态,Modem会发送消息通知Call状态变化,由RIL.java进行接收

六、总结

  1. 用户进入拨号键盘发起拨号请求(可通过联系人,短信等其他应用发起拨号请求),调用startActivity()进入OutgoingCallBroadcaster.javaonCreate()方法,通过Intent传递拨号的基本信息,如拨号请求的电话号码、是否延迟拨号、是否紧急呼救等信息。
  2. OutgoingCallBroadcaster.javaonCreate方法,通过Intent获取拨号请求相关信息,并验证拨号请求信息的完整性和正确性,验证通过后,调用CallControllerplaceCall方法继续发起拨号请求。
  3. 接着进入PhoneUtils.placeCall方法,通过图可知,步骤3、4、5、6进入了一连串的dial方法调用。Phone应用中采用placeCall方法进行拨号请求方法,而Telephony Frameworks层中均采用dial方法作为拨号请求方法。
  4. RIL.javadial方法的实现机制采用了异步调用方式,直接返回给GsmCallTracker,如上图所示,步骤7、8、10、11共4步,一层一层返回到CallControllerplaceCall方法;与此同时,RIL会向Modem发起ATD的拨号命令。
  • 整个拨号流程使用了同步和异步两种处理机制,在流程图中步骤6和9采用异步消息处理机制,其余均为直接调用方法处理机制

  • 拨号最终的目的是RILModem发起ATD拨号命令


  • RILJRILD如何通信 RILJ在创建的过程中,会启动两个线程,RILSenderRILReceiverRILSender负责将指令发送给RILD,而RILReceiver则负责读取从RILD发送过来的数据。RILJRILD的通信通道就是在RILReceiver中以LocalSocket的方式建立起来的。连接成功后,RILD会发送一个消息给RILJ,表示连接成功了,这样RILJ就可以将请求数据发送给RILD进行通信了

为什么CallManager中的basePhone默认是GSMPhone

CallController中获取到的phone实例,再逐渐传到CallManager当中的

    // find the phone first
    // TODO Need a way to determine which phone to place the call
    // It could be determined by SIP setting, i.e. always,
    // or by number, i.e. for international,
    // or by user selection, i.e., dialog query,
    // or any of combinations
    phone = PhoneUtils.pickPhoneBasedOnNumber(mCM, scheme, number, sipPhoneUri, linkId);
    public static Phone pickPhoneBasedOnNumber(CallManager cm,
            String scheme, String number, String primarySipUri, int linkId) {
        if (DBG) {
            log("pickPhoneBasedOnNumber: scheme " + scheme
                    + ", number " + toLogSafePhoneNumber(number)
                    + ", sipUri "
                    + (primarySipUri != null ? Uri.parse(primarySipUri).toSafeString() : "null"
                    + ", linkId " + linkId));
        }
    
        if (primarySipUri != null) {
            Phone phone = getSipPhoneFromUri(cm, primarySipUri);
            if (phone != null) return phone;
        }
    
        return PhoneGlobals.getInstance().getPhone(linkId);
    }

可以看到到PhoneGlobals中通过getPhone()去获取Phone对象

    static Phone getPhone(int linkId) {
        return getInstance().mPhoneList.get(linkId);
    }
    
    public void onCreate() {
        if (VDBG) Log.v(LOG_TAG, "onCreate()...");
    
        if (mPhoneList.size() == 0) {
            // Initialize the telephony framework
            PhoneFactory.makeDefaultPhones(this);
    
            // Get the default phone
            mPhoneList = PhoneFactory.getDefaultPhonesMSMS();
            phone = mPhoneList.get(0);

PhoneGlobals中的onCreate是在初始化的时候就执行的,最终是在PhoneFactory.java#getDefaultPhoneMSMS()中获取sProxyPhoneList的第一个是默认Phone,在makeDefaultPhone中将GSMPhone作为第一个添加进列表中

    int phoneType = TelephonyManager.getPhoneType(networkMode);
    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
        Rlog.i(LOG_TAG, "Creating GSMPhone");
        /* BEGIN: Modified for PN:s_project double modem single standby */
        //only creat one phone
        sProxyPhone = new PhoneProxy(new GSMPhone(context,
                sCommandsInterface, sPhoneNotifier, false, 0), 0);
        /* END:   Modified for PN:s_project double modem single standby */
    
        sProxyPhoneList.add(0, sProxyPhone);

添加进列表是根据TelephonyManager.getPhoneType决定的,而getPhoneType的默认就是GSM

    public static int getPhoneType(int networkMode) {
        switch(networkMode) {
        case RILConstants.NETWORK_MODE_CDMA:
        case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
        case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
            return PhoneConstants.PHONE_TYPE_CDMA;
    
        case RILConstants.NETWORK_MODE_WCDMA_PREF:
        case RILConstants.NETWORK_MODE_GSM_ONLY:
        case RILConstants.NETWORK_MODE_WCDMA_ONLY:
        case RILConstants.NETWORK_MODE_GSM_UMTS:
        case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
        case RILConstants.NETWORK_MODE_LTE_WCDMA:
        case RILConstants.NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA:
            return PhoneConstants.PHONE_TYPE_GSM;
    
        // Use CDMA Phone for the global mode including CDMA
        case RILConstants.NETWORK_MODE_GLOBAL:
        case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
            return PhoneConstants.PHONE_TYPE_CDMA;
    
        case RILConstants.NETWORK_MODE_LTE_ONLY:
            return PhoneConstants.PHONE_TYPE_GSM;
            //Added by hongyang.chen for Modify Excepion on 2018.3.13
    
            /*if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
                return PhoneConstants.PHONE_TYPE_CDMA;
            } else {
                return PhoneConstants.PHONE_TYPE_GSM;
            }*/
        default:
            return PhoneConstants.PHONE_TYPE_GSM;
        }
    }

app到telephony流程.png

dial消息到rild后,通过processCommandsCallback函数监听,通过librilutils目录中的 record_stream.c提供的处理函数,获取到消息数据,再经过processCommandBuffer函数派发,经过追寻代码,最终pRI->pCI->dispatchFunction(p, pRI),通过这种方式来调用到函数进行处理