Android 源碼開發系列 (四) Android 4.2中與SIM/USIM 管理流程

     Android在經過幾次更新後,在與卡相關的管理出現的重大的改變。謹以些文,給自己做下學習該塊的筆記。既然作爲開源的,我們第一步當然是從代碼入手,分析該處的關係。

Uicc的架構圖如下:

從圖中可以看出,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的實例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章