1. 程式人生 > >android -- sim/usim卡導聯絡人

android -- sim/usim卡導聯絡人

        前面有一篇說了導卡上的資訊(android 資訊(mms)的故事(七)-- sim卡簡訊),sim卡上還有一類非常重要的資訊,就是卡上的聯絡人。不知道大家注意到沒有,android手機,尤其是那些帶有運營商標識的手機開機都比較慢,這個和開機導卡上的聯絡人和資訊不無關係,運營商是要求開機必須導卡的,不過要說句公道話,開機慢和導卡有關但也不能完全歸咎於它(android手機啟動時載入的東西本身也很多),當然如果你的手機開機不導卡也慢那肯定是另有原因的。 

        當然Android原始碼開機是不導卡,如果我們需要檢視卡上的聯絡人需要手動匯入,從聯絡人Contact應用ContactsListActivity.java這個類的menu選單找到匯入匯出,選擇匯入sim卡聯絡人,進入SimContactsSelectActivity.java這個類,在這個類裡會執行query()方法,對應的provider和uri分別是IccProvider.java與uri.parse(content://icc/adn),進入IccProvider.java後的程式碼是本文要關注的部分,從query()方法看起

      public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sort) {
        ArrayList<ArrayList> results;
            switch (URL_MATCHER.match(url)) {
                case ADN:
                results = loadFromEf(IccConstants.EF_ADN);
                break;

       我們關心這個loadFromEf()方法,IccConstants.EF_AND這個值是6F3A,這是告訴告訴我們要先去查詢卡上這個位置的內容。  

    private ArrayList<ArrayList> loadFromEf(int efType) {
        ArrayList<ArrayList> results = new ArrayList<ArrayList>();
        List<AdnRecord> adnRecords = null; 
        try {
            IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(//Binder來了                    ServiceManager.getService("simphonebook"));
            if (iccIpb != null) {
                adnRecords = iccIpb.getAdnRecordsInEf(efType);//proxy模式
            }
        } …省略次要程式碼….        }

        接下來程式碼走到IccPhoneBookInterfaceManager.java這個類的getAdnRecordsInEf(intefid)方法,其中涉及了代理模式,IccPhoneBookInterfaceManagerProxy.java在中間做了個轉換。在getAdnRecordsInEf(intefid)方法裡呼叫了下updateEfForIccType(efid),判斷是sim卡還是usim卡,確定下面的efid是用IccConstants.EF_AND還是IccConstants.EF_PBR,真正的查詢是requestLoadAllAdnLike()是這個方法,這裡要注意EVENT_LOAD_DONE這個標誌,要用它來接收查詢結果的。

    requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
        ArrayList<Message> waiters;
        ArrayList<AdnRecord> result;
        if (efid == EF_PBR) {  //usim卡,這裡我們看這個方法
            result = mUsimPhoneBookManager.loadEfFilesFromUsim();
        } else {//sim卡
            result = getRecordsIfLoaded(efid);
        }。

       看下loadEfFilesFromUsim()方法,迴圈讀取直到所有file讀完後再返回,裡面又分了兩種,一種是電話號碼的聯絡人,一種是郵箱地址的聯絡人,郵箱的相比電話號碼還要麻煩點。

   public ArrayList<AdnRecord> loadEfFilesFromUsim() {
        synchronized (mLock) {
           //…省略次要程式碼….
            numRecs = mPbrFile.mFileIds.size();
            for (int i = 0; i < numRecs; i++) {
                readAdnFileAndWait(i);
                readEmailFileAndWait(i);
            } // All EF files are loaded, post the response.
        }

       readAdnFileAndWait(i)和readEmailFileAndWait(i)最後都會呼叫mPhone.getIccFileHandler().loadEFLinearFixedAll()方法,只不過具體的引數不同。看下loadEFLinearFixedAll()的程式碼。

    public void loadEFLinearFixedAll(int fileid, Message onLoaded) {
        Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
                        new LoadLinearFixedContext(fileid,onLoaded));
        phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
    }
        iccIO()方法顯示程式碼跑到RIL.java了, 之前讀卡上的資訊程式碼也是走這裡       
    public void iccIO (int command, int fileid, String path, int p1, int p2, int p3,
            String data, String pin2, Message result) {      
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_IO, result);//關注這個TAG標誌
        rr.mp.writeInt(command);
        rr.mp.writeInt(fileid);
        rr.mp.writeString(path);
        rr.mp.writeInt(p1);
        rr.mp.writeInt(p2);
        rr.mp.writeInt(p3);
        rr.mp.writeString(data);
        rr.mp.writeString(pin2);
        send(rr);
    }
   根據上面那個TAG,可以在Reference-ril.c找到這個分支。
   case RIL_REQUEST_SIM_IO:
            requestSIM_IO(data,datalen,t);
            break;
      閱讀requestSIM_IO()方法可以看到android原始碼是如何讀取sim卡上的聯絡人的,原始碼使用了AT +CRSM命令,關於AT命令可以看3gpp 27.007這個文件。讀取完成時會收到EVENT_PBR_LOAD_DONE這個訊息,在裡面createPbrFile()方法中完成字串的解析得到聯絡人資訊。
public void handleMessage(Message msg) {
 …省略程式碼….    
        switch (msg.what) {
            case EVENT_PBR_LOAD_DONE:
                if (ar.exception == null) {
                    createPbrFile((ArrayList<byte[]>) ar.result);
                }…省略程式碼….               
                break;

        到這裡,卡聯絡人匯入大部分流程就走完了,後面還有些寫入聯絡人資料庫的操作比較簡單就不寫了,總體上流程還算清晰,只是迴圈比較多,遠端除錯的時候並不是很方便,通常一個sim卡可以存250個聯絡人,一個usim卡能存的聯絡人要多些,但數量不固定,其中讀寫聯絡人的郵箱地址要麻煩一些,由於儲存空間的限制需要多次找對應的索引能找到我們想要的東西。至於讀寫卡聯絡人的具體例子,後續單獨補充吧。最後貼圖一張,有圖有真相。