TelephonyManager.java
--------》getSimState接口
/**
* Returns a constant indicating the state of the default SIM card.
*
* @see #SIM_STATE_UNKNOWN
* @see #SIM_STATE_ABSENT
* @see #SIM_STATE_PIN_REQUIRED
* @see #SIM_STATE_PUK_REQUIRED
* @see #SIM_STATE_NETWORK_LOCKED
* @see #SIM_STATE_READY
* @see #SIM_STATE_NOT_READY
* @see #SIM_STATE_PERM_DISABLED
* @see #SIM_STATE_CARD_IO_ERROR
*/
public int getSimState() {
int slotIdx = getDefaultSim();
// slotIdx may be invalid due to sim being absent. In that case query all slots to get
// sim state
if (slotIdx < 0) {
// query for all slots and return absent if all sim states are absent, otherwise
// return unknown
for (int i = 0; i < getPhoneCount(); i++) {
int simState = getSimState(i);
if (simState != SIM_STATE_ABSENT) {
Rlog.d(TAG, "getSimState: default sim:" + slotIdx + ", sim state for " +
"slotIdx=" + i + " is " + simState + ", return state as unknown");
return SIM_STATE_UNKNOWN;
}
}
Rlog.d(TAG, "getSimState: default sim:" + slotIdx + ", all SIMs absent, return " +
"state as absent");
return SIM_STATE_ABSENT;
}
return getSimState(slotIdx);
}
--------》
public int getSimState(int slotIdx) {
int simState = SubscriptionManager.getSimStateForSlotIdx(slotIdx);
return simState;
}
-------》SubscriptionManager.java
public static int getSimStateForSlotIdx(int slotIdx) {
int simState = TelephonyManager.SIM_STATE_UNKNOWN;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
simState = iSub.getSimStateForSlotIdx(slotIdx);
}
} catch (RemoteException ex) {
}
return simState;
}
------》SubscriptionController.java
/**
* Get the SIM state for the slot idx
* @return SIM state as the ordinal of {@See IccCardConstants.State}
*/
@Override
public int getSimStateForSlotIdx(int slotIdx) {
State simState;
String err;
if (slotIdx < 0) {
simState = IccCardConstants.State.UNKNOWN;
err = "invalid slotIdx";
} else {
Phone phone = PhoneFactory.getPhone(slotIdx);
if (phone == null) {
simState = IccCardConstants.State.UNKNOWN;
err = "phone == null";
} else {
IccCard icc = phone.getIccCard();
if (icc == null) {
simState = IccCardConstants.State.UNKNOWN;
err = "icc == null";
} else {
simState = icc.getState();
err = "";
}
}
}
if (VDBG) {
logd("getSimStateForSlotIdx: " + err + " simState=" + simState
+ " ordinal=" + simState.ordinal() + " slotIdx=" + slotIdx);
}
return simState.ordinal();
}
PhoneFactory.getPhone的内容,我们暂时不用管
其实是GsmCdmaPhone.java的:
@Override
public IccCard getIccCard() {
return mIccCardProxy;
}
而,mIccCardProxy恰好是:
private IccCardProxy mIccCardProxy;
通过IccCardProxy ,获取simState = icc.getState();
一个sim卡一个IccCardProxy;
然后return内容是simState.ordinal();
--------》IccCardProxy .java
/* IccCard interface implementation */
@Override
public State getState() {
synchronized (mLock) {
return mExternalState;
}
}
然后分析全局的mExternalState,是如何获取到的:
private State mExternalState = State.UNKNOWN;
初始值为UNKNOWN
然后生成一个Sim卡的Icc代理,就会初始化内容为:
setExternalState(State.NOT_READY, false);
也就是说,如果设备没有Modem,默认情况下为NOT_READY,即为6
我们主要注册了以下Message,如下:
mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context,
ci, this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
mUiccController = UiccController.getInstance();
mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
ci.registerForOn(this,EVENT_RADIO_ON, null);
ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
我们再看如下处理消息的代码:
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_RADIO_OFF_OR_UNAVAILABLE:
mRadioOn = false;
if (CommandsInterface.RadioState.RADIO_UNAVAILABLE == mCi.getRadioState()) {
setExternalState(State.NOT_READY);
}
break;
case EVENT_RADIO_ON:
mRadioOn = true;
if (!mInitialized) {
updateQuietMode();
}
break;
case EVENT_ICC_CHANGED:
if (mInitialized) {
updateIccAvailability();
}
break;
case EVENT_ICC_ABSENT:
mAbsentRegistrants.notifyRegistrants();
setExternalState(State.ABSENT);
break;
IccCardProxy就相当于一个接口代理类,集成了关于Icc的所有接口
但是Icc数据的来源呢?
当然一切的数据来源都是Modem了,BP,基带了
AP端接受BP信号的入口为RIL.java
结合框架图和时序图,下面大致说一下各个类的工作细节。从上到下的顺序:
RIL.java
->CommandsInterface.java(CommandsInterface由RIL来实现)
->UiccController.java
->UiccCard.java
->UiccCardApplication.java
->IccCardProxy.java
关于这些类,做一个简要的总结如下:
UiccController 设计为单例模式。监听RIL中的SIM卡状态,并把SIM卡状态的变化通知给其他类。在UICC框架中,它属于核心部分,除了分发SIM卡状态变化,还对外提供接口用于获取UiccCard,IccFileHandler,IccRecords,UiccCardApplication的对象。
UiccCard 它代表了具体的卡,一个UiccCard对象就表示了一张SIM卡(PhoneID)。SIM卡中的状态或者数据都可以在这里获取,比如,属性mCardState保存了SIM卡状态,mUniversalPinState保存了PIN码状态,mCatService代表了STK应用信息,mUiccApplications中包含了SIM卡的具体数据。。。等等,总结起来,UiccCard是SIM卡的大管家,它既代表了SIM卡,又控制了UiccApplications,CatService的生命周期。
UiccCardApplication 顾名思义,这是SIM卡应用(不是STK)。应该是对应了上面说到的逻辑模块,一张SIM卡可以有多个逻辑模块,也就有多个UiccCardApplication对象。它控制了IccRecords和IccFileHandler的生命周期。而IccRecords和IccFileHandler都是读取和保存SIM卡中具体数据的操作类
IccFileHandler 读取SIM卡中(逻辑模块)的文件系统,也就是SIM卡中的具体数据。根据UICC卡的种类不同,衍生了几个对应的子类SIMFileHandler,UsimFileHandler,RuimFileHandler,CsimFileHandler,IsimFileHandler
IccRecords 模板类,通过IccFileHandler来操作SIM卡中的文件系统个,获取并保存SIM中的具体数据,根据UICC卡的种类不同,衍生了几个对应的子类SIMRecords,RuimRecords,IsimUiccRecords
IccCardProxy 封装了对UICC的一系列操作(状态和数据),并对外提供接口。一张卡(PhoneID)对应一个IccCardProxy对象。实际上,我们可以使用UiccController获取其他的对象从而实现对UICC的操作,但是需要传入一系列参数并保证卡状态正确,否则需要做判断处理,使用IccCardProxy操作只需要知道PhoneID。并发出ACTION_SIM_STATE_CHANGED广播,通知应用层以及没有监听UiccController得知SIM卡状态发生变化的其他类。
详细内容参考如下文章:
https://blog.csdn.net/supergame111/article/details/106452987
现在来分析如题问题,为何会机器在无卡状态下,偶现获取状态值为6,
正确值应该是1:
SIM_STATE_NOT_READY---------6
SIM_STATE_ABSENT-------1
是用at命令,获取的值为1
如果重置网络数据,这个值理应就是1了
因为首次开机,sim卡状态会被system保存,只有icc状态发生改变,modem才会触发上报机制,然后更新icc状态
为了验证推理是否正确,因为sim卡支持热插拔,重新插一张卡,状态为Ready,然后把卡拔了,状态值为1,属正常了。因为插入卡,触发了更新状态流程
此问题解决方式:
因为大部分时候,机器都没问题,可能重启、刷机一下,就能解决此问题,概率比较低,约为2/500,如果要彻底解决此问题,建议从modem端解决,较为彻底。
可以使用重发机制,icc卡状态上报两次,间隔一段时间。
如果要在AP端解决此问题,可以在更新icc状态前,做一定的延时,或者针对mInitialized为false的情况,重发一次。