建立Android守护进程(底层服务)【转】

本文转载自:https://blog.csdn.net/myfriend0/article/details/80016739linux

建立Android守护进程(底层服务)

前言

Android底层服务,即运行在 linux 下的进程,是 Android 系统运行的基础,完成 Android 或者说计算机最基本的功能。好比链接服务(包括 WIFI,BT 等等);好比 Android 的 adb 功能;好比存储监控等等。没有这些底层服务,上层也就没有了对应的功能。android

Android 底层服务每每是常驻内存,时刻运行完成任务。底层服务进程,每每具备更多的权限,可能和驱动通讯,可能和 linux 内核通讯,可能须要操做系统核心运行文件以及节点等等。因此,底层服务,能够帮你完成更多计算机基本功能。安全

本文所使用的 AOSP 是基于 Android 8.1。阅读文本须要对 Android 的架构、编译系统、AOSP工程和 SeAndroid 有基本认识。bash

建立守护进程

建立目录编写代码

建立目录

咱们在 Android 系统通用守护进程目录下建立咱们的守护进程,固然你也能够在其它目录下放置你的守护进程。markdown

/system/core/
  • 1

在上面的目录下,建立守护进程的文件夹 nativeservice,那么,咱们的守护进程就存在以下目录,下文中称简称目录表明以下目录。架构

/system/core/nativeservice/
  • 1

编写代码

在目录中建立主代码文件 native_main.cpp。另外,咱们须要编译,那么就须要 mk 文件,建立一个 Android.mk 文件。这时,目录架构就是以下这个样子dom

这里写图片描述

编写Android.mk

我在代码中尽量的注释清楚重要语句的做用,读者若是对 Android AOSP 编译不了解的,能够查阅更多 mk 语法的资料学习。socket

# Copyright 2013 The Android Open Source Project # 当前路径 LOCAL_PATH := $(call my-dir) #清除历史变量 include $(CLEAR_VARS) ### nativeservice ### #待编译的源码文件 LOCAL_SRC_FILES := \ native_main.cpp \ common_c_includes := \ bionic \ system/core/include/sysutils \ #引用一些函数库 common_shared_libraries := \ libsysutils \ libcutils \ liblog \ libutils \ libbinder \ libbase LOCAL_C_INCLUDES := \ $(common_c_includes) #守护进程的名字 LOCAL_MODULE := nativeservice LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror LOCAL_SHARED_LIBRARIES := \ $(common_shared_libraries) LOCAL_MODULE_TAGS := optional #编译守护进程,也就是可执行文件 #编译后,在/system/bin/ 下,变多了 nativeservice 可执行文件。 include $(BUILD_EXECUTABLE)
  • 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
编写native_main.cpp

在 Linux 中,一个开机启动的服务,执行完后会自动退出,而咱们是守护进程,那么就须要一直运行。让程序一直运行有不少种方法。在 native_main.cpp 中贴出了三种方式,它们分别是 epoll,有名管道(FIFO)和循环。ionic

epoll 的方式是 Android 系统比较常见的方式,系统的电池状态变化、USB 接口状态变化等守护进程即是经过 epoll 的方式,实时鉴定并读取新状态。函数

有名管道,在 IPC 通讯中比较简单、便捷,适合轻量级任务。

循环,这个是最老套的方式。

三种方式在 native_main.cpp 都贴出来了,本文侧重使用有名管道(FIFO)的方式,鉴于篇幅过长,其它方式就一笔带过了,若是读者对 epoll 等较为兴趣的,能够自行查阅更多资料学习。

下面是 native_main.cpp 的代码,请认真看注释哦。

// // Created familyyuan user on 18-4-20. // #include <errno.h> #include <string.h> #include <unistd.h> #include <cutils/log.h> #include <fcntl.h> #include <android-base/logging.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/epoll.h> #include <cutils/uevent.h> #include <sys/ioctl.h> #define MAX_EPOLL_EVENTS 40 //epoll方式的 epoll fd static int epollfd; //FIFO 方式的 fd static int fifo_fd; //epoll方式的 uevent fd static int uevent_fd; #define BUFFER_SIZE PIPE_BUF int main(int argc, char *argv[]) { SLOGD("native_service start"); // // 一、epoll 的方式, // 监听一个 socket,若是 socket 被链接,便激活程序读取数据。 // Android 驱动和用户态程序较多使用这种方式交互。 // /* int eventct = 5; struct epoll_event events[eventct]; struct epoll_event ev; uevent_fd = uevent_open_socket(64*1024, true); //建立 epoll 通道,监听 socket fd epollfd = epoll_create(MAX_EPOLL_EVENTS); if (epollfd == -1) { SLOGD("native_service epoll_create failed"); } else { SLOGD("native_service epoll_create success"); } // fcntl(uevent_fd, F_SETFL, O_NONBLOCK); ev.events = EPOLLIN; ev.data.fd=uevent_fd; //注册 epoll fd if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) { SLOGD("native_service epoll_ctl failed"); } else { SLOGD("native_service epoll_ctl success"); } while(1){ SLOGD("native_service epoll running"); int nevents = 0; // 监听 socket 端口 nevents = epoll_wait(epollfd, events, eventct, 100000); if (nevents == -1 || nevents == 0) { SLOGD("native_service epoll_wait failed"); } else { SLOGD("native_service epoll_wait success"); } epoll_ctl(epollfd, EPOLL_CTL_DEL, uevent_fd, &ev); } close(uevent_fd); */ // // 二、 FIFO 的方式, // 在/mnt/下建立一个名为 nativeservice 的管道, // 监听管道的数据变化,若是有数据写入管道,便读取数据。 // int res; int bytes = 0; char buffer[BUFFER_SIZE + 1]; // 建立 FIFO res = mkfifo("/mnt/nativeservice", 0777); if (res != 0){ SLOGD("native_service create fifo exist or failed"); } else{ SLOGD("native_service create fifo success"); } // 以阻塞的方式打开 FIFO,知道管道有数据写入,激活程序,往下执行 fifo_fd = TEMP_FAILURE_RETRY(open("/mnt/nativeservice",O_RDONLY)); if (fifo_fd < 0) { SLOGD("native_service open failed"); } else { SLOGD("native_service open success"); } if (fifo_fd != -1){ while(1){ //读取管道数据,若是没有数据,阻塞等待数据被写入,激活 res = read(fifo_fd, buffer, BUFFER_SIZE); bytes += res; SLOGD("native_service result=%s", buffer); } } else { SLOGD("native_service open failed"); } //关闭管道资源。 close(fifo_fd); // // 三、循环的方式 // 这种方式代码最简单,可是耗资源,没有实时性。 // 一个死循环,每隔 5 秒运行一次 // /* while(1){ SLOGD("native_service runnig"); sleep(5); SLOGD("native_service wake"); } */ SLOGD("native_service die"); return 0; } 
  • 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
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

推动编译系统

编写好 Android.mk 和 native_main.cpp 后,能够经过单边命令 “mmm system/core/nativeservice” 编译咱们的守护进程了。可是此时用 make 编译整个 AOSP 时,却不会编译咱们的 nativeservice。所以,须要告诉编译系统,编译工程时,同时编译 nativeservice。修改以下

这里写图片描述

在 /build/make/target/product/core.mk 文件添加 nativeservice,固然不限制添加在这个文件,不少厂商的工程,也会增长本身的 PRODUCT_PACKAGES 配置 mk 文件。

配置开机启动

至此,编译整个工程,守护进程也能够被编译了,这个时候,刷到手机是否就能够运行了呢?不会的,咱们还须要让守护进程在手机开机的时候运行起来,且运行中进程死掉的话,也须要从新启动守护进程。方法以下

在 system/core/rootdir/init.rc 文件中添加以下代码

service healthd /system/bin/healthd
    class core
    critical
    group root system wakelock
#咱们的代码开始 service nativeservice /system/bin/nativeservice class main #main类,属于main的服务会开机被运行,且死掉会重启 group system #属于 system 组 #user system #以system用户启动,不设置以root用户启动 seclabel u:r:nativeservice:s0 #SeAndroid SContext,domain是nativeservice restorecon nativeservice #咱们的代码结束 service console /system/bin/sh
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

读者能够查看 AOSP 中 system/core/init/README.md 文件了解 init.rc 的语法和配置方法。对于 class core 等不一样类别的区别,读者能够阅读《Android加密之全盘加密》相关的阐述。

配置SeAndroid

至此,编译整个工程,守护进程也能够被编译了,也配置了开机自启动。这个时候,刷到手机是否就能够运行守护进程了呢?不能够,咱们知道 Android 继用了 SeLinux 安全机制,同时发展出 SeAndroid 机制,全部文件和进程都须要配置 SeAndroid 才能有权限。所以,若是没有给守护进程以及守护进程须要操做的目录和文件赋予权限,都会被 SeAndroid 过滤或禁止。

因为 QCOM 和 Mediatek 的不一样,在相关文件的放置路径会不一样,可是方法都是同样的,不一样的平台,找到对应的路径下的文件就能够了。本文以 MTK 平台的为例。

一、在 device/mediatek/sepolicy/basic/non_plat/file_contexts 中添加以下代码

/system/bin/nativeservice                  u:object_r:nativeservice_exec:s0
  • 1

二、在 device/mediatek/sepolicy/basic/non_plat/ 中添加 nativeservice.te 文件,文件内容以下

#守护进程 domain 为 nativeservice type nativeservice, domain; typeattribute nativeservice coredomain; type nativeservice_exec, exec_type, file_type; init_daemon_domain(nativeservice) #allow nativeservice self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl; #allow nativeservice tmpfs:file { getattr open read write ioctl create }; #容许 nativeservice 在mnt目录读写管道文件 allow nativeservice tmpfs:fifo_file rw_file_perms; #容许 nativeservice 在mnt目录建立管道文件 allow nativeservice tmpfs:fifo_file create_file_perms; #容许 nativeservice 在mnt目录读写 allow nativeservice tmpfs:dir rw_dir_perms; #容许 nativeservice 在mnt目录建立目录 allow nativeservice tmpfs:dir create_dir_perms;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

刷机验证

至此,须要编译整个 AOSP 工程,固然,若是有编译过,只须要增量编译便可,很快就能够编译完成。

一、刷机后在手机的 /system/bin/nativeservie 目录下能看到守护进程;

这里写图片描述

二、看一下 SeAndroid 的 SContext

这里写图片描述

三、看一下 FIFO 管道文件

这里写图片描述

四、prwx 前面的 p 表明是一个管道文件

这里写图片描述

五、管道文件 SeAndroid 的 tcontext

这里写图片描述

六、守护进程启动,启动后打开管道,等待管道数据写入。因为守护进程比抓 log 的工具启动还早,所以,开机时前面的 log 没法抓取,以下 log 是手动 kill 掉守护进程打印的 log

这里写图片描述

七、经过终端给管道写入数据

这里写图片描述

八、守护进程激活,读取数据

这里写图片描述

总结

Android 守护进程能够作不少上层没法完成的功能,可是,为了安全,要运用好 SeAndroid,以最小能力的原则去配置安全权限。建立守护进程,要编写对应代码,配置 rc 文件,配置 SeAndroid。

相关文章
相关标签/搜索