卡联系人IccProvider

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 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<AdnRecord> 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<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 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); //该方法实际是从缓存读取数据
        }
        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<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 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<AdnRecord>(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<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
    @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<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
    @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,这个流程简单不再做分析。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值