卡联系人IccProvider
16lz
2022-05-30
IccProvider概述
读取卡联系人的provider定义于:
/packages/services/Telephony/AndroidManifest.xml
packages/services/Telephony/src/com/android/phone/IccProvider.java public class IccProvider extends com.android.internal.telephony.IccProvider { public IccProvider() { super(); }}
Telephony目录下的IccProvider其实是空的,实现全部在framework的同名文件中 frameworks/opt/telephony/src/java/com/android/internal/telephony/IccProvider.java
public class IccProvider extends ContentProvider {
IccProvider读取卡联系人流程
static { URL_MATCHER.addURI("icc", "adn", ADN); URL_MATCHER.addURI("icc", "adn/subId/#", ADN_SUB); ... }
static块中加入了adn的uri,adn/subid/#可以指定读取的sim卡 public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) { ... switch (URL_MATCHER.match(url)) { case ADN: return loadFromEf(IccConstants.EF_ADN, mSubscriptionManager.getDefaultSubId()); ... }
query方法,调用loadFromEf private MatrixCursor loadFromEf(int efType, int subId) { List adnRecords = null; try { IIccPhoneBook iccIpb = getIccPhbService(); if (iccIpb != null) { adnRecords = iccIpb.getAdnRecordsInEfForSubscriber(subId, efType); } } catch (RemoteException ex) { log(ex.toString()); } catch (SecurityException ex) { log(ex.toString()); } if (adnRecords != null) { // Load the results final int size = adnRecords.size(); final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, size); if (DBG) { log("adnRecords.size=" + size); } for (int i = 0; i < size; i++) { loadRecord(adnRecords.get(i), cursor, i); } return cursor; } ... }
首先获取AdnRecord列表然后,然后loadRecord依据这个列表生成cursor返回。生成cursor的函数很简单,不做分析了。 private IIccPhoneBook getIccPhbService() { IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( ServiceManager.getService("simphonebook")); return iccIpb; }
读取的服务名称叫做simphonebook,该服务添加的代码在UiccPhoneBookController中: frameworks/opt/telephony/src/java/com/android/internal/telephony/UiccPhoneBookController.java public UiccPhoneBookController(Phone[] phone) { if (ServiceManager.getService("simphonebook") == null) { ServiceManager.addService("simphonebook", this); } mPhone = phone; }
构造函数中添加了服务,UiccPhoneBookController实例是在phone进程启动就初始化的,phone进程又是常驻的,所以phone的相关服务基本等于是永远可用的。 public List getAdnRecordsInEfForSubscriber(int subId, int efid) throws android.os.RemoteException { IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy = getIccPhoneBookInterfaceManagerProxy(subId); if (iccPbkIntMgrProxy != null) { return iccPbkIntMgrProxy.getAdnRecordsInEf(efid); } ... }
然后调用IccPhoneBookInterfaceManagerProxy的getAdnRecordsInEf private IccPhoneBookInterfaceManagerProxy getIccPhoneBookInterfaceManagerProxy(int subId) { ... try { return ((PhoneProxy)mPhone[(int)phoneId]).getIccPhoneBookInterfaceManagerProxy(); ... }
frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneProxy.java public IccPhoneBookInterfaceManagerProxy getIccPhoneBookInterfaceManagerProxy() { return mIccPhoneBookInterfaceManagerProxy; }
public PhoneProxy(PhoneBase phone) { ... mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy( phone.getIccPhoneBookInterfaceManager()); ... }
IccPhoneBookInterfaceManagerProxy是在PhoneProxy构造函数中初始化的。 frameworks/opt/telephony/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java public List getAdnRecordsInEf(int efid) { return mIccPhoneBookInterfaceManager.getAdnRecordsInEf(efid); }
这里的mIccPhoneBookInterfaceManager就是PhoneProxy构造函数传递进去的phone.getIccPhoneBookInterfaceManager() 该对象实际是在Phone的构造函数中初始化的,拿GsmPhone举例 frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
SimPhoneBookInterfaceManager的基类是IccPhoneBookInterfaceManager /home/lgy/code/mtk6797/frameworks/opt/telephony/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java public synchronized List getAdnRecordsInEf(int efid) { ... synchronized (mLock) { checkThread(); AtomicBoolean status = new AtomicBoolean(false); Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status); if (mAdnCache != null) { mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response); waitForResult(status); } ... } return mRecords; }
frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java public void requestLoadAllAdnLike (int efid, int extensionEf, Message response) { ArrayList waiters; ArrayList result; if (efid == EF_PBR) { result = mUsimPhoneBookManager.loadEfFilesFromUsim(); } else { result = getRecordsIfLoaded(efid); //该方法实际是从缓存读取数据 } logd("requestLoadAllAdnLike result = null ?" + (result == null)); // Have we already loaded this efid? if (result != null) { //如果缓存已有数据,return 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 waiters.add(response); return; } waiters = new ArrayList(); waiters.add(response); mAdnLikeWaiters.put(efid, waiters); ... 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 public void loadAllFromEF(int ef, int extensionEF, Message response) { ... mFh.mCi.queryPhbStorageInfo( type, obtainMessage(EVENT_PHB_QUERY_STAUTS)); ... }
调用ril的queryPhbStorageInfo向modem发送请求,读取结果会在handleMessage中处理 case EVENT_PHB_QUERY_STAUTS: /* * response.obj.result[0] is number of current used entries * response.obj.result[1] is number of total entries in the * storage */ ar = (AsyncResult) (msg.obj); int[] info = (int[]) (ar.result); if (ar.exception != null) { throw new RuntimeException("PHB Query Info Error", ar.exception); } type = getPhbStorageType(mEf); readInfo = new int[3]; readInfo[0] = 1; // current_index; readInfo[1] = info[0]; // # of remaining entries readInfo[2] = info[1]; // # of total entries mAdns = new ArrayList(readInfo[2]); for (int i = 0; i < readInfo[2]; i++) { // fillin empty entries to mAdns adn = new AdnRecord(mEf, i + 1, "", ""); mAdns.add(i, adn); } readEntryFromModem(type, readInfo); mPendingExtLoads = 1; break;
获取到了卡联系人总数目,先用空值初始化mAdn列表,然后调用readEntryFromModem正真的读取数据 private void readEntryFromModem(int type, int[] readInfo) { ... mFh.mCi.ReadPhbEntry(type, readInfo[0], eIndex, obtainMessage(EVENT_PHB_LOAD_ALL_DONE, readInfo)); }
消息处理: case EVENT_PHB_LOAD_ALL_DONE: ar = (AsyncResult) (msg.obj); readInfo = (int[]) (ar.userObj); entries = (PhbEntry[]) (ar.result); ... for (int i = 0; i < entries.length; i++) { adn = getAdnRecordFromPhbEntry(entries[i]); if (adn != null) { mAdns.set(adn.mRecordNumber - 1, adn); readInfo[1]--; Rlog.d(LOG_TAG, "Read entries: " + adn); } } ...
for循环中向mAdns添加数据。AdnRecordLoader会向AdnRecordCache发消息,EVENT_LOAD_ALL_ADN_LIKE_DONE消息处理: case EVENT_LOAD_ALL_ADN_LIKE_DONE: ... if (ar.exception == null) { mAdnLikeFiles.put(efid, (ArrayList) ar.result); } else { Rlog.d(LOG_TAG, "EVENT_LOAD_ALL_ADN_LIKE_DONE exception", ar.exception); } notifyWaiters(waiters, ar); ... break;
一路向上传递消息,这里的ar其实就包含了联系人数据列表ArrayList 回到IccPhoneBookInterfaceManager.java
case EVENT_LOAD_DONE: ar = (AsyncResult)msg.obj; ... mRecords = (List) ar.result; ...
整个流程走完了。可以看出名称叫做IccProvider,其实没有建立任何数据库。第一次的查询是通过发送ril请求读取sim卡得到数据,后续用缓存返回数据。 Contacts读取Sim卡联系人的流程
分析的以mtk的代码为例,高通的代码和mtk差异很大,且不在Contacts目录下。是在vendor目录下,单独作为一个app。 /home/lgy/code/mtk6797/packages/apps/Contacts/AndroidManifest.xml
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 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,这个流程简单不再做分析。 更多相关文章
- Android(安卓)Java方法链起来!链式调用方法、仿Builder 等。
- Android(安卓)HttpGet和HttpPost设置超时
- android 9.0 在rk3326平台上hidl的使用
- Android(安卓)调用系统相机拍照的返回结果
- android 分享功能
- android学习小结3
- Android(安卓)使用ContentObserver监听短信的变化,并发送信息给特
- Android(安卓)实现手机震动功能
- Android(安卓)获取通讯录联系人