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的情況,重發一次。