Android中通过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并连接

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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发起连接

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
sendAtErrnoType setupToRild( void )
{
         sendAtErrnoType result = E_SUCCESS;
         char recvBuffer[RECV_BUFFER_LEN] = { 0 };
         Parcel recvP;
         sendAtFd fd = s_send_at_fd;
 
         <strong> //</strong><strong>向RILD发起连接</strong>       
         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 ;
 
             <strong> //读取长度</strong>
             int messageLength = ((recvBuffer[ 0 ] & 0xff ) << 24 )
                 | ((recvBuffer[ 1 ] & 0xff ) << 16 )
                 | ((recvBuffer[ 2 ] & 0xff ) << 8 )
                 | (recvBuffer[ 3 ] & 0xff );
             ALOGI( "sendAt messageLength = %d" ,messageLength);
 
             <strong> //读取内容</strong>
             recvLen = recv(fd,recvBuffer,messageLength, 0 );
             recvP.setData((uint8_t*)recvBuffer,   recvLen);
 
             <strong> //解析数据</strong>
             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);
 
             <strong> //已建立连接 退出循环</strong>
             break ;
         }
 
         return result;
     }
加载中...

  同样需要接收RILD传递来的消息,也需要按照这种格式进行,可以另起一个线程专门来负责接收RILD发送来的消息。

4 发送数据

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
sendAtErrnoType <strong>sendDataToRild</strong>(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;
         
         <strong> // parcel length in big endian 转化数据长度为byte数组</strong>
         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);
 
         <strong> //发送数据长度</strong>
         res = send(fd,( const void *)dataLength, 4 , 0 );
 
         <strong> //发送数据内容</strong>
         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 实现代码片段

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main( int argc, char *argv[])
{   
         send_at_main_init();
         
         send_at_fight_mode( 1 );
         
         <strong> //不能立即结束程序,否则造成ECONNRESET 104错误 connection reset by peer
         </strong> //sleep的精度是秒,usleep的精度是微妙
         sleep( 3 );
         
         //或者不让程序退出,进入死循环
         while ( 0 ) {
             // sleep(UINT32_MAX) seems to return immediately on bionic
             sleep( 0x00ffffff );
             
         }
         return 0 ;
}
加载中...

下面看几个具体的数据发送流程 和格式:

6 拨打电话

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
 
         //发送数据
<strong>        sendDataToRild</strong>(p);
}
加载中...

7 设置Radio状态

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
 
         //发送数据
<strong>        sendDataToRild</strong>(p);
}
加载中...

8 编译一个可执行二进制文件

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

adb push到手机/system/bin目录下,更改权限即可执行

加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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 := \
 
<strong>LOCAL_MODULE</strong>:= sendAt
LOCAL_MODULE_TAGS := optional
 
include $(<strong>BUILD_EXECUTABLE</strong>)

你可能感兴趣的:(Android中通过Socket直接与RILD进行通信)