Android 源码开发系列 (4) Android 4.2中与SIM/USIM 管理流程

Android 源码开发系列 (四) Android 4.2中与SIM/USIM 管理流程

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

Uicc的架构图如下:

Android 源码开发系列 (4) Android 4.2中与SIM/USIM 管理流程

从图中可以看出,UiccController是用来控制所有与卡相关的操作,通过UiccController,我们可以访问IccRecords(SIM卡相关), CatService(STK相关),IccFileHandle(读取SIM卡文件)。

与前面几个版相比,变化最大的就是IccCard.java这个文件,在4.2以前的版本中,该IccCard是一个类,而在4.2中,它却是一个接口,代码如下:

public interface IccCard 
所以就不会存在以前版本中的SimCard, UsimCard, 现在统一用IccCardProxy来替代他们所有功能。

下面我将会展现卡相关的怎么样初始化的,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;
        }
    }


从上面的代码看,最终都是调用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怎么初始化的。代码如下:

                // 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;
                    }
                }
在上面这段代码,请大家注意NEW  PHONEPROXY的时候,会根据PHONE的类型创建不同的PHONE,但然后再用PHONEPROXY把所有PHONE之间的区别不一样的给屏蔽掉,以便对外面的接口来说,都是一致的。PHONEPROXY的初始化,如下:

    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);
        }
    }
至于细节东东,只有去看代码了,在创建GSMPHONE的时候做了好多的事。需要自己去体会。由于一直从事GSMPHONE ,好像还没有遇到CDMA的实例。