<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                其實,Rild沒什么難度,相信見識過Audio和Surface系統的讀者都會有同感。但Java層的Phone應用及相關的Telephony模塊卻相當復雜,這里不去討論Phone的實現,而是通過實例來分析一個電話是如何撥打出去的。這個例子和Rild有關的東西比較簡單,但在分析代碼的路途上,讀者可以領略到Java層Phone代碼的復雜。 1. 創建Phone Android支持GSM和CDMA兩種Phone,到底創建哪種Phone呢?來看PhoneApp.java是怎么做的: **PhoneApp.java** ~~~ public void onCreate() { ...... if(phone == null) { //創建一個Phone,這里使用了設計模式中的Factory(工廠)模式 PhoneFactory.makeDefaultPhones(this); phone = PhoneFactory.getDefaultPhone(); ...... } ~~~ 工廠模式的好處在于,將Phone(例如代碼中的GSMPhone或CDMAPhone)創建的具體復雜過程屏蔽起來了,因為用戶只關心工廠的產出物Phone,而不關心創建過程。通過工廠模式可降低使用者和創建者代碼之間的耦合性,即使以后增加TDPhone,使用者也不需要修改太多的代碼。 下面來看這個Phone工廠: **PhoneFactory.java** ~~~ public static void makeDefaultPhones(Context context){ makeDefaultPhone(context);//調用makeDefaultPhone函數,直接去看看 } public static void makeDefaultPhone(Contextcontext) { synchronized(Phone.class) { ...... //根據系統設置獲取通信網絡的模式 intnetworkMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_NETWORK_MODE,preferredNetworkMode); intcdmaSubscription = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription); //RIL這個對象就是rild socket的客戶端,AT命令由它發送給Rild sCommandsInterface= new RIL(context, networkMode,cdmaSubscription); int phoneType =getPhoneType(networkMode); if(phoneType == Phone.PHONE_TYPE_GSM) { //先創建GSMPhone,然后創建PhoneProxy,這里使用了設計模式中的Proxy模式 sProxyPhone = new PhoneProxy(newGSMPhone(context, sCommandsInterface,sPhoneNotifier)); }else if (phoneType == Phone.PHONE_TYPE_CDMA) { //創建CDMAPhone sProxyPhone = new PhoneProxy(new CDMAPhone(context, sCommandsInterface,sPhoneNotifier)); } sMadeDefaults = true; } } } ~~~ 假設創建的是GSMPhone,makeDefaultPhones函數將返回PhoneProxy對象,不過這是一個代理Phone,具體工作還是會由GSMPhone完成。 Phone創建完后,就要撥號了。 2. Dial撥號 Phone應用提供了一個PhoneUtils類,最終的撥號是由它完成的: **PhoneUtils.java** ~~~ static int placeCall(Phone phone, String number,Uri contactRef) { int status = CALL_STATUS_DIALED; try { //調用Phone的dial函數,這個Phone的真實類型是PhoneProxy,number就是電話號碼 Connectioncn = phone.dial(number); ...... } ...... } ~~~ 前面說過,PhoneProxy代理的對象是GSMPhone,直接去看它的dial函數: **GSMPhone.java** ~~~ public Connection dial(String dialString) throws CallStateException { returndial(dialString, null);//調用另外一個dial函數 } public Connection dial (String dialString,UUSInfo uusInfo) throws CallStateException { StringnewDialString = PhoneNumberUtils.stripSeparators(dialString); if (mmi== null) { //mCT是GsmCallTrack類型,調用它的dial函數。 returnmCT.dial(newDialString, uusInfo); } elseif (mmi.isTemporaryModeCLIR()) { returnmCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo); } ...... } ~~~ 又調用GSMCallTrack的dial函數,它的代碼如下所示: **GSMCallTrack.java** ~~~ Connection dial (String dialString, intclirMode, UUSInfo uusInfo) throws CallStateException { ...... //構造一個GsmConnection對象 pendingMO = newGsmConnection(phone.getContext(), dialString, this, foregroundCall); hangupPendingMO = false; /* cm對象的真實類型就是我們前面提到的RIL類,它實現了CommandInterface。 下面將調用它的dial函數。 */ cm.dial(pendingMO.address, clirMode, uusInfo, obtainCompleteMessage()); ...... returnpendingMO; } ~~~ 3. Java層RIL類的分析 Phone應用是不是很復雜?從創建Phone開始,頗費周折才來到了Java層的RIL類,RIL將是Rild中rild socket的唯一客戶端。下面來認識一下RIL。 (1)RIL的架構 RIL的構造函數的代碼如下所示。 **RIL.java** ~~~ public RIL(Context context, int networkMode, intcdmaSubscription) { super(context); mCdmaSubscription =cdmaSubscription; mNetworkMode = networkMode; //創建一個HandlerThread,從名字上看它是用來發送消息的 mSenderThread = new HandlerThread("RILSender"); mSenderThread.start(); Looper looper = mSenderThread.getLooper(); mSender = new RILSender(looper); mReceiver = new RILReceiver(); //創建一個RILReceiver線程,從名字上看它是用來接收消息的 mReceiverThread = new Thread(mReceiver, "RILReceiver"); mReceiverThread.start(); ...... } ~~~ 那么和Rild中rild socket通信的socket是在哪創建的呢?答案是在接收線程中,其代碼: **RIL.java** ~~~ class RILReceiver implements Runnable { byte[] buffer; ...... public void run() { int retryCount = 0; try {for (;;) { LocalSocket s = null; LocalSocketAddress l; try { s = new LocalSocket(); l = newLocalSocketAddress(SOCKET_NAME_RIL, LocalSocketAddress.Namespace.RESERVED); //和Rild進行連接 s.connect(l); ...... } mSocket = s; int length = 0; try { InputStream is =mSocket.getInputStream(); for (;;) { Parcel p; //讀數據 length = readRilMessage(is,buffer); //解析數據 p = Parcel.obtain(); p.unmarshall(buffer, 0,length); p.setDataPosition(0); //處理請求,以后再看 processResponse(p); p.recycle(); } } ...... } ~~~ 從上面代碼中可知,RIL封裝了兩個線程: - mSenderThread,用來向Rild發送消息。 - mReceiverThread,用來從Rild中接收消息。 待RIL創建后,dail函數該干什么呢? (2)發送dail請求 dial的處理過程,其代碼如下所示: **RIL.java** ~~~ public void dial(String address, int clirMode,UUSInfo uusInfo, Message result) { //創建一個Java層的RIL請求包 RILRequest rr =RILRequest.obtain(RIL_REQUEST_DIAL, result); rr.mp.writeString(address); rr.mp.writeInt(clirMode); rr.mp.writeInt(0); if(uusInfo == null) { rr.mp.writeInt(0); // UUS information is absent }else { rr.mp.writeInt(1); // UUS information is present rr.mp.writeInt(uusInfo.getType()); rr.mp.writeInt(uusInfo.getDcs()); rr.mp.writeByteArray(uusInfo.getUserData()); } //發送數據 send(rr); } private void send(RILRequest rr) { Messagemsg; //發送EVENT_SEND消息,由mSender這個Handler處理 msg= mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget();//由發送線程處理 } ~~~ 下面看handleMessage函數: **RIL.java** ~~~ public void handleMessage(Message msg) { RILRequest rr = (RILRequest)(msg.obj);//請求消息 RILRequestreq = null; ...... switch (msg.what) { caseEVENT_SEND: booleanalreadySubtracted = false; try{ LocalSocket s; s = mSocket; //這個mSocket就是和Rild通信的socket /* 執行異步請求/處理時,請求方需要將請求包保存起來,待收到完成通知后再從請求隊列 中找到對應的那個請求包并做后續處理。請求包一般會保存請求時的上下文信息。 以酒店的Morning Call服務為例。假設預約了7、8、9點的服務,那么當7點鐘 接到電話時,一看表便知道是7點的那個請求完成了,而不是8點或9點的請求完成了。 這個7便是請求號的標示,而且完成通知必須回傳這個請求號。至于上下文信息,則 保存在請求包中。例如酒店會在電話中通知說7點鐘要開一個會,這個開會的信息是 預約服務的時候由你提供給酒店的。 保存請求包是異步請求/處理或異步I/O中常見的做法,不過這種做法有一個 很明顯的缺點,就是當請求量比較大的時候,會占用很多內存來保存請求包信息。 */ synchronized (mRequestsList) { mRequestsList.add(rr); } byte[] data; data = rr.mp.marshall(); rr.mp.recycle(); rr.mp = null; ...... s.getOutputStream().write(dataLength); s.getOutputStream().write(data); //發送數據 } ...... } ~~~ 至止,應用層已經通過RIL對象將請求數據發送了出去。由于是異步模式,請求數據發送出去后應用層就直接返回了,而且目前還不知道處理結果。那么Rild是如何處理這個請求的呢? 4. Rild處理請求的分析 根據前面對Rild的分析可知,當收到客戶端的數據時會由eventLoop調用對應的任務處理函數進行處理,而這個函數就是processCommandsCallback。看它的代碼: (1)Rild接收請求 Rild接收請求的代碼如下所示: **Ril.cpp** ~~~ static void processCommandsCallback(int fd,short flags, void *param) { RecordStream *p_rs; void*p_record; size_trecordlen; intret; //RecordStream為processCommandsCallback的參數,里面維護了一個接收緩沖區并 //有對應的緩沖讀寫位置控制 p_rs =(RecordStream *)param; for(;;) { /* 下面這個函數將從socket中read數據到緩沖區,并從緩沖區中解析命令。 注意,該緩沖區可能累積了多條命令,也就是說,客戶端可能發送了多個命令,而 Rild通過一次read就全部接收到了。這個特性是由TCP的流屬性決定的。 所以這里有一個for循環來接收和解析命令。 */ ret = record_stream_get_next(p_rs, &p_record, &recordlen); if(ret == 0 && p_record == NULL) { /* end-of-stream */ break; }else if (ret < 0) { break; }else if (ret == 0) { //處理一條命令 processCommandBuffer(p_record, recordlen); } } if(ret == 0 || !(errno == EAGAIN || errno == EINTR)) { ......//出錯處理,例如socket read出錯 } } ~~~ 每解析出一條命令,就調用processCommandBuffer函數進行處理,看這個函數: **Ril.cpp** ~~~ static int processCommandBuffer(void *buffer,size_t buflen) { Parcelp; status_t status; int32_t request; int32_t token; RequestInfo *pRI; intret; p.setData((uint8_t *) buffer, buflen); status= p.readInt32(&request); status= p.readInt32 (&token); ...... //s_commands定義了Rild支持的所有命令及其對應的處理函數 if(request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) { ...... return 0; } //Rild內部處理也是采用的異步模式,所以它也會保存請求,又分配一次內存。 pRI =(RequestInfo *)calloc(1, sizeof(RequestInfo)); pRI->token= token; //s_commands是什么? pRI->pCI = &(s_commands[request]); //請求信息保存在一個單向鏈表中。 ret =pthread_mutex_lock(&s_pendingRequestsMutex); pRI->p_next = s_pendingRequests;//p_next指向鏈表的后繼結點 s_pendingRequests = pRI; ret =pthread_mutex_unlock(&s_pendingRequestsMutex); //調用對應的處理函數 pRI->pCI->dispatchFunction(p, pRI); return0; } ~~~ 上面的代碼中,出現了一個s_commands數組,它保存了一些CommandInfo結構,這個結構封裝了Rild對AT指令的處理函數。另外Rild還定義了一個s_unsolResponses數組,它封裝了unsolicited Response對應的一些處理函數。這兩個數組,如下所示: **Ril.cpp** ~~~ typedef struct {//先看看CommandInfo的定義 intrequestNumber; //請求號,一個請求對應一個請求號 //請求處理函數 void(*dispatchFunction) (Parcel &p, struct RequestInfo *pRI); //結果處理函數 int(*responseFunction) (Parcel &p, void *response, size_tresponselen); } CommandInfo; //下面是s_commands的定義 static CommandInfo s_commands[] = { #include "ril_commands.h" }; //下面是s_unsolResponses的定義 static UnsolResponseInfo s_unsolResponses[] = { #include "ril_unsol_commands.h" //這個頭文件讀者可以自己去看看 }; ~~~ 再來看ril_commands.h的定義: **ril_commands.h** ~~~ {0, NULL, NULL}, //除了第一條外,一共定義了103條CommandInfo {RIL_REQUEST_GET_SIM_STATUS, dispatchVoid,responseSimStatus}, ...... {RIL_REQUEST_DIAL, dispatchDial, responseVoid},//打電話的處理 ...... {RIL_REQUEST_SEND_SMS, dispatchStrings,responseSMS}, //發短信的處理 ...... ~~~ 根據上面的內容可知,在Rild中打電話的處理函數是dispatchDial,它的結果處理函數是responseVoid。 (2)Rild處理請求 Rild處理請求的代碼如下所示: **Ril.c** ~~~ static void dispatchDial (Parcel &p,RequestInfo *pRI) { RIL_Dial dial; //創建一個RIL_Dial對象,它存儲打電話時所需要的一些參數。 RIL_UUS_Info uusInfo; int32_t sizeOfDial; int32_tt; int32_t uusPresent; status_t status; memset(&dial, 0, sizeof(dial)); dial.address = strdupReadString(p); status= p.readInt32(&t); dial.clir = (int)t; ...... //中間過程我們略去 //調用RIL_RadioFunctions的onRequest函數,也就是向RefRil庫發送一個請求。 s_callbacks.onRequest(pRI->pCI->requestNumber, &dial,sizeOfDial, pRI); ...... return; } ~~~ 下面去RefRil庫,看這個onRequest的處理: **Reference_Ril.c** ~~~ static void onRequest (int request, void *data,size_t datalen, RIL_Token t) { ATResponse *p_response; interr; ...... switch(request) { ...... case RIL_REQUEST_DIAL: //打電話處理 requestDial(data, datalen, t); break; ...... caseRIL_REQUEST_SEND_SMS: //發短信處理 requestSendSMS(data, datalen, t); break; default: RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); break; } } //我們看看requestDial函數 static void requestDial(void *data, size_tdatalen, RIL_Token t) { RIL_Dial*p_dial; char*cmd; constchar *clir; intret; p_dial= (RIL_Dial *)data; ...... //at_send_command將往串口設備發送這條AT指令 ret =at_send_command(cmd, NULL); free(cmd); /* 對于dail請求,把數據發送給串口就算完成了,所以dial發送完數據后直接調用 RIL_onRequestComplete函數來通知請求處理的結果。而有一些請求需要先由 AT模塊的readLoop線程從串口中讀取BP的處理結果后再行通知。 */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); } ~~~ 看RIL_onRequestComplete函數,這個函數的實現由RIL_Env提供。 **Ril.c** ~~~ extern "C" void RIL_onRequestComplete(RIL_Token t, RIL_Errno e,void *response, size_t responselen) { RequestInfo *pRI; intret; size_terrorOffset; pRI= (RequestInfo *)t; //由于已經收到了請求的處理結果,這表明該請求已經完成,所以需要從請求隊列中去掉這個請求。 if(!checkAndDequeueRequestInfo(pRI)) { } ...... if(pRI->cancelled == 0) { Parcel p; p.writeInt32 (RESPONSE_SOLICITED); p.writeInt32 (pRI->token); errorOffset = p.dataPosition(); p.writeInt32 (e); if(response != NULL) { //dial請求的responseFunction函數是responseVoid,讀者可以看這個函數 ret =pRI->pCI->responseFunction(p, response, responselen); if (ret != 0) { p.setDataPosition(errorOffset); p.writeInt32 (ret); } } ...... sendResponse(p);//將結果發送給Java的RIL } done: free(pRI); } ~~~ Rild內部也采用了異步請求/處理的結構,這樣做有它的道理,因為有一些請求執行的時間較長,例如在信號不好的地方搜索網絡信號往往會花費較長的時間。采用異步的方式,能避免工作線程阻塞在具體的請求函數中,從而騰出手來做一些別的工作。 (3)Java RIL處理完成的通知 Rild將dial請求的結果,通過socket發送給Java中的RIL對象。前面說過,RIL中有一個接收線程,它收到數據后會調用processResponse函數進行處理,看這個函數: **RIL.java** ~~~ private void processResponse (Parcel p) { int type; type = p.readInt(); if(type == RESPONSE_UNSOLICITED) { processUnsolicited (p); }else if (type == RESPONSE_SOLICITED) { processSolicited (p); //dial是應答式的,所以走這個分支 } releaseWakeLockIfDone(); } private void processSolicited (Parcel p) { int serial, error; boolean found = false; serial= p.readInt(); error= p.readInt(); RILRequestrr; //根據完成通知中的請求包編號從請求隊列中去掉對應的請求,以釋放內存 rr= findAndRemoveRequestFromList(serial); Objectret = null; if(error == 0 || p.dataAvail() > 0) { try { switch (rr.mRequest) { ...... //調用responseVoid函數處理結果 caseRIL_REQUEST_DIAL: ret = responseVoid(p);break; ...... if (rr.mResult != null) { /* RILReceiver線程將處理結果投遞到一個Handler中,這個Handler屬于 另外一個線程,也就是處理結果最終將交給另外一個線程做后續處理,例如切換界面顯示等工作, 具體內容就不再詳述了。為什么要投遞到別的線程進行處理呢?因為RILReceiver 負責從Rild中接收數據,而這個工作是比較關鍵的,所以這個線程除了接收數據外,最好 不要再做其他的工作了。 */ AsyncResult.forMessage(rr.mResult, ret,null); rr.mResult.sendToTarget(); } rr.release(); } ~~~ 實例分析就到此為止。相信讀者已經掌握了Rild的精髓。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看