IccProvider概述

读取卡联系人的provider定义于:

/packages/services/Telephony/AndroidManifest.xml

            <provider android:name="IccProvider"android:authorities="icc"android:multiprocess="true"android:exported="true"android:readPermission="android.permission.READ_CONTACTS"android:writePermission="android.permission.WRITE_CONTACTS" />

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<AdnRecord> 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 resultsfinal 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<AdnRecord> getAdnRecordsInEfForSubscriber(int subId, int efid)throws android.os.RemoteException {IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy =getIccPhoneBookInterfaceManagerProxy(subId);if (iccPbkIntMgrProxy != null) {return iccPbkIntMgrProxy.getAdnRecordsInEf(efid);} ...}

然后调用IccPhoneBookInterfaceManagerProxy的getAdnRecordsInEf

  private IccPhoneBookInterfaceManagerProxygetIccPhoneBookInterfaceManagerProxy(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<AdnRecord> 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<AdnRecord> 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 voidrequestLoadAllAdnLike (int efid, int extensionEf, Message response) {ArrayList<Message> waiters;ArrayList<AdnRecord> 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) {   //如果缓存已有数据,returnif (response != null) {AsyncResult.forMessage(response).result = result;response.sendToTarget();}return;}// Have we already *started* loading this efid?waiters = mAdnLikeWaiters.get(efid);if (waiters != null) { //正在读取中,把回调消息加入等待队列中,returnwaiters.add(response);return;}waiters = new ArrayList<Message>();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 voidloadAllFromEF(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 entriesreadInfo[2] = info[1]; // # of total entriesmAdns = new ArrayList<AdnRecord>(readInfo[2]);for (int i = 0; i < readInfo[2]; i++) {// fillin empty entries to mAdnsadn = 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<AdnRecord>) ar.result);} else {Rlog.d(LOG_TAG, "EVENT_LOAD_ALL_ADN_LIKE_DONE exception", ar.exception);}notifyWaiters(waiters, ar);...break;

一路向上传递消息,这里的ar其实就包含了联系人数据列表ArrayList<AdnRecord>

回到IccPhoneBookInterfaceManager.java

                case EVENT_LOAD_DONE:ar = (AsyncResult)msg.obj;...mRecords = (List<AdnRecord>) ar.result;...

整个流程走完了。可以看出名称叫做IccProvider,其实没有建立任何数据库。第一次的查询是通过发送ril请求读取sim卡得到数据,后续用缓存返回数据。

Contacts读取Sim卡联系人的流程

分析的以mtk的代码为例,高通的代码和mtk差异很大,且不在Contacts目录下。是在vendor目录下,单独作为一个app。
/home/lgy/code/mtk6797/packages/apps/Contacts/AndroidManifest.xml
        <service android:name="com.mediatek.contacts.simservice.SIMProcessorService"/><receiver android:name="com.mediatek.contacts.simcontact.BootCmpReceiver"><intent-filter><action android:name="android.intent.action.PHB_STATE_CHANGED" /><action android:name="android.intent.action.BOOT_COMPLETED" /><action android:name="android.intent.action.USER_SWITCHED_FOR_MULTIUSER_APP"/><action android:name="com.android.contacts.REFRESH_SIM_CONTACT"/></intent-filter></receiver>

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
    @Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "[onCreate]...");mProcessorManager = new SIMProcessorManager(this, mListener);}@Overridepublic 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开始导入联系人的工作:

     @Overridepublic 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<Object> {

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

    @Overridepublic 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,这个流程简单不再做分析。

卡联系人IccProvider相关推荐

  1. Android学习之——操作SIM卡联系人

    今天!!!对,就是就是今天,,,,我终于换手机啦啦啦,四儿子拿到手啦...虽然是个二手货,不过人家他爸也不卖了,只能买二手货了,五儿子那凸凸的摄像头和价格又有点难以下咽. 嗯.....新机子到手,折腾 ...

  2. Android SIM卡联系人操作总结

    --- by Ruiming.Lv 在Android中,对SIM中的联系人进行操作,需要通过系统提供的Content Provider进行,该Provider就是Telphony中的IccProvid ...

  3. android获取sim卡手机号码,Android 读取SIM卡联系人

    Android 5.0之前的版本中,系统只支持单卡,可以使用URI -- content://icc/adn 读取到sim卡里的联系人.附:IccProvider(4.4.4_r1) Android ...

  4. android中对sim卡联系人的增删改查以及监听sim卡联系数据的改变

    sim卡联系人的增删改查主要是通过ContentProvider来进行操作的,在android中对sim卡联系人操作的provider是定义在IccProvider.java这个类中的,这个类位于an ...

  5. 查询SIM卡联系人——源码流程简介

    查询SIM卡联系人 查询SIM卡中的联系人使用的方法为 query( ) 方法,与操作数据库中的查询方法极其类似,使用方式与如下类似: getContentResolver().query(" ...

  6. 将G1内的SIM卡联系人导入到GMAIL的联系人中

    将G1内的SIM卡联系人导入到GMAIL的联系人中 具体方法是:进入联系人--按下"MENU"键--导入联系人--按下"MENU"键--"全部导入&q ...

  7. Android删除UIM卡联系人

    Android删除UIM卡联系人 问题描述: Android系统下如果保存过UIM卡联系人后,无法彻底删除.即使通过联系人管理删除了UIM卡联系人,重启手机后UIM卡联系人依然会出现.甚至把UIM卡拔 ...

  8. android sim卡联系人存储格式,Android SIM卡联系人的增删改查操作

    手机在存储联系人时支持存储到手机或者sim卡,本文主要讲述Android的sim卡中联系人是如何操作的. 1.权限 由于操作联系人的信息,所以联系人的读取和写入是必不可少的. 2.URI URI的创建 ...

  9. android sim卡联系人存储格式,Android获取手机通讯录、sim卡联系人及调用拨号界面方法...

    android获取手机通讯录联系人信息 private void getPhoneContacts() { ContentResolver resolver = this.getContentReso ...

最新文章

  1. php mysql商品管理_PHP基础示例:商品信息管理系统v1.1[转]
  2. java 固定listview_listview Button始终放在底部示例
  3. 充实你的素材库!10款免费的 PSD 素材下载
  4. GemBox Spreadsheet Professional 2.9
  5. java 读取Zip文件进行写入
  6. 常用容器管理器易受危险 exploit 攻击
  7. 浅谈对软件工程的认识与理解
  8. 基于matlab的gmsk,基于matlab的gmsk
  9. 安卓原生系统_体验类原生安卓系统PixelExperience流畅得不像话
  10. 已解决:“apktool” W: invalid resource directory name:XXX\res navigation
  11. html5考试总结300字,期中考试总结300字(优秀篇)
  12. sql优化之终极方案
  13. .net mysql sqlhelper_「谢灿asp.net三层架构」5、DAL中公共类-SqlHelper类应该这样写
  14. Pspice——可控硅的控制
  15. python撩妹技能_干货必看 | 手把手教你用Python撩妹
  16. hp服务器的网络显示红叉,惠普笔记本白屏后重启后显示红叉网络信号联不上网的原因?...
  17. Rebranding (字典序替换 思维)
  18. magento怎么修改货币符号,在之前加上国家缩写
  19. Windows内网协议学习Kerberos篇之PAC
  20. 【2023新书】《ChatGPT在做什么…以及它为什么好使》

热门文章

  1. dedecms v5.7 sp2代码执行漏洞复现
  2. 更新adfs的证书_ADFS服务证书更新介绍
  3. WinForm的托盘开发,PowerTalk,在线咨询,源码,原码,类似53kf,live800
  4. 用CSS3实现放大效果
  5. 最常见的常用性能测试工具推荐
  6. (笔记)ubuntu20.04下 yolov5学习与使用
  7. C语言中标识符不能用的符号,【单选题】按照C语言规定的用户标识符命名规则,( )不能出现在标识符中. A. 大写字母 B. 任意标点符号 C. 数字字符 D. 下划线...
  8. 不要在乎一城一池的得失
  9. 【74HC595芯片】核心驱动代码
  10. onethink学习之动态扩展菜单