开机后将sim/uim卡上的联系人写入数据库
tyle="margin:20px 0px 0px; font-size:14px; line-height:26px; font-family:Arial; color:rgb(51,51,51)">
1. 類SyncSimContactsReceiver:
這個類是一個廣播接收器(broadcastReceiver),主要看它的onReceive()方法: 在接收到android.intent.action.BOOT_COMPLETED這個intent的時候,會執行startService(),service對應的類是SyncSimContactsService
2. 類SyncSimContactsService:
這個類是一個服務(service),service被啟動后,
2.1 先執行onCreate(),在onCreate()中會創建一個handler(mServiceHandler),
mServiceHandler = new ServiceHandler();
ServiceHandler是一個SyncSimContactsService的內部類,這個類實現了一個handleMessage方法.
創建Handler時默認沒有傳入參數,那么系統就會默認將當前線程的looper綁定到handler上,looper對象中維護著一個消息隊列,handler發送的消息都會存儲在這個消息隊列中,looper不斷的遍歷這個消息隊列,取出消息交給handleMessage()來處理,因為looper屬于當前線程,所以handleMessage()就會在當前線程中執行。
2.2 再執行onStartCommand(),在onStartCommand()中主要執行doServicehandler(),在doServicehandler()中,mServiceHandler會發送兩個MESSAGE_INIT消息,消息中的arg2參數會記錄sim卡的index,表示要對兩個sim卡都行初始化。
2.3 由于mServiceHandler綁定的是當前線程的消息隊列,因此當前線程的消息隊列收到MESSAGE_INIT后會執行handleMessage方法,handleMessage對消息進行解析,消息中有4個主要參數:
1. what: 存儲的值是messageId,在這里就是MESSAGE_INIT。
2?arg1: 存儲的值是startId, 這個參數是service在執行onStartCommand方法時,作為形參傳入的,表示是由誰啟動的service
3. arg2: 存儲的值是phoneId, 表明是哪個sim卡。
4. obj:? 存儲的值是intent, 這個值也是service在執行onStartCommand方法時,作為形參傳入的,表示是哪個intent來啟動的service。
接下來就是具體處理MESSAGE_INIT這個消息了,先要根據phoneId獲取到對應的TelephonyManager的對象,然后通過調用getSimOperator方法來獲取sim_oper_num(MCC+MNC),如果這個值有效,則執行importDualSimAction方法來導入sim卡聯系人的數據了。
在importDualSimAction中會創建一個線程,并啟動這個線程:
DualSimcardImportThread simImport = new DualSimcardImportThread(serviceId,new ContactsAccount(accountName, Account.SIM_ACCOUNT_TYPE, uri), phoneId);
simImport.start();
DualSimcardImportThread是一個內部類, 它的構造函數的形參有3個:
1. serviceId:即前面提到的startId,
2. ContactsAccount:這個對象里面攜帶了訪問sim卡的URI數據,如果是sim1,則對應的URI是"content://icc0/adn",如果是sim2,對應的URI是"content://icc1/adn"
3. phoneId: 前面已經提到過。
這個線程類最主要的當然是實現了run方法,
在run方法中,先執行deleteSimAction方法,刪除本地數據庫"content://com.android.contacts/raw_contacts"中account_name=sim1 or sim2的數據,這些聯系人不是本地聯系人,所以開機后需要刪除后重新加載。
接下來就是訪問sim卡數據庫了“content://icc0/adn”:
simCursor = mResolver.query(mAccount.getContactsAccountUri(),SIM_COLUMN, null, null, null);
在phone.apk的manifest.xml中:
???? <provider android:name="MsmsIccProvider"
????????????????????? android:authorities="icc"
????????????????????? android:multiprocess="true"
????????????????????? android:readPermission="android.permission.READ_CONTACTS"
????????????????????? android:writePermission="android.permission.WRITE_CONTACTS" />
?? <provider android:name="MsmsIccProvider"
????????????????????? android:authorities="icc0"
????????????????????? android:multiprocess="true"
????????????????????? android:readPermission="android.permission.READ_CONTACTS"
????????????????????? android:writePermission="android.permission.WRITE_CONTACTS" />
?? <provider android:name="MsmsIccProvider"
????????????????????? android:authorities="icc1"
????????????????????? android:multiprocess="true"
????????????????????? android:readPermission="android.permission.READ_CONTACTS"
????????????????????? android:writePermission="android.permission.WRITE_CONTACTS" />
因此通過URI的匹配,我們可以訪問到MsmsIccProvider這個類的Query方法
3. 類MsmsIccProvider
MsmsIccProvider的繼續關系是MsmsIccProvider-》IccProvider-》ContentProvider,它是一個contentProvider,
這里先看它實現的Query方法,
在Query方法中,會執行loadFromEf方法,入口參數是根據URI匹配得到的ADN/FDN/SDN,?
ADN: Abbreviated dialing number, 就是常規的用戶號碼,用戶可以存儲/刪除
FDN:Fixed dialer number,固定撥號,固定撥號功能讓您設置話機的使用限制,當您開啟固定撥號功能后,您只可以撥打存儲的固定撥號列表中的號碼。固定號碼表存放在SIM卡中。能否使用固定撥號功能取決于SIM卡類型以及網絡商是否提供此功能。
SDN:Service dialing number,系統撥叫號碼,網絡服務撥號,固化的用戶不能編輯。
從以上的描述,我們可以看到,一般情況下都是訪問ADN。
在loadFromEf中,要先得到一個IIccPhoneBook對象:
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(ServiceManager.getService(PhoneFactory.getServiceName("simphonebook", phoneId)));
這個對象是用AIDL接口來獲取到的,然后調用getAdnRecordsInEf方法:
adnRecords = iccIpb.getAdnRecordsInEf(efType);
這是通過AIDL接口實現的方法調用,所以先要找到Stub的實體類,因為最終是調用到了Stub實體類的getAdnRecordsInEf方法.這個實體類的對象是通過ServiceManager.getService來獲取的,那么找到addService的地方就可以發現它了。
4. 類IccPhoneBookInterfaceManagerProxy
IccPhoneBookInterfaceManagerProxy這是stub的實體類,繼承了IIccPhoneBook.Stub,在它的構造函數中執行了addService方法:
? String serviceName = PhoneFactory.getServiceName("simphonebook", phoneId);
? if(ServiceManager.getService(serviceName) == null) {
????? ServiceManager.addService(serviceName, this);
? }
addService方法傳入參數為當前類的對象,因此,在PhoneFactory.getServiceName("simphonebook", phoneId)是獲得的就是IccPhoneBookInterfaceManagerProxy類的對象。
那么在前面(3)中提到的MsmsIccProvider類中iccIpb.getAdnRecordsInEf方法實際就調用到了IccPhoneBookInterfaceManagerProxy類的getAdnRecordsInEf方法。
在getAdnRecordsInEf方法中,執行:
mIccPhoneBookInterfaceManager.getAdnRecordsInEf(efid);
mIccPhoneBookInterfaceManager是IccPhoneBookInterfaceManagerProxy的一個成員對象,它是何時被賦值的呢,注意在IccPhoneBookInterfaceManagerProxy的構造函數中:
mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager;
iccPhoneBookInterfaceManager是構造函數傳入的形參,這么看來,還要看IccPhoneBookInterfaceManagerProxy這個類是何時被實例化的?
5. 類PhoneProxy
在PhoneProxy的構造函數中,執行:
mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy(phone.getIccPhoneBookInterfaceManager());
mIccPhoneBookInterfaceManagerProxy是PhoneProxy的一個成員,在這里類IccPhoneBookInterfaceManagerProxy被實例化,之前我們提到,我們需要找到在實例化這個類時傳入的形參,在這里我們看到這個形參是
通過phone.getIccPhoneBookInterfaceManager()來會獲取的。這個獲取到的對象就是(4)中的mIccPhoneBookInterfaceManager。
而phone是PhoneProxy的構造函數的形參傳入的一個對象,要找到這個對象的出處,就要看類PhoneProxy是何時被實例化的?
6.PhoneFactory
在類PhoneFactory中有一個makeDefaultPhone方法,里面執行了:
sCommandsInterface[phone_index] = new SprdRIL(context, networkMode, cdmaSubscription, phone_index);
sProxyPhone[phone_index] = new SprdPhoneProxy(new TDPhone(context,sCommandsInterface[phone_index], sPhoneNotifier[phone_index]));
先看類SprdPhoneProxy的繼承關系: SprdPhoneProxy-》PhoneProxy-》Handler
也就是說,在這里,類PhoneProxy被實例化,當然實際上是它的子類SprdPhoneProxy被實例化,構造函數傳入的形參是一個類TDPhone的對象,
類TDPhone的繼承關系: TDPhone->GSMPhone->PhoneBase->Handler
前面(5)中要找的phone.getIccPhoneBookInterfaceManager方法的返回值就是類TDPhone的getIccPhoneBookInterfaceManager方法的返回值,getIccPhoneBookInterfaceManager方法在類TDPhone中沒有實現,而是在它的父類GSMPhone中實現的。
7.GSMPhone
在類GSMPhone中實現了getIccPhoneBookInterfaceManager方法
public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
??????? return mSimPhoneBookIntManager;
}
mSimPhoneBookIntManager是一個成員,它在類GSMPhone的構造函數中被賦值:
public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {
。。。。
mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
。。。。
}
mSimPhoneBookIntManager就是(6)中的類TDPhone的getIccPhoneBookInterfaceManager方法返回值
8. SimPhoneBookInterfaceManager
類SimPhoneBookInterfaceManager的繼承關系:SimPhoneBookInterfaceManager-》IccPhoneBookInterfaceManager
(4)中的mIccPhoneBookInterfaceManager.getAdnRecordsInEf方法實際上就是類SimPhoneBookInterfaceManager的getAdnRecordsInEf方法,
而getAdnRecordsInEf在類SimPhoneBookInterfaceManager中沒有實現,而是在它的父類IccPhoneBookInterfaceManager中實現的,
(3)中的iccIpb.getAdnRecordsInEf方法執行調用的就是IccPhoneBookInterfaceManager.getAdnRecordsInEf方法
9. IccPhoneBookInterfaceManager
類IccPhoneBookInterfaceManager中實現了getAdnRecordsInEf方法
在getAdnRecordsInEf方法中,執行:
adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response);
adnCache是類AdnRecordCache的對象
10. AdnRecordCache
在類AdnRecordCache中實現了requestLoadAllAdnLike方法,
在requestLoadAllAdnLike中,執行:
new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0))
這里實例化一個類AdnRecordLoader的對象,并且調用該對象的loadAllFromEF方法
11. AdnRecordLoader
在類AdnRecordLoader中實現了loadAllFromEF方法,
在loadAllFromEF方法中,執行:
mFh.loadEFLinearFixedAll(ef, obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
mFh是類IccFileHandler的對象,實際上是它的子類TDUSIMFileHandler的對象,繼承關系是:TDUSIMFileHandler-》SIMFileHandler-》IccFileHandler
12. IccFileHandler
在類IccFileHandler中實現了loadEFLinearFixedAll方法,
在loadEFLinearFixedAll方法中,執行:
phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
mCM是類SprdRIL的對象,SprdRIL的繼承關系是:SprdRIL-》RIL
13. RIL
在類RIL中實現了iccIO方法,
在iccIO方法中,執行:
RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_IO, result);
創建一個RILRequest對象,requestId=RIL_REQUEST_SIM_IO
然后把這個對象當作一個obj,放到message中,
msg = mSender.obtainMessage(EVENT_SEND, rr);
messageId=EVENT_SEND
mSender是一個handler對象,它是如何生成的呢,在RIL的構造函數中可以看到:
mSenderThread = new HandlerThread("RILSender");
mSenderThread.start();
Looper looper = mSenderThread.getLooper();
mSender = new RILSender(looper);
mSender在實例化時傳入的looper是發送子線程的looper,因此,mSender發出的消息會發送到這個子線程的消息隊列中,也就是mSenderThread這個線程中,handleMessage也會在這個子線程中執行。
接下來看類RILSender的handleMessage方法,
在handleMessage中,解析出來msg.what=EVENT_SEND,從msg.obj中取出數據,寫入socket
s = mSocket;
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
mSocket是在另外一個子線程,即接收子線程中創建的,
s = new LocalSocket();
l = new LocalSocketAddress(SOCKET_NAME_RIL,LocalSocketAddress.Namespace.RESERVED);
s.connect(l);
mSocket = s;
至此,數據請求已經通過socket發送給rild了,rild會解析這些數據,再組裝成AT命令,通過串口發送給modem,modem會返回AT數據給rild,rild再解析AT數據,然后打包通過socket發揮給RIL.java來接收。
實際上,前面這一句描述有些含糊,從(1)中我們可以看到,從SyncSimContactsReceiver接收到intent開始,一直到(13)整個代碼執行都工作在Contacts.apk(android.process.acore)這個進程中,只不過有的是在主線程中,有的是在子線程中,這個應用進程通過socket與rild這個守護進程通信,將數據傳送到rild這個進程中,rild得到串口返回的數據后,再通過socket發送給應用層進程Contacts.apk(android.process.acore).
這里,我們假設數據已經通過socket返回給應用層進程Conatacts.apk(android.process.acore),那么RIL.java是怎么接收的呢?
在類RIL的構造函數中可以看到:
mReceiver = new RILReceiver();
mReceiverThread = new Thread(mReceiver, "RILReceiver");
mReceiverThread.start();
這里創建了一個接收子線程,這個線程用來接收rild進程通過socket返回來的數據,
我們看一下接收子線程的run方法,
在run方法中,執行:
InputStream is = mSocket.getInputStream();
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, offset, length - offset);
p.setDataPosition(0);
processResponse(p);
從socket接收到的數據進行解析,主要在processResponse方法中執行,
ret =? responseICC_IO(p);
AsyncResult.forMessage(rr.mResult, ret, null);
rr.mResult.sendToTarget();
將數據解析出來后,打包到message中,發送出去,那么發送到哪個消息隊列中了呢?
rr.mResult存儲的message對象是iccIO方法的形參傳入的,而iccIO方法是(12)中的類IccFileHandler的loadEFLinearFixedAll方法調用的,
14. IccFileHandler
在IccFileHandler中實現了loadEFLinearFixedAll方法,
? public void loadEFLinearFixedAll(int fileid, Message onLoaded) {
??????? Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
??????????????????????? new LoadLinearFixedContext(fileid,onLoaded));
??????? phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
??????????????????????? 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
??? }
這里的response就是(13)中的rr.mResult,
IccFileHandler的繼承關系:IccFileHandler-》Handler
這里我們也看一下IccFileHandler的構造函數,
??? protected IccFileHandler(PhoneBase phone) {
??????? super();
??????? this.phone = phone;
??? }
在調用父類構造函數時,沒有傳入參數,意味著這個handler是與主線程的消息隊列綁定的,handleMessage方法就在主線程中執行,
那么(13)中的rr.mResult.sendToTarget()發送到了主線程中,主線程會調用IccFileHandler的handleMessage方法進行處理,
public void handleMessage(Message msg) {
。。。。。。。
?case EVENT_GET_RECORD_SIZE_DONE:
。。。。。。。。
lc.countRecords = size / lc.recordSize;
?phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
???????????????????????? lc.recordNum,
???????????????????????? READ_RECORD_MODE_ABSOLUTE,
???????????????????????? lc.recordSize, null, null,
???????????????????????? obtainMessage(EVENT_READ_RECORD_DONE, lc));
。。。。。。。。。
根據socket返回的數據,計算出聯系人record的totalNumber,然后再一次通過調用類RIL的iccIO方法向socket發送數據請求讀取第一個聯系人record的具體內容,然后socket接收到返回數據后,發送message給消息隊列,繼續由類IccFileHandler的handleMessage方法處理,
case EVENT_READ_RECORD_DONE:
。。。。。。
lc.results.add(result.payload);
lc.recordNum++;
。。。。。。
phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),lc.recordNum,READ_RECORD_MODE_ABSOLUTE,lc.recordSize, null, null,obtainMessage(EVENT_READ_RECORD_DONE,0,pathNum,lc));
。。。。。。
把讀取到record內容保存起來,然后num++,繼續讀取下一條record,依此類推,逐條的來發送請求和接收數據,sim的容量是250,則要發送250個數據請求給rild。
當最后一個數據接收到之后, 就要發送message給上層的消息隊列了,
response = lc.onLoaded;
response.sendToTarget();
這里的lc.onLoaded是在(11)中的mFh.loadEFLinearFixedAll(ef, obtainMessage(EVENT_ADN_LOAD_ALL_DONE))傳入的,因此發送消息的handler是類AdnRecordLoader的對象,消息隊列中接收到消息后
就要調用類AdnRecordLoader的handleMessage方法了
15. AdnRecordLoader
類AdnRecordLoader的繼承關系:AdnRecordLoader-》Handler
在類AdnRecordLoader中實現了handleMessage方法,
case EVENT_ADN_LOAD_ALL_DONE:
。。。
for (int i = 0, s = datas.size(); i < s; i++) {
adn = new AdnRecord(ef, 1 + i, datas.get(i));
adns.add(adn);
}
userResponse.sendToTarget();
....
將數據打包到消息中,發送到消息隊列中,那么userResponse這個消息又是和哪個handler關聯的呢??
public void loadAllFromEF(int ef, int extensionEF, Message response) {
this.ef = ef;
this.extensionEF = extensionEF;
this.userResponse = response;
。。。
mFh.loadEFLinearFixedAll(ef, obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
}
可以看到userResponse是loadAllFromEF方法的形參傳入的,這是由(10)中的
new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0))調用的
這個message(EVENT_LOAD_ALL_ADN_LIKE_DONE)是類AdnRecordCache的對象發出的,那么發出這個消息后,由類AdnRecordCache的handleMessage方法調用的
16. AdnRecordCache
類AdnRecordCache實現了HandleMessage方法,
case EVENT_LOAD_ALL_ADN_LIKE_DONE:
waiters = adnLikeWaiters.get(efid);
adnLikeWaiters.delete(efid);
notifyWaiters(waiters, ar);
在notifyWaiters方法中,
Message waiter = waiters.get(i);
AsyncResult.forMessage(waiter, ar.result, ar.exception);
waiter.sendToTarget();
這里還是發送一個消息到消息隊列中,這個消息是什么呢? waiter消息是在requestLoadAllAdnLike方法中被賦值的,
public void requestLoadAllAdnLike(int efid, int extensionEf,Message response) {
。。。
waiters.add(response);
。。。
}
這個response是形參帶入的,就是(9)中的adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response)調用的,
即類IccPhoneBookInterfaceManager的getAdnRecordsInEf方法調用的,
17.IccPhoneBookInterfaceManager
在類IccPhoneBookInterfaceManager中實現了getAdnRecordsInEf方法,
public synchronized List<AdnRecord> getAdnRecordsInEf(int efid) {
...
Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response);
waitForResult(status);
...
}
這里可以看到message是mBaseHandler發出的,由mBaseHandler的handleMessage方法處理,
case EVENT_LOAD_DONE:
notifyPending(ar);
這里執行解鎖,那么getAdnRecordsInEf方法中的waitForResult就可以返回了,
至此,getAdnRecordsInEf方法才執行完畢,而這是個AIDL接口調用,它是由(3)中的iccIpb.getAdnRecordsInEf(efType)發起的,現在執行到了類MsmsIccProvider的loadFromEf方法中,
18. MsmsIccProvider
在類MsmsIccProvider中,實現了loadFromEf方法,
?private ArrayList<ArrayList> loadFromEf(int efType, int phoneId) {
。。。
adnRecords = iccIpb.getAdnRecordsInEf(efType);
?int N = adnRecords.size();
??? for (int i = 0; i < N ; i++) {
??????????????? loadRecord(adnRecords.get(i), results);
??? }
這個方法執行完畢之后,走回到類MsmsIccProvider的query方法中,將results轉換成cursor對象返回。
query方法執行完畢后,(2)中的mResolver.query(mAccount.getContactsAccountUri(),SIM_COLUMN, null, null, null);也就返回了
19. SyncSimContactsService
類SyncSimContactsService的DualSimcardImportThread這個線程的run方法中,query語句就執行完畢了
至此,從sim卡上就讀取到全部的聯系人數據了,接下來就是將這些數據存儲到本地數據庫中了,當然要做一下標記,account_name=sim1 or sim2。
?
總結
以上是生活随笔為你收集整理的开机后将sim/uim卡上的联系人写入数据库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 声明字符串的三种方式
- 下一篇: mysql-connector-net不