已同步更新至個人blog: dxjia.cn
Uicc框架
UICC框架是Android在4.1引入的,使的對卡的管理控制更加清晰。要了解這個UICC框架,需要從UiccController開始,它是整個UICC框架的開始與控制者,該類被設計為單例,是消息處理類Handler的子類,所以其實現肯定是基於event觸發的,其在Phone創建的早期被初始化:
1 2 3 |
// Instantiate UiccController so that all other classes can just // call getInstance() mUiccController = UiccController.make(context, sCommandsInterfaces); |
make函數只能被調用一次,以后如果要想獲得UiccController對象,只能通過getInstance進行,來看UiccController的構造函數:
1 2 3 4
5 6 7 8 9 |
publicstatic UiccController make(Context c, CommandsInterface[] ci){ synchronized(mLock){ if(mInstance !=null){ thrownew RuntimeException("MSimUiccController.make() should only be called once"); } mInstance =new UiccController(c, ci); return(UiccController)mInstance; } } |
private UiccController(Context c, CommandsInterface []ci){ if(DBG) log("Creating UiccController"); mContext = c; mCis = ci; for(int i =0; i < mCis.length; i++){ Integer index =new Integer(i); mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index); // TODO remove this once modem correctly notifies the unsols mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index); mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index); } } |
CommandsInterface即為RILJ實例,這里保存下來就可以直接與RIL進行通信。與此同時,在每個RILJ實例上注冊了3個事件,分別是
1 2 3 |
registerForIccStatusChanged(this,EVENT_ICC_STATUS_CHANGED, index); registerForAvailable(this,EVENT_ICC_STATUS_CHANGED, index); registerForNotAvailable(this,EVENT_RADIO_UNAVAILABLE, index); |
這里可以看到增加了一個index參數,這個index這里就是指的phoneId,是對雙卡的支持,是5.0新增的。增加了這個參數之后,EVENT_ICC_STATUS_CHANGED和EVENT_RADIO_UNAVAILABLE消息上來,UiccController才能分清是從哪個Phone過來的消息,也就是從哪個modem或者說是從哪個卡。。。
再來看看消息處理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@Override publicvoid handleMessage (Message msg){ synchronized(mLock){ Integer index = getCiIndex(msg);
if(index <0|| index >= mCis.length){ Rlog.e(LOG_TAG,"Invalid index : "+ index +" received with event "+ msg.what); return; }
switch(msg.what){ caseEVENT_ICC_STATUS_CHANGED: if(DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index)); break; caseEVENT_GET_ICC_STATUS_DONE: if(DBG) log("Received EVENT_GET_ICC_STATUS_DONE"); AsyncResult ar =(AsyncResult)msg.obj; onGetIccCardStatusDone(ar, index); break; caseEVENT_RADIO_UNAVAILABLE: if(DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card"); if(mUiccCards[index]!=null){ mUiccCards[index].dispose(); } mUiccCards[index]=null; mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index,null)); break; default: Rlog.e(LOG_TAG," Unknown Event "+ msg.what); } } } |
總結如下:
1). 消息到來之后,首先從Message中取出index值,也就是PhoneId;
2). 根據EVENT分發處理,如果是 EVENT_ICC_STATUS_CHANGED消息,對根據index調用對應的RILJ的getIccCardStatus函數,並傳遞EVENT_GET_ICC_STATUS_DONE,典型的異步處理,當EVENT_GET_ICC_STATUS_DONE返回時,就會從底層獲取到了這個index對應的卡的狀態,然后調用onGetIccCardStatusDone來更新對應index的卡相關的對象。卡相關的對象都是在這里被創建出來的。具體如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
privatesynchronizedvoid onGetIccCardStatusDone(AsyncResult ar, Integer index){ if(ar.exception !=null){ Rlog.e(LOG_TAG,"Error getting ICC status. " +"RIL_REQUEST_GET_ICC_STATUS should " +"never return an error", ar.exception); return; } if(!isValidCardIndex(index)){ Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : "+ index); return; }
IccCardStatus status =(IccCardStatus)ar.result;
if(mUiccCards[index]==null){ //Create new card mUiccCards[index]=new UiccCard(mContext, mCis[index], status, index);
/* // Update the UiccCard in base class, so that if someone calls // UiccManager.getUiccCard(), it will return the default card. if (index == PhoneConstants.DEFAULT_CARD_INDEX) { mUiccCard = mUiccCards[index]; } */ }else{ //Update already existing card mUiccCards[index].update(mContext, mCis[index], status); }
if(DBG) log("Notifying IccChangedRegistrants"); mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index,null));
} |
從代碼的實現可以看出,首先從result中解析出IccCardStatus,然后根據這個值進行UiccCard的創建,如果對應的index的卡 UiccCard已經存在,那么就會調用UiccCard.update來更新其內部的UiccCardApplication,這里提一下這幾個類的關系:
UIccController 中根據卡的個數創建對應數量的 UIccCard,而每個UiccCard中又會分別根據自己卡的實際情況創建對應的UiccCardApplication
UiccController 總體控制
UiccCard 具體的卡
UiccCardApplication 具體的卡里的應用【每個UiccCardApplication內部都會根據app_type來創建對應的 IccRecords和IccFileHandler對象作為操作卡上內容的接口】
3). 如果是 EVENT_RADIO_UNAVAILABLE消息,則會銷毀對應的UiccCard實例,並notify。
所以總結來看,UiccController就是通過向RIL注冊卡狀態變化的監聽,當底層一有變化時,會通過RIL上報給UiccController,這樣就會觸發其下發getIccCardStatus來查詢卡狀態,得到卡狀態后更新其內部的UiccCard及UIccCardApplication等。所以phone或者其他state tracker service可以通過UiccController來獲取到正確的卡信息。
整個家族樹總結如下:
IccardProxy
在我看來IccardProxy是一個有些多余的類,因為其內部實際維護的各種實例都是從UiccController框架中取得的,就連ICC_CARD_STATUS_CHANGED消息,也是通過向UiccControler注冊來得到notify,所以卡狀態的更新與維護,UiccController永遠是第一步的。
通過閱讀代碼,我感覺IcccardProxy就是一個用來提供給外部使用的接口,可以使得app不用直接操作UiccController,android給出來注釋如下:
/** * @Deprecated use {@link UiccController}.getUiccCard instead. * * The Phone App assumes that there is only one icc card, and one icc application * available at a time. Moreover, it assumes such object (represented with IccCard) * is available all the time (whether {@link RILConstants#RIL_REQUEST_GET_SIM_STATUS} returned * or not, whether card has desired application or not, whether there really is a card in the * slot or not). * * UiccController, however, can handle multiple instances of icc objects (multiple * {@link UiccCardApplication}, multiple {@link IccFileHandler}, multiple {@link IccRecords}) * created and destroyed dynamically during phone operation. * * This class implements the IccCard interface that is always available (right after default * phone object is constructed) to expose the current (based on voice radio technology) * application on the uicc card, so that external apps won't break. */ |
IccCardProxy在Phone創建的時候被構造,在UiccController初始化之后,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 |
// Instantiate UiccController so that all other classes can just // call getInstance() mUiccController = UiccController.make(context, sCommandsInterfaces);
for(int i =0; i < numPhones; i++){ PhoneBase phone =null; int phoneType = TelephonyManager.getPhoneType(networkModes[i]); if(phoneType == PhoneConstants.PHONE_TYPE_GSM){ phone =new GSMPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i); }elseif(phoneType == PhoneConstants.PHONE_TYPE_CDMA){ phone =new CDMALTEPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i); } Rlog.i(LOG_TAG,"Creating Phone with type = "+ phoneType +" sub = "+ i);
sProxyPhones[i]=newPhoneProxy(phone); } |
上面的l17行,通過phone創建的PhoneProxy代理類實例內部會創建IccCardProxy。
mIccCardProxy = new IccCardProxy(mActivePhone.getContext(), mCommandsInterface, mActivePhone.getPhoneId()); |
這里也可以看出,IccCardProxy實例的個數是與Phone的個數相對應的,有2個phone就會有兩個IccCardProxy對象,而UiccController里的UiccCard對象是跟卡動態關聯的。所以,app如果通過phoneproxy.getIccCard是可以隨時拿到IccCardProxy對象的,這樣就不會發生獲取不到卡狀態的問題。也就是說APP是不會直接操作UiccController的,都是通過IccCardProxy來進行。
先來看看他的構造函數:
1 2 3 4 5 6 7 8 9 10
11 12 13 14
15 16 17 18 19 20 21 |
public IccCardProxy(Context context, CommandsInterface ci){ log("Creating"); mContext = context; mCi = ci; 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); setExternalState(State.NOT_READY); }
public IccCardProxy(Context context, CommandsInterface ci,int cardIndex){ this(context, ci);
mCardIndex = cardIndex;
resetProperties(); setExternalState(State.NOT_READY,false); } |
黃色高亮的是幾個關鍵函數。
首先IccCardProxy會向UiccController中注冊ICC_CARD_STATUS_CHANGED消息,也就是在UiccController在更新完自己內部的UiccCard之后會notify IccCardProxy來讓IccCardProxy更新自己內部的UiccCard實例等,但這里有個問題,就是UiccController雖是單例的,但其內部的UiccCard卻可能會是多個的(多卡的情況下),而這里registerForIccChanged,注冊EVENT時,卻沒有指定phoneid,那么UiccController無論哪個卡有更新都會來notify,單卡的情況下無所謂,但雙卡的情況下就會引入多余notify,是一個可以考慮改進的地方。
另外,重置properties,這里使用系統屬性記錄卡的狀態
1 2 3 4 5 6 7 8 |
void resetProperties(){ if(mCurrentAppType == UiccController.APP_FAM_3GPP){ log("update icc_operator_numeric="+""); setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, mCardIndex,""); setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, mCardIndex,""); setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, mCardIndex,""); } } |
1 2 3 4 |
privatevoid setSystemProperty(String property,int slotId, String value){ long[] subId = SubscriptionController.getInstance().getSubId(slotId); TelephonyManager.setTelephonyProperty(property, subId[0], value); } |
TelephonyManager.setTelephonyProperty 這里不再貼了,說一下其記錄property來支持雙卡的方法:android使用同一個key,同時保存兩個卡的屬性值,值之間使用","分隔,順序以phoneId從小到大排序。使用時取出后將","分隔轉換為數組直接取下標即可。
總結:UiccController負責對卡槽的卡實時實例化或銷毀對象,IccCardProxy監聽UiccController里的變化並及時更新自己內部的狀態,Phone實例通過getIccCard得到IccCardProxy實例來獲取各種卡狀態,Phone再通過service形式將這些接口暴露給應用層。