通過Socket直接與RILD進行通信


1 RIL_J與RIL_C通信

  上層通常要和RILD通信,是通過Socket,在RIL_JAVA層實現;

沿着這樣代碼流程進行Framework——native:

  Phone——RIL_JAVA——>RIL_CPP

 

那么可不可以直接和RILD(RIL_CPP)進行通信呢?

  肯定是可以的,因為通信使用的rild socket,只要通過這個socket就可以和RILD進行通信 ;

但實際中可靠的使用是不可行的,因為RILD在創建的時候,

 設計初始化已經決定了RILD同時所支持的客戶端的數量:

  單卡僅支持一個客戶端;

  雙卡實現方式代碼提供了兩種方式:

         1)雙卡兩個RIL客戶端對應一個RILD服務端,以標簽SUB0和SUB1來區分,RILD中數據流程也對應兩個實體;

         2)一個RIL客戶端對應一個RILD服務端,也就是雙卡的話,就會有個多個RILD進程

        

因Phone進程的特殊性,常駐進程開機啟動會和RILD建立連接,作為RILD客戶端.

  所以如果你通過socket與RILD建立連接,就會將原來的Phone與RILD的連接斷開掉;

  這樣就可能會造成沖突,產生異常,除非你將自帶的Phone刪除掉;

  通常第三方的撥號軟件也都是在Phone的基礎上實現的。

 

因為所有的上層代碼都是通過Framework,再傳遞帶C/C++層進行處理;

  之前有一些做法是,從底層將需求發到上層,在通過上層正常的流程去調用,再傳遞到底層,

  這本身就不是很合理的,但是卻不得不這樣做;

 

假如現在有這樣一個需求,在不啟動上層的情況下進行手機的功能測試,或者直接和RILD進行底層通信

比如網絡通信功能,怎么做呢?

  還得在底層C層直接通過socket與RILD層建立連接,進行通信;

下面就看看這個實現過程:

2 創建socket並連接

