一、前言
在Android4.4源码中,拨号过程简单说就是电话应用通过Telephony Frameworks向RIL发起拨号请求,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;
四、拨号流程APP层
-
拨号界面 首先要找到拨号的代码入口,可以通过
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和相关参数,我们可以跟踪Intent,ACTION_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-alias:Activity的别名PrivilegedOutgoingCallBroadcaster,特权的拨号请求,比如在联系人、短彩信、浏览器等应用中,都采用此Activity入口targetActivity:实际处理的Activity为OutgoingCallBroadcaster由此可以确定在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判断,生成拨号状态,从而返回拨号结果
总结拨号流程在Dialer和Phone应用的过程如下图所示:
说明:
- 进入拨号盘,首先进入
DialtactsActivity界面,包括不同的Tab,找到拨号界面的Fragment,DialpadFragment - 在
Phone电话应用中,主要包括OutgoingCallBroadcaster.java、SipCallOptionHandler.java、CallController.java、PhoneUtils.java源代码,可以发现它们之间的交互均采用直接调用方法的方式。 - 拨号流程中
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走,因为它是默认使用的手机网络制式,所以进入GSMPhone的dial方法
在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() 发起拨号请求,mCT是GsmCallTracker ,往上看可以看到是在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是哪里来的
在PhoneFactory的makeDefaultPhone函数中,首先构造一个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的参数,就终于知道在GsmCallTracker的dial()方法中使用mCi.dial()进行拨号请求调用,实际时调用了RIL.java的dial()方法
PhoneBase的mCi属性为RIL对象,GSMPhone作为PhoneBase的子类继承了PhoneBase的mCi属性,而GsmCallTracker的mCi属性是GSMPhone的对象通过this 传递给GsmCallTracker,在GsmCallTracker的构造方法中有mCi = phone.mCi ,即GsmCallTracker中mCi就是RIL对象,(并且与Phone对象的mCM均指向同一个对象?)
PhoneFactory的静态方法makeDefaultPhone,创建Phone对象的工厂方法,什么时候会创建Phone对象呢?答案是在PhoneApp中,手机开机过程中会加载Phone.apk应用,便会调用PhoneApp的onCreate方法;所以在手机启动时,Phone、CallTracker等对象已经创建好了,在应用中只管使用即可。
到此为止,拨号请求流程已经跟踪到
RIL层,RIL负责与BP Modem交互,由Modem通过移动网络发起实际的拨号请求;Modem拨号操作成功后电话状态便为接通或通话中,或是无法接通等状态,Modem会发送消息通知Call状态变化,由RIL.java进行接收
六、总结
- 用户进入拨号键盘发起拨号请求(可通过联系人,短信等其他应用发起拨号请求),调用
startActivity()进入OutgoingCallBroadcaster.java的onCreate()方法,通过Intent传递拨号的基本信息,如拨号请求的电话号码、是否延迟拨号、是否紧急呼救等信息。 OutgoingCallBroadcaster.java的onCreate方法,通过Intent获取拨号请求相关信息,并验证拨号请求信息的完整性和正确性,验证通过后,调用CallController的placeCall方法继续发起拨号请求。- 接着进入
PhoneUtils.placeCall方法,通过图可知,步骤3、4、5、6进入了一连串的dial方法调用。Phone应用中采用placeCall方法进行拨号请求方法,而Telephony Frameworks层中均采用dial方法作为拨号请求方法。 RIL.java中dial方法的实现机制采用了异步调用方式,直接返回给GsmCallTracker,如上图所示,步骤7、8、10、11共4步,一层一层返回到CallController的placeCall方法;与此同时,RIL会向Modem发起ATD的拨号命令。
-
整个拨号流程使用了同步和异步两种处理机制,在流程图中步骤6和9采用异步消息处理机制,其余均为直接调用方法处理机制
-
拨号最终的目的是
RIL向Modem发起ATD拨号命令
-
RILJ与RILD如何通信RILJ在创建的过程中,会启动两个线程,RILSender和RILReceiver,RILSender负责将指令发送给RILD,而RILReceiver则负责读取从RILD发送过来的数据。RILJ与RILD的通信通道就是在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;
}
}
dial消息到rild后,通过processCommandsCallback函数监听,通过librilutils目录中的 record_stream.c提供的处理函数,获取到消息数据,再经过processCommandBuffer函数派发,经过追寻代码,最终pRI->pCI->dispatchFunction(p, pRI),通过这种方式来调用到函数进行处理