点击打开链接

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进行通信相关推荐

  1. Android中基于Socket的网络通信

    1. Socket介绍 2. ServerSocket的建立与使用 3. 使用ServerSocket建立聊天服务器-1 4. 使用ServerSocket建立聊天服务器-2 5. 在Android中 ...

  2. 多个android手机客户端通信,android中利用Socket实现手机客户端与PC端进行通信

    服务器端: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; impo ...

  3. Android中关于Socket通信数据大小,内存缓冲区和数据可靠性的一点调查

    关于TCP和UDP Socket通信的区别: 应用场景: UDP传输协议效率高,但不可靠: TCP传输效率低,但可靠. 传输数据大小: UDP传输数据限定在64K以下: TCP传输数据无大小限制,可进 ...

  4. Android学习之Socket多个客户端即时通信聊天

    聊天室的原理是这样的,一个客户端与服务器建立通讯成功,即客户端socket连接到服务器的ServerSocket之后,服务器端程序将对应的socket加入到容器中,为每一个socket创建一条线程,服 ...

  5. 问题:解决Android中socket自动断开连接

    在strain sensor APP开发过程当中,需要客户端不断地读取从蓝牙模块发送过来的数据. 遇到的问题: 连接上蓝牙模块后,能够正常读数,蓝牙数据发送速率为0.03s每个.大概在发送了342个数 ...

  6. Android中的IPC方式

    Android中的IPC方式 在上一篇,我们探索了IPC的基本知识,序列化,和Binder的基本实现原理 本篇我们来看看Android中的一些具体的跨进程通信的方式 使用Bundle 我们知道,四大组 ...

  7. 【Framework】透视Android中的Handler

    准备对基于Android应用开发Framework层的内容进行学习回顾,学习一个新技术前我们一般都会灵魂三问:What-Why-How(是什么.为什么.怎么用).源码的学习一定要亲自去看,用IDE或者 ...

  8. Android中socket通信简单实现

    Android中socket通信简单实现 socket通信需要有一个服务器和客户端,可以把同一个APP作为服务器跟客户端,也可以分开成两个APP. 先上个图: 这里以一个APP作为服务器跟客户端为示例 ...

  9. android socket 发送byte_如何正确地创建和销毁网络通讯程序中的Socket类的对象实例...

    软件项目实训及课程设计指导--如何正确地创建和销毁软件应用系统中网络通讯中的Socket类的对象实例 1.基于TCP/IP协议的Socket通信相关的基础知识 (1)TCP/IP(Transmissi ...

最新文章

  1. 转:读AD里特殊的属性in C#
  2. 【转】GeoServer地图开发解决方案(四):发布Web地图服务(WMS)篇
  3. parted如何将磁盘所有空间格式化_linux下大于2T的硬盘格式化问题
  4. http1.0 和 http1.1 主要区别
  5. android_Text
  6. OpenStack nova-network 支持多vlan技术实现片段代码
  7. quartz2d 实现太极图
  8. 百度大脑公开课:快速定制、部署高精度深度学习模型!
  9. 美国红帽软件公司是做什么的
  10. qmake构建项目详细讲解
  11. crawler4j源码学习(1):搜狐新闻网新闻标题采集爬虫
  12. 地图标识符号大全_资源小结:中国分省地图大全(10.23版)
  13. SPSS实现游程检验
  14. mouseover mouseout和mouseenter mouseleave的区别
  15. 富士通Fujitsu DPK320 打印机驱动
  16. ios push上移64_iOS上的C64 Basic
  17. 【数学建模】随机抽样的三种方法(简单随机抽样、分层抽样、系统抽样),自定义封装函数直接调用
  18. 前端三大框架React、Vue、Angular简述
  19. CSS圆角边框、盒子阴影、文字阴影(01-07课)
  20. python写一个飞花令程序

热门文章

  1. 012_日期内建函数
  2. eclipse 达梦 连接_达梦Hibernate Spring集成开发示例
  3. CentOS 7系统启动后怎么从命令行模式切换到图形界面模式
  4. linux命令上常用命令
  5. C++ 引用通过代码例子理解
  6. 云服务器 架设传奇_传奇手游-战神引擎架设教程
  7. 实现位数超过32bit的整数的加减乘除运算_Excel的加减乘除已经不再是你想象的加减乘除...
  8. python 并行计算 opencv_opencv-python计算影像
  9. Spring 详解(四):Spring MVC
  10. 如何听节拍器_我是如何开垮一家琴行的!