from: http://blog.csdn.net/baimy1985/article/details/7820024
前面有一篇說了導卡上的資訊(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()方法看起
[java] view plaincopy
- 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,這是告訴告訴我們要先去尋找卡上這個位置的內容。
[java] view plaincopy
- 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這個標誌,要用它來接收查詢結果的。
[java] view plaincopy
- 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讀完後再返回,裡面又分了兩種,一種是電話號碼的連絡人,一種是郵箱地址的連絡人,郵箱的相比電話號碼還要麻煩點。
[java] view plaincopy
- 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()的代碼。
[java] view plaincopy
- 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了, 之前讀卡上的資訊代碼也是走這裡
[java] view plaincopy
- 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()方法中完成字串的解析得到連絡人資訊。
[java] view plaincopy
- 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卡能存的連絡人要多些,但數量不固定,其中讀寫連絡人的郵箱地址要麻煩一些,由於儲存空間的限制需要多次找對應的索引能找到我們想要的東西。至於讀寫卡連絡人的具體例子,後續單獨補充吧。最後貼圖一張,有圖有真相。