转载:
https://blog.csdn.net/firedancer0089/article/details/60762199
https://blog.csdn.net/mafei19870124/article/details/73521995
https://segmentfault.com/a/1190000011314014 (主要参考)
第一部分:读取卡联系人流程
读取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。