Android中通過Socket直接與RILD進行通訊

來源:互聯網
上載者:User

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:= sendAtLOCAL_MODULE_TAGS := optionalinclude $(BUILD_EXECUTABLE)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.