Android9.0 sim卡读取联系人

转载:
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 recordif (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&lt;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。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值