原文:http://blog.csdn.net/guoleimail/article/details/8738041
Android在经过几次更新后,在与卡相关的管理出现的重大的改变。谨以些文,给自己做下学习该块的笔记。既然作为开源的,我们第一步当然是从代码入手,分析该处的关系。
Uicc的架构图如下:
从图中可以看出,UiccController是用来控制所有与卡相关的操作,通过UiccController,我们可以访问IccRecords(SIM卡相关), CatService(STK相关),IccFileHandle(读取SIM卡文件)。
与前面几个版相比,变化最大的就是IccCard.java这个文件,在4.2以前的版本中,该IccCard是一个类,而在4.2中,它却是一个接口,代码如下:
- public interface IccCard
下面我将会展现卡相关的怎么样初始化的,UiccController是被PhoneFacotry中的makeDefaultPhone去初始化的,代码如下:
- sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
- // Instantiate UiccController so that all other classes can just call getInstance()
- UiccController.make(context, sCommandsInterface);
- int phoneType = TelephonyManager.getPhoneType(networkMode);
- if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- Log.i(LOG_TAG, "Creating GSMPhone");
- sProxyPhone = new PhoneProxy(new GSMPhone(context,
- sCommandsInterface, sPhoneNotifier));
可以看到,第一步先实例化RIL,我们会得到一个RIL的实例,然后,把这个实例调用UiccControl的make函数。这个函数会实例UiccController中一个成员变量,其它使用时,直接使用getInstance方向即可, 从这个地方可以看,它是个单例的模式,只用创建一次。代码如下:
- public static UiccController make(Context c, CommandsInterface ci) {
- synchronized (mLock) {
- if (mInstance != null) {
- throw new RuntimeException("UiccController.make() should only be called once");
- }
- mInstance = new UiccController(c, ci);
- return mInstance;
- }
- }
- public static UiccController getInstance() {
- synchronized (mLock) {
- if (mInstance == null) {
- throw new RuntimeException(
- "UiccController.getInstance can't be called before make()");
- }
- return mInstance;
- }
- }
- private UiccController(Context c, CommandsInterface ci) {
- if (DBG) log("Creating UiccController");
- mContext = c;
- mCi = ci;
- mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
- // TODO remove this once modem correctly notifies the unsols
- mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null);
- }
2. 在UiccController初始化完成后,从它的构造函数中,我们可以看到该类还注册了一个监控事件,分别为EVENT_ICC_STATUS_CHANGED,该事件是用来监控SIM卡的状态有变化的,由上层主动给FRAMEWORK上报消息。当接收到这个消息后,UiccController会通过RIL给MODEM发送消息,查询下SIM卡的状态。
- public void handleMessage (Message msg) {
- synchronized (mLock) {
- switch (msg.what) {
- case EVENT_ICC_STATUS_CHANGED:
- if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
- mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
- break;
- case EVENT_GET_ICC_STATUS_DONE:
- if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
- AsyncResult ar = (AsyncResult)msg.obj;
- onGetIccCardStatusDone(ar);
- break;
- default:
- Log.e(LOG_TAG, " Unknown Event " + msg.what);
- }
- }
- }
- private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
- if (ar.exception != null) {
- Log.e(LOG_TAG,"Error getting ICC status. "
- + "RIL_REQUEST_GET_ICC_STATUS should "
- + "never return an error", ar.exception);
- return;
- }
- IccCardStatus status = (IccCardStatus)ar.result;
- if (mUiccCard == null) {
- //Create new card
- mUiccCard = new UiccCard(mContext, mCi, status);
- } else {
- //Update already existing card
- mUiccCard.update(mContext, mCi , status);
- }
- if (DBG) log("Notifying IccChangedRegistrants");
- mIccChangedRegistrants.notifyRegistrants();
- }
当发送查询请求,待到查询结果上来后,会初始化UiccController的成员变量mUiccCard,如果是mUiccCard是空,即还没有初始化过,就重新NEW一个UiccCard的实例。如果是实例化过的,就重新更新下UiccCard的信息。到这个时候时,就算有SIM卡了,需要向其它注册了 监控SIM卡状态的注册者通知。以便其它应用能做知道SIM卡已经好了。
3. 我们接下往下走,刚说到在初始化UiccCard的时候,会重新NEW一个实例,这个NEW的过程到底干了什么,请看下面的代码:
- public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
- if (DBG) log("Creating");
- mCardState = ics.mCardState;
- update(c, ci, ics);
- }
- public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
- synchronized (mLock) {
- if (mDestroyed) {
- loge("Updated after destroyed! Fix me!");
- return;
- }
- CardState oldState = mCardState;
- mCardState = ics.mCardState;
- mUniversalPinState = ics.mUniversalPinState;
- mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
- mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
- mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
- mContext = c;
- mCi = ci;
- //update applications
- if (DBG) log(ics.mApplications.length + " applications");
- for ( int i = 0; i < mUiccApplications.length; i++) {
- if (mUiccApplications[i] == null) {
- //Create newly added Applications
- if (i < ics.mApplications.length) {
- mUiccApplications[i] = new UiccCardApplication(this,
- ics.mApplications[i], mContext, mCi);
- }
- } else if (i >= ics.mApplications.length) {
- //Delete removed applications
- mUiccApplications[i].dispose();
- mUiccApplications[i] = null;
- } else {
- //Update the rest
- mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
- }
- }
- if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
- // Initialize or Reinitialize CatService
- mCatService = CatService.getInstance(mCi,
- mContext,
- this);
- } else {
- if (mCatService != null) {
- mCatService.dispose();
- }
- mCatService = null;
- }
- sanitizeApplicationIndexes();
- RadioState radioState = mCi.getRadioState();
- if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
- + mLastRadioState);
- // No notifications while radio is off or we just powering up
- if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
- if (oldState != CardState.CARDSTATE_ABSENT &&
- mCardState == CardState.CARDSTATE_ABSENT) {
- if (DBG) log("update: notify card removed");
- mAbsentRegistrants.notifyRegistrants();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
- } else if (oldState == CardState.CARDSTATE_ABSENT &&
- mCardState != CardState.CARDSTATE_ABSENT) {
- if (DBG) log("update: notify card added");
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
- }
- }
- mLastRadioState = radioState;
- }
- }
第一:更新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怎么初始化的。代码如下:
- // Instantiate UiccController so that all other classes can just call getInstance()
- UiccController.make(context, sCommandsInterface);
- int phoneType = TelephonyManager.getPhoneType(networkMode);
- if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- Log.i(LOG_TAG, "Creating GSMPhone");
- sProxyPhone = new PhoneProxy(new GSMPhone(context,
- sCommandsInterface, sPhoneNotifier));
- } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- switch (TelephonyManager.getLteOnCdmaModeStatic()) {
- case PhoneConstants.LTE_ON_CDMA_TRUE:
- Log.i(LOG_TAG, "Creating CDMALTEPhone");
- sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
- sCommandsInterface, sPhoneNotifier));
- break;
- case PhoneConstants.LTE_ON_CDMA_FALSE:
- default:
- Log.i(LOG_TAG, "Creating CDMAPhone");
- sProxyPhone = new PhoneProxy(new CDMAPhone(context,
- sCommandsInterface, sPhoneNotifier));
- break;
- }
- }
- public PhoneProxy(PhoneBase phone) {
- mActivePhone = phone;
- mResetModemOnRadioTechnologyChange = SystemProperties.getBoolean(
- TelephonyProperties.PROPERTY_RESET_ON_RADIO_TECH_CHANGE, false);
- mIccSmsInterfaceManagerProxy = new IccSmsInterfaceManagerProxy(
- phone.getIccSmsInterfaceManager());
- mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy(
- phone.getIccPhoneBookInterfaceManager());
- mPhoneSubInfoProxy = new PhoneSubInfoProxy(phone.getPhoneSubInfo());
- mCommandsInterface = ((PhoneBase)mActivePhone).mCM;
- mCommandsInterface.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
- mCommandsInterface.registerForOn(this, EVENT_RADIO_ON, null);
- mCommandsInterface.registerForVoiceRadioTechChanged(
- this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
- mIccCardProxy = new IccCardProxy(phone.getContext(), mCommandsInterface);
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
- // For the purpose of IccCardProxy we only care about the technology family
- mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
- } else if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
- }
- }