mtk平台卡槽切换引发的bug

近日遇到一个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广播接收处理会出问题。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值