近日遇到一个bug,开机的时候一个app使用ACTION_SIM_STATE_CHANGED广播监听,在intent数据中的IccCardConstants.INTENT_KEY_ICC_STATE为IccCardConstants.INTENT_VALUE_ICC_LOADED的情况下却发现sim卡却不存在,广播都收到了为啥卡确不存在??这个问题还是随机出现。
hasIccCard
先看下判断sim卡是否存在的方法,TelephonyManager中的:
frameworks/base/telephony/java/android/telephony/TelephonyManager.java
public boolean hasIccCard(int slotId) {
...
ITelephony telephony = getITelephony();
...
return telephony.hasIccCardUsingSlotId(slotId);
...
}
这里的ITelephony服务是phone服务,在Telephony包中的PhoneInterfaceManager中实现
packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java
public boolean hasIccCardUsingSlotId(int slotId) {
int subId[] = mSubscriptionController.getSubIdUsingSlotId(slotId);
final Phone phone = getPhone(subId[0]);
if (subId != null && phone != null) {
return phone.getIccCard().hasIccCard();
} else {
return false;
}
}
这里的getIccCard实际获取的就是PhoneProxy中的mIccCardProxy对象,类型为IccCardProxy
frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
public boolean hasIccCard() {
synchronized (mLock) {
// MTK-START
//if (mUiccCard != null && mUiccCard.getCardState() != CardState.CARDSTATE_ABSENT) {
// return true;
//}
//return false;
boolean isSimInsert = false;
// To obtain correct status earily,
// we use system property value to detemine sim inserted state.
if (isSimInsert == false) {
String iccId = null;
iccId = SystemProperties.get(PROPERTY_ICCID_SIM[mPhoneId]);
if (DBG) log("iccId = " + iccId);
if ((iccId != null) && !(iccId.equals("")) && !(iccId.equals(ICCID_STRING_FOR_NO_SIM))) {
isSimInsert = true;
}
}
if (isSimInsert == false && mUiccCard != null && mUiccCard.getCardState() != CardState.CARDSTATE_ABSENT) {
isSimInsert = true;
}
if (DBG) log("hasIccCard(): isSimInsert = " + isSimInsert + " ,CardState = " + ((mUiccCard != null) ? mUiccCard.getCardState() : ""));
return isSimInsert;
// MTK-END
}
}
这里看出mtk注释掉了google原生的逻辑,加入了依靠iccid值判断的逻辑,可是从日志中:
04-24 20:09:46.171397 1616 1616 D IccCardProxy: hasIccCard(): isSimInsert = false ,CardState = (slot 0)
...
04-24 20:09:46.171991 1616 1616 D IccCardProxy: hasIccCard(): isSimInsert = false ,CardState = (slot 1)
看出iccid和mUiccCard的输出都是空的,很奇怪。
iccid系统属性的设置和清除
整个framework的代码中只有读取iccid的代码,没有设置该属性的代码,那么ril代码中设置iccid的可能性很大。
看代码果然如此
/vendor/mediatek/proprietary/hardware/ril/gsm/mtk-ril/ril_sms.c
插入
void enforceQueryAllIccId() {
...
RLOGD("queryIccId: strlen of response is %d", strlen(iccId) );
upadteSystemPropertyByCurrentModeGemini(i, PROPERTY_ICCID_SIM[0], PROPERTY_ICCID_SIM[1], PROPERTY_ICCID_SIM[2], PROPERTY_ICCID_SIM[3], iccId);
...
}
清除
void resetSIMProperties(RIL_SOCKET_ID rid) {
...
upadteSystemPropertyByCurrentModeGemini(rid, PROPERTY_ICCID_SIM[0], PROPERTY_ICCID_SIM[1], PROPERTY_ICCID_SIM[2], PROPERTY_ICCID_SIM[3], NULL);
...
}
那么调用流程往上追究
vendor/mediatek/proprietary/hardware/ril/gsm/mtk-ril/ril_callbacks.c
static void resetSystemProperties(RIL_SOCKET_ID rid)
{
...
resetSIMProperties(rid);
}
static void initializeCallback(void *param)
{
...
resetSystemProperties(rid);
...
}
然后往上到主消息循环中
static void *mainLoop(void *param)
{
...
while (s_main_loop) {
...
int sim3G = queryMainProtocol(RIL_DEFAULT);
...
if (performSWSimSwitchIfNecessary(sim3G)) {
...
RIL_requestTimedCallback(initializeCallback, (void *)&s_pollSimId, &TIMEVAL_0);//初始化卡槽1的ril相关信息
if (SIM_COUNT >= 2) {
if (isInternationalRoamingEnabled()
|| getTelephonyMode() == 100
|| getTelephonyMode() == 101
|| isEVDODTSupport()) {
curr_share_modem = 2;
} else {
if (isDualTalkMode()) {
curr_share_modem = 1;
} else {
curr_share_modem = getMtkShareModemCurrent();
}
}
switch (curr_share_modem) {
case 2:
case 3:
case 4:
RLOGI("initializeCallback ril2");
RIL_requestTimedCallback(initializeCallback, (void *)&s_pollSimId2, &TIMEVAL_0); //初始化卡槽2的ril相关信息
break;
default:
break;
}
}
...
}
...
}
RLOGI("Main loop exit");
return 0;
}
从日志中看是走到了这里:
04-24 20:09:42.794702 3398 3423 I RIL : initializeCallback ril2
那么performSWSimSwitchIfNecessary方法返回的肯定是true,表示上层发送了切换sim主副卡的请求,所以会重启rild。
static int performSWSimSwitchIfNecessary(int sim3G)
{
int continueInit = 1;
int telephonyMode = getTelephonyMode();
int firstModem = getFirstModem();
RLOGI("current telephony mode and 3G SIM: [mode:%d, 3G SIM:%d, FirstMD:%d]", telephonyMode, sim3G, firstModem);
...
switch (telephonyMode) {
...
default:
if (sim3G >= CAPABILITY_3G_SIM2)
s_isSimSwitched = 1;
else
s_isSimSwitched = 0;
break;
}
return continueInit;
}
从日志中看出这里的SIM3G为2,代表卡槽2,telephonyMode是0
04-24 20:09:42.775262 3398 3423 I RIL : current telephony mode and 3G SIM: [mode:0, 3G SIM:2, FirstMD:0]
所以不用看switch的其它分支,s_isSimSwitched设置为1。
切换卡槽
目前一般的全网通机器只有一个卡槽拥有全网通能力,另一个就只有2G的能力,这个对使用没啥大影响,因为副卡2G就是用来打电话的,主卡全网通用来上网。最初是固定卡1有全网通能力,后来可以切换卡槽,但还是只有一张卡有全网通能力。
那么自动切换卡槽的流程是咋样的?从日志中可以找到线索:
04-24 20:09:47.703300 1616 1616 D DataSubSelector: onReceive: action=android.intent.action.ACTION_SUBINFO_RECORD_UPDATED
...
04-24 20:09:47.704720 1616 1616 D DataSubSelector: DataSubSelector detectedType:4
04-24 20:09:47.704791 1616 1616 D DataSubSelector: setCapabilityIfNeeded,mIsSubReady = true, mRadioTechDone = true
04-24 20:09:47.704831 1616 1616 D DataSubSelector: setCapabilityForC2K6M,c2kP2 = 1
04-24 20:09:47.704856 1616 1616 D DataSubSelector: DataSubSelector for setCapabilityForC2K6M: only for capability switch
frameworks/opt/telephony/src/java/com/mediatek/internal/telephony/dataconnection/DataSubSelector.java
广播处理中:
if (TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED.equals(action)) {
if (SystemProperties.get(MTK_C2K_SUPPORT).equals("1")){
int detectedType = intent.getIntExtra(
SubscriptionManager.INTENT_KEY_DETECT_STATUS,
SubscriptionManager.EXTRA_VALUE_NOCHANGE);
log("DataSubSelector detectedType:" + detectedType);
if (mOperatorSpec.equals(OPERATOR_OP01)) {
if (detectedType == SubscriptionManager.EXTRA_VALUE_NOCHANGE) {
subSelectorForOp01(mIntent);
}
}
if (mOperatorSpec.equals(OPERATOR_OM) || isOP09CSupport()){
if (isCanSwitch()){
if (isOP09CSupport()){
if (detectedType == SubscriptionManager.EXTRA_VALUE_NOCHANGE) {
subSelectorForC2k6m(mIntent);
}
} else {
subSelectorForC2k6m(mIntent);
}
}
}
}
}
从日志中看是走到了setCapabilityForC2K6M
private void subSelectorForC2k6m(Intent intent) {
...
if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
// always set capability to this phone
setCapability(phoneId);
}
}
其中的phoneId是数据业务卡槽值。
private boolean setCapability(int phoneId) {
...
if (false == iTelEx.setRadioCapability(rat)) {
log("Set phone rat fail!!!");
isSwitchSuccess = false;
}
...
return isSwitchSuccess;
}
packages/services/Telephony/src/com/mediatek/phone/PhoneInterfaceManagerEx.java
public boolean setRadioCapability(RadioAccessFamily[] rafs) {
...
ProxyController.getInstance().setRadioCapability(rafs);
...
}
frameworks/opt/telephony/src/java/com/android/internal/telephony/ProxyController.java
public boolean setRadioCapability(RadioAccessFamily[] rafs) {
...
return doSetRadioCapabilities(rafs);
}
private boolean doSetRadioCapabilities(RadioAccessFamily[] rafs) {
...
mIsCapSwitching = true;
synchronized (mSetRadioAccessFamilyStatus) {
logd("setRadioCapability: new request session id=" + mRadioCapabilitySessionId);
resetRadioAccessFamilyStatusCounter();
onExceptionCount = 0;
for (int i = 0; i < rafs.length; i++) {
int phoneId = rafs[i].getPhoneId();
mSetRadioAccessFamilyStatus[phoneId] = SET_RC_STATUS_STARTING;
mOldRadioAccessFamily[phoneId] = mProxyPhones[phoneId].getRadioAccessFamily();
int requestedRaf = rafs[i].getRadioAccessFamily();
mNewRadioAccessFamily[phoneId] = requestedRaf;
mCurrentLogicalModemIds[phoneId] = mProxyPhones[phoneId].getModemUuId();
mNewLogicalModemIds[phoneId] = getLogicalModemIdFromRaf(requestedRaf);
logd("setRadioCapability: mOldRadioAccessFamily[" + phoneId + "]="
+ mOldRadioAccessFamily[phoneId]);
logd("setRadioCapability: mNewRadioAccessFamily[" + phoneId + "]="
+ mNewRadioAccessFamily[phoneId]);
sendRadioCapabilityRequest(
phoneId,
mRadioCapabilitySessionId,
RadioCapability.RC_PHASE_START,
mOldRadioAccessFamily[phoneId],
mCurrentLogicalModemIds[phoneId],
RadioCapability.RC_STATUS_NONE,
EVENT_START_RC_RESPONSE);
}
}
return true;
}
从日志中可以看到对应的输出:
01-01 08:00:42.281523 1616 1616 D ProxyController: setRadioCapability: mOldRadioAccessFamily[0]=81928
01-01 08:00:42.281554 1616 1616 D ProxyController: setRadioCapability: mNewRadioAccessFamily[0]=65536
...
01-01 08:00:42.283138 1616 1616 D ProxyController: setRadioCapability: mOldRadioAccessFamily[1]=65536
01-01 08:00:42.283167 1616 1616 D ProxyController: setRadioCapability: mNewRadioAccessFamily[1]=81928
看出两个卡槽的网络能力正好是掉了个,后续就会调用到Phone对象的代码,然后调用到ril向rilc发送请求,不再追究。
总结
这里可以看出mtk的机器在开机的时候如果卡槽2做为主卡的话会重启一次rild切换卡槽,时机不对的话app广播接收处理会出问题。