Android 5.0 Uicc框架分析

已同步更新至我的blog:   dxjia.cnandroid

Uicc框架

UICC框架是Android在4.1引入的,使的對卡的管理控制更加清晰。要了解這個UICC框架,須要從UiccController開始,它是整個UICC框架的開始與控制者,該類被設計爲單例,是消息處理類Handler的子類,因此其實現確定是基於event觸發的,其在Phone建立的早期被初始化:數組

1 app

2 框架

3異步

// Instantiate UiccController so that all other classes can just ide

// call getInstance() 函數

mUiccController = UiccController.make(context, sCommandsInterfaces);ui

make函數只能被調用一次,之後若是要想得到UiccController對象,只能經過getInstance進行,來看UiccController的構造函數:this

1 spa

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形式將這些接口暴露給應用層。

相關文章
相關標籤/搜索