源码路径:/frameworks/base/telephony/java/com/android/internal/telephony/
IccProvider路径:/frameworks/opt/telephony/src/java/com/android/internal/telephony/IccProvider.java
SIM联系人的标记为EF_ADN。
USIM联系人标记EF_PBR。
图1获取USIM中联系人信息时序图
1.1 获取手机联系人
获取手机联系人很简单,就算是查询android的数据库,用到的是ContentProvider进行跨进程通讯。
名词说明:
PBR:指的是USIM;
USIM:efid用IccContacts.EF_PBR;
SIM: efid用IccContacts.EF_AND。
1.1.1 整体流程总结
1、每次插拔SIM卡都会将联系人数据库中关于SIM卡的联系人删除;
2、SimStateReceiver通过接收RIL上报的关于SIM卡状态变化的广播去启动SimContactsService;
3、SimContactsService异步启动IccProvider,去查询SIM卡联系人;
4、通过IccPhoneBookInterfaceManager一层层调用到RIL,然后层层返回,最后构造MatrixCursor插入数据库。
1.1.2 涉及的类
ProxyController.java中初始化:UiccPhoneBookController。
1)IccProvider:对外暴露的接口,通过它来查询SIM卡中的contacts信息;
2)IccPhoneBookInterfaceManager: PhoneBook操作服务;
3)IccRecoder:存储与卡相关的一些信息;
4)AdnRecord:一条联系人信息读取后被封装的类型;
5)AdnRecordCache:对SIM卡中联系人进行cache;
6)AdnRecoderLoader:实际通过IccFileHandler去获取adn信息的类;
7)IccFileHandler:向RIL发起请求,或者接收反馈;
8)RIL:向底层发起请求,并接收反馈。
IccProvider:
外部读取sim卡联系人是通过IccProvider来调用的。而IccProvider是通过aidl的接口调用IIccPhoneBook.Stub.asInterface来得到IccPhoneBook对象的。
IccProvider-----》IIccPhoneBook.Stub.asInterface 。
UiccPhoneBookController:
UiccPhoneBookController.java实现了IccPhoneBook.Stub定义的接口。在UiccPhoneBookController.java的构造函数中调用了ServiceManager.addService("simphonebook", this),来注册UiccPhoneBookController实现的接口。
图1 UiccPhoneBookController构造函数
1.1.3 函数说明
UiccPhoneBookController.java 接口函数:
1)getAdnRecordsInEf //获取sim卡联系人
getAdnRecordsInEf ---》getAdnRecordsInEfForSubscriber获取SIM卡联系人信息
2)updateAdnRecordsInEfByIndex //更新联系人
updateAdnRecordsInEfByIndex---》updateAdnRecordsInEfByIndexForSubscriber
Iccfilehandler.java:
Iccfilehandler.java读取线性长度的文件信息流:先获取当前分区记录的长度,拿到长度之后再去读取当前记录的具体内容。
1)loadEFLinearFixedAll()---》mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath, 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response); 调用RIL的方法向modem读取SIM卡当前分区的长度。
2)RIlhemodem交互之后,RIL向IccFileHandler反馈信息EVENT_GET_RECORD_SIZE_DONE,去handleMessage处理:
// 得到从modem拿到的原始数据:
ar = (AsyncResult)msg.obj;
lc = (LoadLinearFixedContext) ar.userObj; …….
// 得到当前记录的长度
lc.mRecordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF;
// 加上要读取的记录长度再次读取数据
mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path,lc.mRecordNum, READ_RECORD_MODE_ABSOLUTE,lc.mRecordSize, null, null, mAid, obtainMessage(EVENT_READ_RECORD_DONE, lc));
总结:IccFileHandler第一次调用RIL的方法只是获取记录的长度,第二次调用RIL的方法才是真正的获取数据。
3)RIL_REQUEST_SIM_IO,根据这个标志,可以在Reference-ril.c找到这个分支。
/hardware/ril/reference-ril/reference-ril.c在这里可以看到android源码如何读取联系人的。源码使用了AT+CRSM命令。读取完毕时会收到EVENT_PBR_LOAD_DONE这个消息,在里面会createPbrFile()完成字符串的解析得到联系人信息。
图2 reference-ril.c
4)IccFileHandler第二次处理的消息是EVENT_READ_RECORD_DONE:
循环读取记录
if (lc.mRecordNum > lc.mCountRecords) {
sendResult(response, lc.results, null);
} else {
if (path == null) {
path = getEFPath(lc.mEfid);
}…….
我们可以看到,在读取记录的过程中认识循环读取的过程,直到记录完全读取完毕 (lc.mRecordNum > lc.mCountRecords) 后,才会调用sendResult将记录数据发送给当初的请求者UsimPhoneBookManager.java。
5)AdnRecordCache.java的requestLoadAllAdnLike()函数继续执行,执行loadAllFromEF()。
再然后就是1获取记录长度;
2获取记录数据。
6)就是IccProvider执行loadRecord()。
1.1.4 MTK平台茶树SIM卡所有联系人到手机
/vendor/mediatek/proprietary/packages/apps/Contacts/SimProcessor/src/com/mediatek/simprocessor/SimImportProcessor.java
1. 导入所有联系人到Contacts2.db数据库:SimImportProcessor.importAllSimContacts()---》actuallyImportOneSimContact()
actuallyImportOneSimContact():将查询出来的每条联系人导入Contacts2.db数据库的RawContacts表单。
actuallyImportOneSimContact:
final NamePhoneTypePair namePhoneTypePair = new NamePhoneTypePair(cursor.getString(NAME_COLUMN));
final String name = namePhoneTypePair.name;获取名字
String phoneNumber = cursor.getString(NUMBER_COLUMN);获取电话号码
operationList.add(builder.build());将查询出来的信息封装在operationList的ArrayList中。
resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
最后调用applyBatch 方法插入到Contacts2.db数据库里面。
2.总结:首先利用AsyQueryHandler