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


更多相关文章

  1. Android中ListView异步加载数据
  2. Android下的SQLite数据库的相关操作及AndroidTestCase测试
  3. Android中Data和String数据类型转换
  4. Android room操作数据库
  5. Android 获取通讯录联系人
  6. android调用系统邮件组件(intent匹配的流程)
  7. android中ListView数据刷新时的同步
  8. android 参数 加密,解密 参数提交,数据返回
  9. android 数据库初体验

随机推荐

  1. 解决Laravel使用laravel-excel扩展包(maa
  2. 互联网金融风控中的数据科学
  3. 总结函数的返回值,参数 2. 实例演绎你对
  4. 教你 Shiro 整合 SpringBoot,避开各种坑
  5. 小米深度学习平台架构与实现
  6. PHP:字符串系统函数,ASCII字符集转换,url
  7. 通过QQ浏览器内核看browser性能优化
  8. 创业公司中的Java高效应用
  9. Java 生态圈与微服务
  10. 面向前端开发者的V8性能优化