轉載:
https://blog.csdn.net/firedancer0089/article/details/60762199
https://blog.csdn.net/mafei19870124/article/details/73521995
https://segmentfault.com/a/1190000011314014 (主要參考)
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9cmaOh3YtJGcxIjYox2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZwpmL3cTN5ITOwgDMxMTNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
第一部分:讀取卡聯系人流程
讀取sim卡聯系人 分兩條路線,一條是用于資料的緩沖路線,一條是用于消息的傳遞
IccProvider.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)
public class IccProvider extends ContentProvider {
從上面可以看到,真正的ICCProvider是在framework中繼承ContentProvider,處理ADN/FDN/SDN的query/insert/update/delete等操作,和SIM卡互動完成後,将資料改變資訊通知給ContentObserver,然後ContentObserver将資料變化的發送給注冊監聽的應用,Contacts應用做相應的同步動作。
UiccPhoneBookController.java
UiccPhoneBookController在ProxyController的構造方法中初始化。
mUiccPhoneBookController = new UiccPhoneBookController(mPhones);
UiccPhoneBookController的構造方法:
public class UiccPhoneBookController extends IIccPhoneBook.Stub {
private static final String TAG = "UiccPhoneBookController";
private Phone[] mPhone;
/* only one UiccPhoneBookController exists */
public UiccPhoneBookController(Phone[] phone) {
if (ServiceManager.getService("simphonebook") == null) {
ServiceManager.addService("simphonebook", this);
}
mPhone = phone;
}
IccProvider通過AIDL調用UiccPhoneBookController:
IccProvider.java frameworks\opt\telephony\src\java\com\android\internal\telephony
private MatrixCursor loadFromEf(int efType, int subId) {
if (DBG) log("loadFromEf: efType=0x" +
Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId);
List<AdnRecord> adnRecords = null;
try {
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if (iccIpb != null) {
adnRecords = iccIpb.getAdnRecordsInEfForSubscriber(subId, efType);
}
} catch (RemoteException ex) {
// ignore it
} catch (SecurityException ex) {
if (DBG) log(ex.toString());
}
關于AIDL的實作後面詳細學習
IccPhoneBookInterfaceManager.java
IccPhoneBookInterfaceManager是在Phone的初始化時完成執行個體化:
protected void initOnce(CommandsInterface ci) {
if (ci instanceof SimulatedRadioControl) {
mSimulatedRadioControl = (SimulatedRadioControl) ci;
}
mCT = mTelephonyComponentFactory.makeGsmCdmaCallTracker(this);
mIccPhoneBookIntManager = mTelephonyComponentFactory.makeIccPhoneBookInterfaceManager(this);
AdnRecordCache.java
AdnRecordCache在IccPhoneBookInterfaceManager構造方法中完成初始化:
public IccPhoneBookInterfaceManager(Phone phone) {
this.mPhone = phone;
IccRecords r = phone.getIccRecords();
if (r != null) {
mAdnCache = r.getAdnCache();
}
}
查詢SIM卡聯系人流程
1、 IccProvider.java
static塊中加入了adn的uri,adn/subid/#可以指定讀取的sim卡
static {
URL_MATCHER.addURI("icc", "adn", ADN);
URL_MATCHER.addURI("icc", "adn/subId/#", ADN_SUB);
URL_MATCHER.addURI("icc", "fdn", FDN);
URL_MATCHER.addURI("icc", "fdn/subId/#", FDN_SUB);
URL_MATCHER.addURI("icc", "sdn", SDN);
URL_MATCHER.addURI("icc", "sdn/subId/#", SDN_SUB);
}
UiccPhoneBookController時一個Binder服務類,接口是IIccPhoneBook,服務名"simphonebook"
private MatrixCursor loadFromEf(int efType, int subId) {
if (DBG) log("loadFromEf: efType=0x" +
Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId);
List<AdnRecord> adnRecords = null;
try {
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
ServiceManager.getService("simphonebook"));
if (iccIpb != null) {
adnRecords = iccIpb.getAdnRecordsInEfForSubscriber(subId, efType);
}
} catch (RemoteException ex) {
// ignore it
} catch (SecurityException ex) {
if (DBG) log(ex.toString());
}
if (adnRecords != null) {
// Load the results
final int N = adnRecords.size();
final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N);
if (DBG) log("adnRecords.size=" + N);
for (int i = 0; i < N ; i++) {
loadRecord(adnRecords.get(i), cursor, i);
}
return cursor;
} else {
// No results to load
Rlog.w(TAG, "Cannot load ADN records");
return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES);
}
}
2.UiccPhoneBookController
UiccPhoneBookController.java frameworks\opt\telephony\src\java\com\android\internal\telephony
getAdnRecordsInEfForSubscriber方法:
@Override
public List<AdnRecord> getAdnRecordsInEfForSubscriber(int subId, int efid)
throws android.os.RemoteException {
//擷取執行個體,執行個體在文章開始Phone初始化時設定
IccPhoneBookInterfaceManager iccPbkIntMgr =
getIccPhoneBookInterfaceManager(subId);
if (iccPbkIntMgr != null) {
//調用getAdnRecordsInEf方法
return iccPbkIntMgr.getAdnRecordsInEf(efid);
} else {
Rlog.e(TAG,"getAdnRecordsInEf iccPbkIntMgr is" +
"null for Subscription:"+subId);
return null;
}
}
3.IccPhoneBookInterfaceManager
// 在efid中加載AdnRecords并将它們作為mRecords傳回AdnRecords清單
public List<AdnRecord> getAdnRecordsInEf(int efid) {
//檢查權限
if (mPhone.getContext().checkCallingOrSelfPermission(
android.Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Requires android.permission.READ_CONTACTS permission");
}
//根據SIM卡類型設定EF_ID
efid = updateEfForIccType(efid);
if (DBG) logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid).toUpperCase());
//線程同步
synchronized(mLock) {
checkThread();
AtomicBoolean status = new AtomicBoolean(false);
//回調message
Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
if (mAdnCache != null) {
//查詢聯系人,通過message傳回
mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
//此處線程wait(),mBaseHandler 擷取mRecords後notifyPending(),代碼才能繼續往下執行
waitForResult(status);
} else {
loge("Failure while trying to load from SIM due to uninitialised adncache");
}
}
return mRecords;
}
4.AdnRecordCache查詢,requestLoadAllAdnLike()方法
/**
* Responds with exception (in response) if efid is not a known ADN-like
* record
*/
public void requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
ArrayList<Message> waiters;
ArrayList<AdnRecord> result;
if (efid == EF_PBR) {
result = mUsimPhoneBookManager.loadEfFilesFromUsim();
} else {
result = getRecordsIfLoaded(efid);//該方法實際是從緩存讀取資料
}
// Have we already loaded this efid?
if (result != null) {
if (response != null) {
AsyncResult.forMessage(response).result = result;
response.sendToTarget();
}
return;
}
// Have we already *started* loading this efid?
waiters = mAdnLikeWaiters.get(efid);
if (waiters != null) {//正在讀取中,把回調消息加入等待隊列中,return
// There's a pending request for this EF already
// just add ourselves to it
waiters.add(response);
return;
}
// Start loading efid
waiters = new ArrayList<Message>();
waiters.add(response);
mAdnLikeWaiters.put(efid, waiters);
if (extensionEf < 0) {
// respond with error if not known ADN-like record
if (response != null) {
AsyncResult.forMessage(response).exception
= new RuntimeException("EF is not known ADN-like EF:0x" +
Integer.toHexString(efid).toUpperCase());
response.sendToTarget();
}
return;
}
new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,
obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));//正真讀取
}
流程分析已經寫在注釋中,usim是另一條分支(本流程不做解析),
第一條線資料的緩沖,繼續看loadAllFromEF
frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
/**
* Resulting ArrayList<adnRecord> is placed in response.obj.result
* or response.obj.exception is set
*/
public void loadAllFromEF(int ef, int extensionEF,
Message response) {
mEf = ef;
mExtensionEF = extensionEF;
mUserResponse = response;
/* If we are loading from EF_ADN, specifically
* specify the path as well, since, on some cards,
* the fileid is not unique.
*/
mFh.loadEFLinearFixedAll(
ef, getEFPath(ef),
obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
}
IccFileHandler 調用loadEFLinearFixedAll發送請求,讀取結果會在handleMessage中處理
@Override
public void handleMessage(Message msg) {
......
case EVENT_ADN_LOAD_ALL_DONE:
ar = (AsyncResult)(msg.obj);
ArrayList<byte[]> datas = (ArrayList<byte[]>)(ar.result);
if (ar.exception != null) {
throw new RuntimeException("load failed", ar.exception);
}
mAdns = new ArrayList<AdnRecord>(datas.size());
mResult = mAdns;
mPendingExtLoads = 0;
for(int i = 0, s = datas.size() ; i < s ; i++) {
adn = new AdnRecord(mEf, 1 + i, datas.get(i));
mAdns.add(adn);
if (adn.hasExtendedRecord()) {
// If we have a valid value in the ext record field,
// we're not done yet: we need to read the corresponding
// ext record and append it
mPendingExtLoads++;
mFh.loadEFLinearFixed(
mExtensionEF, adn.mExtRecord,
obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
}
}
mAdns.add(adn); for循環中向mAdns添加資料。
擷取到了卡聯系人總數目,先用空值初始化mAdn清單,然後發送消息EVENT_EXT_RECORD_LOAD_DONE
通過handleMessage中讀取資料
case EVENT_EXT_RECORD_LOAD_DONE:
ar = (AsyncResult)(msg.obj);
data = (byte[])(ar.result);
adn = (AdnRecord)(ar.userObj);
if (ar.exception == null) {
Rlog.d(LOG_TAG,"ADN extension EF: 0x"
+ Integer.toHexString(mExtensionEF)
+ ":" + adn.mExtRecord
+ "\n" + IccUtils.bytesToHexString(data));
adn.appendExtRecord(data);
}
else {
// If we can't get the rest of the number from EF_EXT1, rather than
// providing the partial number, we clear the number since it's not
// dialable anyway. Do not throw exception here otherwise the rest
// of the good records will be dropped.
Rlog.e(LOG_TAG, "Failed to read ext record. Clear the number now.");
adn.setNumber("");
}
mPendingExtLoads--;
// result should have been set in
// EVENT_ADN_LOAD_DONE or EVENT_ADN_LOAD_ALL_DONE
break;
第二條線路消息傳遞:AdnRecordCache通過requestLoadAllAdnLike()調用AdnRecordLoader.loadAllFromEF()發消息,EVENT_LOAD_ALL_ADN_LIKE_DONE消息處理:
public void requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
......
new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,
obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
消息處理:
case EVENT_LOAD_ALL_ADN_LIKE_DONE:
/* arg1 is efid, obj.result is ArrayList<AdnRecord>*/
ar = (AsyncResult) msg.obj;
efid = msg.arg1;
ArrayList<Message> waiters;
waiters = mAdnLikeWaiters.get(efid);
mAdnLikeWaiters.delete(efid);
if (ar.exception == null) {
mAdnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result);
}
notifyWaiters(waiters, ar);
break;
一路向上傳遞消息,這裡的ar其實就包含了聯系人資料清單ArrayList
回到IccPhoneBookInterfaceManager.java
case EVENT_LOAD_DONE://查詢完成
ar = (AsyncResult)msg.obj;
synchronized (mLock) {
if (ar.exception == null) {
if(DBG) logd("Load ADN records done");
//傳回adn list
mRecords = (List<AdnRecord>) ar.result;
} else {
if(DBG) logd("Cannot load ADN records");
mRecords = null;
}
//notify()喚醒
notifyPending(ar);
}
break;
整個流程走完了。可以看出名稱叫做IccProvider,其實沒有建立任何資料庫。第一次的查詢是通過發送ril請求讀取sim卡得到資料,後續用緩存傳回資料。
第二部分:Contacts讀取Sim卡聯系人的流程
packages/apps/Contacts/src/com/mediatek/contacts/simcontact/BootCmpReceiver.java
public void onReceive(Context context, Intent intent) {
...
if (action.equals(TelephonyIntents.ACTION_PHB_STATE_CHANGED)) {
processPhoneBookChanged(context, intent);
}
...
}
收到TelephonyIntents.ACTION_PHB_STATE_CHANGED廣播後,該廣播表示卡聯系人可用不可用,調用processPhoneBookChanged
private void processPhoneBookChanged(Context context, Intent intent) {
...
if (phbReady && subId > 0) {
startSimService(context, subId, SIMServiceUtils.SERVICE_WORK_IMPORT);
} else if (subId > 0 && !phbReady) {
startSimService(context, subId, SIMServiceUtils.SERVICE_WORK_REMOVE);
}
}
廣播處理有兩個分支,一個是删除卡聯系人,一個是導入卡聯系人
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMProcessorService.java
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "[onCreate]...");
mProcessorManager = new SIMProcessorManager(this, mListener);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
processIntent(intent);
}
private void processIntent(Intent intent) {
...
mProcessorManager.handleProcessor(getApplicationContext(), subId, workType, intent);
}
一路調用到handleProcessor,注意mProcessorManager初始化的時候傳入了接口的實作,這樣mProcessorManager就可以通知SIMProcessorService工作開始或者完畢
private SIMProcessorManager.ProcessorManagerListener mListener
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMProcessorManager.java
public void handleProcessor(Context context, int subId, int workType, Intent intent) {
Log.i(TAG, "[handleProcessor] subId=" + subId + ",time=" + System.currentTimeMillis());
SIMProcessorBase processor = createProcessor(context, subId, workType, intent);
if (processor != null && mListener != null) {
Log.d(TAG, "[handleProcessor]Add processor [subId=" + subId + "] to threadPool.");
mListener.addProcessor(/* 1000 + slotId * 300 */0, processor);
}
}
private SIMProcessorBase createProcessor(Context context, int subId, int workType,
Intent intent, ProcessorCompleteListener listener) {
...
if (workType == SIMServiceUtils.SERVICE_WORK_IMPORT) {
processor = new SIMImportProcessor(context, subId, intent, listener);
...
}
createProcessor生成了processor,然後調用mListener的方法,這個就是SIMProcessorService中實作的,addProcessor開始導入聯系人的工作:
@Override
public void addProcessor(long scheduleTime, ProcessorBase processor) {
if (processor != null) {
try {
mExecutorService.execute(processor);
} catch (RejectedExecutionException e) {
Log.e(TAG, "[addProcessor] RejectedExecutionException: " + e.toString());
}
}
}
processor是繼承自ProcessorBase。
packages/apps/ContactsCommon/src/com/android/contacts/common/vcard/ProcessorBase.java
public abstract class ProcessorBase implements RunnableFuture {
ProcessorBase實作了RunnableFuture,是以它可以放到線程池區運作。
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMProcessorBase.java
public void run() {
try {
doWork();
} finally {
mDone = true;
if (mListener != null && !mCanceled) {
mListener.onProcessorCompleted(mIntent);
}
}
}
線程池是調用run方法開啟工作的,run函數中調用doWork完成工作,用mListener接口通知SIMProcessorManager工作完畢
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMImportProcessor.java
@Override
public void doWork() {
...
SIMServiceUtils.deleteSimContact(mContext, mSubId);
...
int simType = SimCardUtils.getSimTypeBySubId(mSubId);
final Uri iccUri = SubInfoUtils.getIccProviderUri(mSubId);
Cursor simCursor = querySimContact(mContext, mSubId, simType, iccUri);
Log.i(TAG, "[dowork]simType = " + simType + ",simType =" + simType + ",mSubId = " + mSubId);
importAllSimContacts(mContext, mSubId, simCursor, simType);
if (simCursor != null) {
simCursor.close();
}
}
首先删除所有資料庫中的卡聯系人,然後查詢卡聯系人,擷取卡聯系人資料後導入到ContactsProvider中。
private Cursor querySimContact(Context context, int subId, int simType, Uri iccUri) {
...
cursor = context.getContentResolver().query(iccUri, COLUMN_NAMES, null, null, null);
...
return cursor;
}
通過IccProvider查詢卡聯系人
private void importAllSimContacts(Context context, final Cursor cursor,
final ContentResolver resolver, int subId, int simType, HashSet<Long> insertSimIdSet,
boolean importSdnContacts) {
...
while (cursor.moveToNext()) {
...
i = actuallyImportOneSimContact(context, cursor, resolver, subId, simType,
indexInSim, importSdnContacts, operationList, i, account, isUsim,
accountSubId, countryCode);
...
if (i > MAX_OP_COUNT_IN_ONE_BATCH) {
...
resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
...
}
}
...
}
基本流程是依據cursor利用actuallyImportOneSimContact生成資料庫插入的operationlist,然後在每大于90個operation就批量操作一次,循環上訴流程直到處理完畢。
doWork結束後會回調接口ProcessorCompleteListener,然後關閉線程池和關閉service。