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卡能存的聯絡人要多些,但數量不固定,其中讀寫聯絡人的郵箱地址要麻煩一些,由於儲存空間的限制需要多次找對應的索引能找到我們想要的東西。至於讀寫卡聯絡人的具體例子,後續單獨補充吧。最後貼圖一張,有圖有真相。