Android在经过几次更新后,在与卡相关的管理出现的重大的改变。谨以些文,给自己做下学习该块的笔记。既然作为开源的,我们第一步当然是从代码入手,分析该处的关系。

Uicc的架构图如下:

android SIM卡数据流程 安卓sim卡管理_ci

从图中可以看出,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.   
    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的时候做了好多的事。需要自己去体会。