Android在经过几次更新后,在与卡相关的管理出现的重大的改变。谨以些文,给自己做下学习该块的笔记。既然作为开源的,我们第一步当然是从代码入手,分析该处的关系。
Uicc的架构图如下:
从图中可以看出,UiccController是用来控制所有与卡相关的操作,通过UiccController,我们可以访问IccRecords(SIM卡相关), CatService(STK相关),IccFileHandle(读取SIM卡文件)。
与前面几个版相比,变化最大的就是IccCard.java这个文件,在4.2以前的版本中,该IccCard是一个类,而在4.2中,它却是一个接口,代码如下:
[html] view plain
1. public interface IccCard
所以就不会存在以前版本中的SimCard, UsimCard, 现在统一用IccCardProxy来替代他们所有功能。
下面我将会展现卡相关的怎么样初始化的,UiccController是被PhoneFacotry中的makeDefaultPhone去初始化的,代码如下:
[html] view plain
1. sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
2.
3. // Instantiate UiccController so that all other classes can just call getInstance()
4. UiccController.make(context, sCommandsInterface);
5.
6. int phoneType = TelephonyManager.getPhoneType(networkMode);
7. if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
8. Log.i(LOG_TAG, "Creating GSMPhone");
9. sProxyPhone = new PhoneProxy(new GSMPhone(context,
10. sCommandsInterface, sPhoneNotifier));
可以看到,第一步先实例化RIL,我们会得到一个RIL的实例,然后,把这个实例调用UiccControl的make函数。这个函数会实例UiccController中一个成员变量,其它使用时,直接使用getInstance方向即可, 从这个地方可以看,它是个单例的模式,只用创建一次。代码如下:
[java] view plain
1. public static UiccController make(Context c, CommandsInterface ci) {
2. synchronized (mLock) {
3. if (mInstance != null) {
4. throw new RuntimeException("UiccController.make() should only be called once");
5. }
6. new UiccController(c, ci);
7. return mInstance;
8. }
9. }
10. public static UiccController getInstance() {
11. synchronized (mLock) {
12. if (mInstance == null) {
13. throw new RuntimeException(
14. "UiccController.getInstance can't be called before make()");
15. }
16. return mInstance;
17. }
18. }
19. private UiccController(Context c, CommandsInterface ci) {
20. if (DBG) log("Creating UiccController");
21. mContext = c;
22. mCi = ci;
23. this, EVENT_ICC_STATUS_CHANGED, null);
24. // TODO remove this once modem correctly notifies the unsols
25. this, EVENT_ICC_STATUS_CHANGED, null);
26. }
2. 在UiccController初始化完成后,从它的构造函数中,我们可以看到该类还注册了一个监控事件,分别为EVENT_ICC_STATUS_CHANGED,该事件是用来监控SIM卡的状态有变化的,由上层主动给FRAMEWORK上报消息。当接收到这个消息后,UiccController会通过RIL给MODEM发送消息,查询下SIM卡的状态。
[java] view plain
1. public void handleMessage (Message msg) {
2. synchronized (mLock) {
3. switch (msg.what) {
4. case EVENT_ICC_STATUS_CHANGED:
5. if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
6. mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
7. break;
8. case EVENT_GET_ICC_STATUS_DONE:
9. if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
10. AsyncResult ar = (AsyncResult)msg.obj;
11. onGetIccCardStatusDone(ar);
12. break;
13. default:
14. " Unknown Event " + msg.what);
15. }
16. }
17. }
18.
19. private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
20. if (ar.exception != null) {
21. "Error getting ICC status. "
22. "RIL_REQUEST_GET_ICC_STATUS should "
23. "never return an error", ar.exception);
24. return;
25. }
26.
27. IccCardStatus status = (IccCardStatus)ar.result;
28.
29. if (mUiccCard == null) {
30. //Create new card
31. new UiccCard(mContext, mCi, status);
32. else {
33. //Update already existing card
34. mUiccCard.update(mContext, mCi , status);
35. }
36.
37. if (DBG) log("Notifying IccChangedRegistrants");
38. mIccChangedRegistrants.notifyRegistrants();
39. }
40.
41.
当发送查询请求,待到查询结果上来后,会初始化UiccController的成员变量mUiccCard,如果是mUiccCard是空,即还没有初始化过,就重新NEW一个UiccCard的实例。如果是实例化过的,就重新更新下UiccCard的信息。到这个时候时,就算有SIM卡了,需要向其它注册了 监控SIM卡状态的注册者通知。以便其它应用能做知道SIM卡已经好了。
3. 我们接下往下走,刚说到在初始化UiccCard的时候,会重新NEW一个实例,这个NEW的过程到底干了什么,请看下面的代码:
[java] view plain
1. public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
2. if (DBG) log("Creating");
3. mCardState = ics.mCardState;
4. update(c, ci, ics);
5. }
6.
7. public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
8. synchronized (mLock) {
9. if (mDestroyed) {
10. "Updated after destroyed! Fix me!");
11. return;
12. }
13. CardState oldState = mCardState;
14. mCardState = ics.mCardState;
15. mUniversalPinState = ics.mUniversalPinState;
16. mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
17. mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
18. mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
19. mContext = c;
20. mCi = ci;
21. //update applications
22. if (DBG) log(ics.mApplications.length + " applications");
23. for ( int i = 0; i < mUiccApplications.length; i++) {
24. if (mUiccApplications[i] == null) {
25. //Create newly added Applications
26. if (i < ics.mApplications.length) {
27. new UiccCardApplication(this,
28. ics.mApplications[i], mContext, mCi);
29. }
30. else if (i >= ics.mApplications.length) {
31. //Delete removed applications
32. mUiccApplications[i].dispose();
33. null;
34. else {
35. //Update the rest
36. mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
37. }
38. }
39.
40. if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
41. // Initialize or Reinitialize CatService
42. mCatService = CatService.getInstance(mCi,
43. mContext,
44. this);
45. else {
46. if (mCatService != null) {
47. mCatService.dispose();
48. }
49. null;
50. }
51.
52. sanitizeApplicationIndexes();
53.
54. RadioState radioState = mCi.getRadioState();
55. if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
56. + mLastRadioState);
57. // No notifications while radio is off or we just powering up
58. if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
59. if (oldState != CardState.CARDSTATE_ABSENT &&
60. mCardState == CardState.CARDSTATE_ABSENT) {
61. if (DBG) log("update: notify card removed");
62. mAbsentRegistrants.notifyRegistrants();
63. null));
64. else if (oldState == CardState.CARDSTATE_ABSENT &&
65. mCardState != CardState.CARDSTATE_ABSENT) {
66. if (DBG) log("update: notify card added");
67. null));
68. }
69. }
70. mLastRadioState = radioState;
71. }
72. }
从上面的代码看,最终都是调用update方法来更新状态。这个update的方法做了以下工作:
第一:更新SIM卡的信息,第二,判断下当前状态转换是怎么样, 即更新mUiccApplications的数组信息。第三,更新STK的SERVICE,这个看过我的前面STK讲解的童鞋,看到这个应该会很熟悉,对这个正是给STK提供服务的SERVICE,从这里可以看到,STK初始化跟以前不一样,这样的话,STK服务启动的较慢,这个时候,和STK上层的一个STK SERVICE RUNNING这个是配合,只有CAT SERVICE向下面报告了CAT SERVICE好了,下面才可以向上报STK的相关命令。第四,通过状态判断,向外面发送CARD REMOVE还是CARDADD消息。
4. 细心点的童鞋有可能会注意到,我在开始的提到的IccCard这个东东怎么没有看到呢,其它,他是在Phone创建后,用phoneproxy来替换phone(包括GSMPHONE,CDMAPHONE等等),这样做的好处是为了屏蔽这些PHONE之间的差异点。对外来说,都是一样的接口。好,既然这样的话,我要去看看phoneproxy怎么初始化的。代码如下:
[java] view plain
1. // Instantiate UiccController so that all other classes can just call getInstance()
2. UiccController.make(context, sCommandsInterface);
3.
4. int phoneType = TelephonyManager.getPhoneType(networkMode);
5. if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
6. "Creating GSMPhone");
7. new PhoneProxy(new GSMPhone(context,
8. sCommandsInterface, sPhoneNotifier));
9. } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
10. switch (TelephonyManager.getLteOnCdmaModeStatic()) {
11. case PhoneConstants.LTE_ON_CDMA_TRUE:
12. "Creating CDMALTEPhone");
13. new PhoneProxy(new CDMALTEPhone(context,
14. sCommandsInterface, sPhoneNotifier));
15. break;
16. case PhoneConstants.LTE_ON_CDMA_FALSE:
17. default:
18. "Creating CDMAPhone");
19. new PhoneProxy(new CDMAPhone(context,
20. sCommandsInterface, sPhoneNotifier));
21. break;
22. }
23. }
在上面这段代码,请大家注意NEW PHONEPROXY的时候,会根据PHONE的类型创建不同的PHONE,但然后再用PHONEPROXY把所有PHONE之间的区别不一样的给屏蔽掉,以便对外面的接口来说,都是一致的。PHONEPROXY的初始化,如下:
[java] view plain
1. public PhoneProxy(PhoneBase phone) {
2. mActivePhone = phone;
3. mResetModemOnRadioTechnologyChange = SystemProperties.getBoolean(
4. false);
5. new IccSmsInterfaceManagerProxy(
6. phone.getIccSmsInterfaceManager());
7. new IccPhoneBookInterfaceManagerProxy(
8. phone.getIccPhoneBookInterfaceManager());
9. new PhoneSubInfoProxy(phone.getPhoneSubInfo());
10. mCommandsInterface = ((PhoneBase)mActivePhone).mCM;
11.
12. this, EVENT_RIL_CONNECTED, null);
13. this, EVENT_RADIO_ON, null);
14. mCommandsInterface.registerForVoiceRadioTechChanged(
15. this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
16. new IccCardProxy(phone.getContext(), mCommandsInterface);
17. if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
18. // For the purpose of IccCardProxy we only care about the technology family
19. mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
20. else if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
21. mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
22. }
23. }
至于细节东东,只有去看代码了,在创建GSMPHONE的时候做了好多的事。需要自己去体会。