Android:native和上层framework基于socket进行通讯

Android:native和上层framework基于socket进行通讯

分类: Android进阶   610人阅读  评论(0)  收藏  举报

在Android中,有几种方式可以和native层进行通讯,根据权限不同可以适当选择沟通的方式:

1.基于JNI在上层java代码中直接调用native code中的C API,这个所有第三方权限都可以,即所谓的NDK编程。

2.基于socket stream进行通讯,在native c层建立一个server task,通过socket连接与上层进行通信。-----需要系统权限

3.基于驱动模块进行通讯,即自定义一个驱动模块,将相关native code写成驱动模块,通过读写设备流进行通信。---需要系统权限

本文以Android源代码来说明第二种通讯方式,android source code中installer模块就是通过socket stream通讯的典型例子,通过分析这个例子可以了解如何设计socket通讯方式。

一、底层server侧native代码(socket通讯就是典型的c/s模型)

源代码文件路径:

android_4.1_src\frameworks\base\cmds\installd

1. installd.h

定义驻留程序server的socketname,在客户端需要通过这个socketname来与server建立连接。

#define SOCKET_PATH "installd"

2.installd.c

定义一个无穷循环,等待客户端的连接,与普通的socket编程并无多大区别:

[cpp]  view plain copy
  1. int main(const int argc, const char *argv[]) {      
  2.     char buf[BUFFER_MAX];  
  3.     struct sockaddr addr;  
  4.     socklen_t alen;  
  5.     int lsocket, s, count;  
  6. //首先获取有效的socketname  
  7.     lsocket = android_get_control_socket(SOCKET_PATH);  
  8.     if (lsocket < 0) {  
  9.         LOGE("Failed to get socket from environment: %s\n", strerror(errno));  
  10.         exit(1);  
  11.     }  
  12.     if (listen(lsocket, 5)) { //开始监听这个socket path  
  13.         LOGE("Listen on socket failed: %s\n", strerror(errno));  
  14.         exit(1);  
  15.     }  
  16.     fcntl(lsocket, F_SETFD, FD_CLOEXEC);  
  17.   
  18.     for (;;) {  
  19.         alen = sizeof(addr);  
  20.         s = accept(lsocket, &addr, &alen);//等待客户端连接上,也就是上层代码请求连接  
  21.         if (s < 0) {  
  22.             LOGE("Accept failed: %s\n", strerror(errno));  
  23.             continue;  
  24.         }  
  25.         fcntl(s, F_SETFD, FD_CLOEXEC);  
  26.   
  27.         LOGI("new connection\n");  
  28.         for (;;) {  
  29.             unsigned short count;  
  30.             if (readx(s, &count, sizeof(count))) { //读取客户端的请求内容  
  31.                 LOGE("failed to read size\n");  
  32.                 break;  
  33.             }  
  34.             if ((count < 1) || (count >= BUFFER_MAX)) {  
  35.                 LOGE("invalid size %d\n", count);  
  36.                 break;  
  37.             }  
  38.             if (readx(s, buf, count)) {  
  39.                 LOGE("failed to read command\n");  
  40.                 break;  
  41.             }  
  42.             buf[count] = 0;  
  43.             if (execute(s, buf)) break;//根据请求内容执行相应的代码  
  44.         }  
  45.         LOGI("closing connection\n");  
  46.         close(s);  
  47.     }  
  48.   
  49.     return 0;  
  50. }  

关于读取客户端的请求命名,然后执行不同的代码和返回数据,请查看installd.c源代码即可。

如果自己需要设计类似的功能,模仿修改即可。

3.Android.mk

由于需要编译成驻留程序,因此需要是直接可执行程序,请参考Android.mk内容,

其中 

include $(BUILD_EXECUTABLE) 表示编译成可执行程序。

4.编译:

  如果是自己定义的模块,可以将源代码放置在 development目录下,创建如 myserver 的目录,编写自己的native server代码.

模仿 上述代码即可。

编译(根据当前环境自己修改):

./mbldenv.sh

mm development/myserver

请根据当前的环境生成system.img

5.生成的可执行文件一般在 out/target/product/xxx/system/xbin/目录下,根据环境不同位置不同。

6.将该可执行文件复制到对应的目录下,如mtk环境下参考如下:

vendor\mediatek\konka73_gb\artifacts\out\target\product\konka73_gb\system\bin

7.在init.rc增加相应的配置,让其开机自动启动:如: \mediatek\config\konka73_gb\init.rc

在zygote启动后加载你的驻留程序:

service zygote /system/bin/app_process -Xzygote/system/bin --zygote --start-system-server

   socket zygote stream 666

   onrestart write /sys/android_power/request_state wake

   onrestart write /sys/power/state on

   onrestart restart media

onrestart restart netd

 #这里yoursocketname必须与server中定义的一致,系统是通过这个找到你的服务的。

#yoursocketdaemon是刚才编译的可执行文件

service yourservicename /system/bin/yoursocketdaemon

socket yoursocketname stream 600 system system

    user root

    group root

    oneshot

如installd的配置:

service installd /system/bin/installd
    class main
    socket installd stream 600 system system

关于init.rc的配置规则,可以参考 /system/cor/init/readme.txt


二、上层framework的代码

还是以installer.java为例说明(位于:android_4.1_src\frameworks\base\services\java\com\android\server\pm下)

上层就比较简单了,直接发起相应的连接请求即可:

客户端调用execute执行相应的命令即可,相关的命令定义与installd.c定义的命令一致,也就是如果需要扩展相应的分别增加相应的命名执行和调用的代码即可。

[cpp]  view plain copy
  1. struct cmdinfo cmds[] = { //in installd.c  
  2.     { "ping",                 0, do_ping },  
  3.     { "install",              3, do_install },  
  4.     { "dexopt",               3, do_dexopt },  
  5.     { "movedex",              2, do_move_dex },  
  6.     { "rmdex",                1, do_rm_dex },  
  7.     { "remove",               2, do_remove },  
  8.     { "rename",               2, do_rename },  
  9.     { "freecache",            1, do_free_cache },  
  10.     { "rmcache",              1, do_rm_cache },  
  11.     { "protect",              2, do_protect },  
  12.     { "getsize",              4, do_get_size },  
  13.     { "rmuserdata",           2, do_rm_user_data },  
  14.     { "movefiles",            0, do_movefiles },  
  15.     { "linklib",              2, do_linklib },  
  16.     { "unlinklib",            1, do_unlinklib },  
  17.     { "mkuserdata",           3, do_mk_user_data },  
  18.     { "rmuser",               1, do_rm_user },  
  19. };  
execute包含了connect和disconnect到服务端的代码:

[java]  view plain copy
  1. private int execute(String cmd) {//in installer.java  
  2.     String res = transaction(cmd);  
  3.     try {  
  4.         return Integer.parseInt(res);  
  5.     } catch (NumberFormatException ex) {  
  6.         return -1;  
  7.     }  
  8. }  

返回信息可以通过reply参数写回给客户端。


请同时阅读android 上述提到的相关源代码以深入了解具体实现。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值