sendAtFd createToRildFd(void)
{ sendAtFd fd
= -1; //創建socket並連接 fd = socket_local_client(SOCKET_NAME_RILD, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if (fd < 0) { perror ("opening radio socket"); exit(-1); } //保存和RILD通信的FD s_send_at_fd = fd; return fd; }

 

這里創建套接字是一個UNIX套接字,參數與網絡套接字不同,

結構體用sockaddr_un,域參數應該是PF_LOCAL,通訊類型應該是SOCK_STREAM或SOCK_DGRAM

 

UNIX本地套接字可以參考下面文檔:

   http://zerocool60.blog.163.com/blog/static/35270508200772955536291/

 

3 向RILD發起連接

sendAtErrnoType setupToRild(void) { sendAtErrnoType result = E_SUCCESS; char recvBuffer[RECV_BUFFER_LEN] = {0}; Parcel recvP; sendAtFd fd = s_send_at_fd; //向RILD發起連接 
        char* SUB1 = "SUB1"; int res = send(fd,(const void *)SUB1,strlen(SUB1),0); //接收RILD返回,建建立連接
        while(1){ sleep(10); //等待接收服務端數據
            int recvLen = recv(fd,recvBuffer,RECV_BUFFER_HEAD_LEN,0); if(recvLen ==0) continue; //讀取長度
            int messageLength = ((recvBuffer[0] & 0xff) << 24) | ((recvBuffer[1] & 0xff) << 16) | ((recvBuffer[2] & 0xff) << 8) | (recvBuffer[3] & 0xff); ALOGI("sendAt messageLength = %d",messageLength); //讀取內容
            recvLen = recv(fd,recvBuffer,messageLength,0); recvP.setData((uint8_t*)recvBuffer, recvLen); //解析數據
            int type = recvP.readInt32(); int response = recvP.readInt32(); //type: RESPONSE_UNSOLICITED / RESPONSE_SOLICITED
            ALOGI("sendAt type = %d",type); //response ID: 指RESPONSE_UNSOLICITED類型的 //對於RESPONSE_SOLICITED的可能就不是這樣的了serial一類的,實際中解析時要區分對待兩種類型 //數據格式可以根據RIL_JAVA參考
            ALOGI("sendAt response = %d",response); //數據內容
            ALOGI("sendAt recvBuffer = %s",recvBuffer); //已建立連接 退出循環
            break; } return result; }

 

  同樣需要接收RILD傳遞來的消息,也需要按照這種格式進行,可以另起一個線程專門來負責接收RILD發送來的消息。

 

4 發送數據

sendAtErrnoType sendDataToRild(Parcel &p) { unsigned char* sendData = NULL; uint32 sendDataLen = p.dataSize(); unsigned char dataLength[4]; int32 res = -1; sendAtErrnoType error = E_SUCCESS; sendAtFd fd = s_send_at_fd; // parcel length in big endian 轉化數據長度為byte數組
        dataLength[0] = dataLength[1] = 0; dataLength[2] = (unsigned char)((sendDataLen >> 8) & 0xff); dataLength[3] = (unsigned char)((sendDataLen) & 0xff); //獲取待發送數據
        sendData = (unsigned char*)malloc(p.dataSize()); memcpy(sendData, p.data(), sendDataLen); //發送數據長度
        res = send(fd,(const void *)dataLength,4,0); //發送數據內容
        res = send(fd,(const void *)sendData,sendDataLen,0); free(sendData); return error; }

 

  看到RILD接收數據使用Parcel進行相關的解析,

  因此數據發送的格式組織依然使用Parcel進行組織;

        

  從上面可以看到數據讀取和數據發送,都是先從數據長度開始,然后數據內容

  這就是與RILD socket通信的數據格式,具體可以參考RILJ發送數據的格式。

        

  與RILD socket通信的客戶端限制權限只能為“Radio”,才可以與之進行通信。

  這在RIL_CPP的listenCallback中有做限制。

        

  但是這里如果將UID改為:setuid(AID_RADIO);時,

  發現在創建socket時又會出錯,不知如何解決,就納悶了同樣是radio權限為什么,

  這里卻不能打開rild socket。

        

  僅僅是作為測試,於是使用include $(BUILD_EXECUTABLE)編譯出來一個可執行文件

  只能去更改RILD對於權限的要求。

        

  在向rild發送了數據之后如果,程序立即就退出了,也會報相應的錯誤;

  引起服務端報出:ECONNRESET 104錯誤 connection reset by peer對方復位連接;

  所以要在函數中做一些延遲,不能立即結束程序運行。

        

  在Android中使用Parcel類等某些類,需要在namespace android {}下才行。

  namespace 是c++的一個標識符,表示定義一個全局空間。

  android代碼把整個android工程看作一個namespace。

  所以要在同一個空間下才能引用。

 

5 實現代碼片段

int main(int argc, char *argv[]) { send_at_main_init(); send_at_fight_mode(1); //不能立即結束程序,否則造成ECONNRESET 104錯誤 connection reset by peer //sleep的精度是秒,usleep的精度是微妙
        sleep(3); //或者不讓程序退出,進入死循環
        while(0) { // sleep(UINT32_MAX) seems to return immediately on bionic
            sleep(0x00ffffff); } return 0; }

 

下面看幾個具體的數據發送流程 和格式:

6 撥打電話

void send_at_dispatch_dial(send_at_request_params* handle_params) { Parcel p; status_t status; uint32 ril_request_dial = RIL_REQUEST_DIAL; uint32 serial = 0; const char address[20] = "15816891234"; uint32 clirMode = 0; uint32 uusInfoNone = 0; //打包數據
        status = p.writeInt32(ril_request_dial); status = p.writeInt32(serial); //字符串寫入跟讀取方式保持一致,參考RILD中的函數
 writeStringToParcel(p,address); status = p.writeInt32(clirMode); status = p.writeInt32(uusInfoNone); //發送數據
 sendDataToRild(p); }

 

7 設置Radio狀態

void send_at_dispatch_fight_mode(send_at_request_params* handle_params) { Parcel p; status_t status; atRadioEvent* pRadioEvent = (atRadioEvent*)handle_params->data; //打包數據
        status = p.writeInt32(pRadioEvent->ril_request_radio); status = p.writeInt32(pRadioEvent->serial); status = p.writeInt32(pRadioEvent->radio); status = p.writeInt32(pRadioEvent->on); //發送數據
 sendDataToRild(p); }

 

8 編譯一個可執行二進制文件

 

生成到目錄:/system/bin/sendAt

adb push到手機/system/bin目錄下,更改權限即可執行

 

# For sendAt binary # ======================= LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ sendAt.c \ sendAtUtil.cpp \ sendAtEvent.cpp \ sendAtDispatch.cpp LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder \ libcutils \ libril LOCAL_CFLAGS := \ LOCAL_MODULE:= sendAt LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM