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

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

前言

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

Android 底层服务往往是常驻内存,时刻运行完成任务。底层服务进程,往往具有更多的权限,可能和驱动通信,可能和 linux 内核通信,可能需要操作系统核心运行文件以及节点等等。所以,底层服务,可以帮你完成更多计算机基本功能。

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

创建守护进程

创建目录编写代码

创建目录

我们在 Android 系统通用守护进程目录下创建我们的守护进程,当然你也可以在其它目录下放置你的守护进程。

/system/core/
  • 1

在上面的目录下,创建守护进程的文件夹 nativeservice,那么,我们的守护进程就存在如下目录,下文中称简称目录代表如下目录。

/system/core/nativeservice/
  • 1

编写代码

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

编写Android.mk

我在代码中尽可能的注释清楚重要语句的作用,读者如果对 Android AOSP 编译不了解的,可以查阅更多 mk 语法的资料学习。

# 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)和循环。

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");
//
// 1、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);
*/ //
// 2、 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); //
// 3、循环的方式
// 这种方式代码最简单,但是耗资源,没有实时性。
// 一个死循环,每隔 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 平台的为例。

1、在 device/mediatek/sepolicy/basic/non_plat/file_contexts 中添加如下代码

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

2、在 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 工程,当然,如果有编译过,只需要增量编译即可,很快就可以编译完成。

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

2、看一下 SeAndroid 的 SContext

3、看一下 FIFO 管道文件

4、prwx 前面的 p 代表是一个管道文件

5、管道文件 SeAndroid 的 tcontext

6、守护进程启动,启动后打开管道,等待管道数据写入。由于守护进程比抓 log 的工具启动还早,因此,开机时前面的 log 无法抓取,如下 log 是手动 kill 掉守护进程打印的 log

7、通过终端给管道写入数据

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

总结

Android 守护进程可以做很多上层无法完成的功能,但是,为了安全,要运用好 SeAndroid,以最小能力的原则去配置安全权限。创建守护进程,要编写对应代码,配置 rc 文件,配置 SeAndroid。

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/myfriend0/article/details/80016739

创建Android守护进程(底层服务)【转】的更多相关文章

  1. Android守护进程

    这几天,一位做Android的朋友和我探讨了一个问题:因为业务需求的原因,在自己的App长时间不使用被kill掉之后,如何让它再重新运行起来. 虽然,我本身很排斥这种做法,有点类似“流氓软件”的行为, ...

  2. linux分享一:进程全攻略--守护进程(服务)

    概括: 进程是程序的运行实例.进程对应一个唯一的进程PID, 统一程序的多个实例可以同时运行,他们的pid互不相同. 进程一般分为交互进程.批处理进程和守护进程(daemons)三类 一:什么是守护进 ...

  3. 为.net Core 3.0 WebApi 创建Linux守护进程

    前言 我们一般可以在Linux服务器上执行 dotnet <app_assembly.dll> 命令来运行我们的.net Core WebApi应用.但是这样运行起来的应用很不稳定,关闭终 ...

  4. Linux环境下,使用PHP创建一个守护进程

    <?php $pid = pcntl_fork(); // fork if ($pid < 0) exit; else if ($pid) // parent exit; else { / ...

  5. Supervisor 为服务创建守护进程

    今天需要再服务上部署一个.net 方面的项目:当时开启服务的命令只能在前台执行:使用nohub CMD &等放在后台开启服务都会宕机:所以搜寻了Supervisor 这个解决办法,为服务创建守 ...

  6. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  7. Linux系统编程之--守护进程的创建和详解【转】

    本文转载自:http://www.cnblogs.com/mickole/p/3188321.html 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终 ...

  8. linux系统编程:守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  9. 守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而 且提供某种服务,不是对整 ...

随机推荐

  1. 基于python的七种经典排序算法(转)

    一.排序的基本概念和分类 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法. 排序的稳定性:经过某种排序后,如果两个 ...

  2. Android Otto框架浅析

    今天要介绍的是一个Android中使用得比較多的android 事件总线 EventBus模式的一个框架Otto. Otto 官网:http://square.github.io/otto/ 一.An ...

  3. X明X源面试题《一》

    本文转载自zhangkang 今天去明源面试,面试题目如下 1 有两张表 A 学生表 ID          Name          age 1           李1             ...

  4. 内网网络摄像机(RTSP/IPC/NVR)如何能在公网进行RTMP/HLS/HTTP-FLV直播

    一.背景需求 传统监控行业里不管是设备端.服务器端亦或是客户端都在一个内网里面.而且现在的大部分监控方案都是这样的格局,小到一个公司范围内的监控,再到一个园区.一个仓库监控.一个农业园林监控.一个养殖 ...

  5. eclipse远程debug Java程序

    使用Eclipse JPDA远程调试Java程序 本文将介绍使用Eclipse JPDA,在Eclipse的开发环境下对远程运行的Java程序进行调试操作. 请按以下步骤进行(本人已经在Eclipse ...

  6. hadoop报错java.io.IOException: Bad connect ack with firstBadLink as 192.168.1.218:50010

    [root@linuxmain hadoop]# bin/hadoop jar hdfs3.jar com.dragon.test.CopyToHDFS Java HotSpot(TM) Client ...

  7. coursera 《现代操作系统》 -- 第五周 同步机制(1)

    临界区块(Critical section)指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源有无法同时被多个线程访问的特性.(不是字面意思的一个区域,是程序片段的集合) ...

  8. 如何避免升级 Linux 实例内核后无法启动

    如何避免升级 Linux 实例内核后无法启动_系统配置_操作运维 Linux_常见问题_云服务器 ECS-阿里云 https://help.aliyun.com/knowledge_detail/59 ...

  9. 2015-02-08——js笔记

    示例1: 关于事件对象 MSIE:window.event,  cancelBubble,  returnValue,  srcElement, button(鼠标按键,1,4,2,左中右) W3C: ...

  10. js内置数据类型

    JS 中分为七种内置类型,七种内置类型又分为两大类型:基本类型和对象(Object). 基本类型有六种: number , string , boolean , null , undefined ,  ...