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

前言

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

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

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

创建守护进程

创建目录编写代码

创建目录

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

/system/core/

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

/system/core/nativeservice/

编写代码

在目录中创建主代码文件 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)

编写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;
}

推进编译系统

编写好 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

读者可以查看 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

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;

刷机验证

至此,需要编译整个 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。

以上这篇创建Android守护进程实例(底层服务)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Android 双进程守护的实现代码

    前言 最近有在项目中用到高德的定位SDK,功能是每隔一定的时间获取一次用户的地理位置,采取的方案是在后台开启一个 Service,监听高德地图的位置变化. 该功能在用户手机屏幕亮时完美实现,但是当屏幕被关闭的时候,位置信息却无法被获取了,经过原因的排查,发现是由于在用户手机息屏后,后台的 Service 被系统清除,所以功能无法起作用,也就是所谓的进程被杀了. 杀进程,一方面是因为手机内存不足,另一方面其实是 Google 从用户的方面考虑,把一些常驻后台的程序通过一定的算法进行管理,将那些过度

  • android studio3.0.1无法启动Gradle守护进程的解决方法

    今天写项目突然出现了无法启动Gradle的bug,如下图 然后就看了log日志: 这个问题是我第一次看见,然后就开始了各种百度,有说需要在Android/.gradle文件夹下面添加gradle.properties文件的,还有是在项目根目录的gradle.properties文件里面添加org.gradle.jvmargs=-Xmx512M的,结果测试了都没有效果,最后还是在另一篇博客中看到了解决办法: 添加环境变量: 变量名:_JAVA_OPTIONS 变量值:-Djava.net.pref

  • Android通过JNI实现守护进程

    开发一个需要常住后台的App其实是一件非常头疼的事情,不仅要应对国内各大厂商的ROM,还需要应对各类的安全管家...虽然不断的研究各式各样的方法,但是效果并不好,比如任务管理器把App干掉,服务就起不来了... 网上搜寻一番后,主要的方法有以下几种方法,但都是治标不治本: 1.提高Service的优先级:这个,也只能说在系统内存不足需要回收资源的时候,优先级较高,不容易被回收,然并卵... 2.提高Service所在进程的优先级:效果不是很明显 3.在onDestroy方法里重启service:

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

    前言 Android底层服务,即运行在 linux 下的进程,是 Android 系统运行的基础,完成 Android 或者说计算机最基本的功能.比如连接服务(包括 WIFI,BT 等等):比如 Android 的 adb 功能:比如存储监控等等.没有这些底层服务,上层也就没有了对应的功能. Android 底层服务往往是常驻内存,时刻运行完成任务.底层服务进程,往往具有更多的权限,可能和驱动通信,可能和 linux 内核通信,可能需要操作系统核心运行文件以及节点等等.所以,底层服务,可以帮你完

  • C语言编写Linux守护进程实例

    守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程是一种很有用的进 程.Linux的大多数服务器就是用守护进程实现的.比如,Internet服务器inetd,Web服务器httpd等.同时,守护进程完成许多系统任 务.比如,作业规划进程crond,打印进程lpd等. 守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同Unix环境下守护进程的编程规则并不一致.这需要读者注意,照搬 某些书上的

  • .net Core 3.0 WebApi 创建Linux守护进程的方法

    前言 我们一般可以在Linux服务器上执行 dotnet <app_assembly.dll> 命令来运行我们的.net Core WebApi应用.但是这样运行起来的应用很不稳定,关闭终端窗口之后,应用也会停止运行.为了让其可以稳定运行,我们需要让它变成系统的守护进程,成为一种服务一直在系统中运行,出现异常时也能重新启动. Linux系统有自己的守护进程管理工具 Systemd .systemd 是内核启动后的第一个用户进程,PID 为1,是所有其它用户进程的父进程.它直接与内核交互,性能出

  • 利用C#编写Linux守护进程实例代码

    前言 Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展到多进程,父子进程文件描述符共享,父子进程通讯.控制等方面,是实现Linux大型服务的基础技术之一. 如果要在Red Hat Enterprise Linux上将.NET Core进程作为后台进程运行,则可以创建自定义systemd单元.今天我将为.NET Core编写两个自定义系统单元的例子

  • shell脚本作为保证PHP脚本不挂掉的守护进程实例分享

    前几天开始跑一份数据名单,名单需要提供用户名.是否有手机号.是否有邮箱,用户名单我轻易的获取到了,但是,用户名单有2000w之多,并且去检测用户是否有手机号.是否有邮箱必须得通过一个对外开放的安全接口一个一个用户去请求,然后分析返回值才能知道.下面是我处理的方案:1.将2000w名单保存到临时数据表2.用PHP程序每次从该表获取500个用户,检测完后生成SQL update原纪录3.为了防止PHP程序突然断掉,用shell脚本每隔1分钟检测,PHP挂掉了则重启我使用shell脚本作为守护进程的原

  • PHP守护进程实例

    php也是可以直接进行守护进程的启动与终止的,相对于shell来说会简单很多,理解更方便,当然了php的守护进程要实现自动重启还是要依赖于shell的crontab日程表,每隔一段时间去执行一次脚本看脚本是否需要重启,如果需要则杀掉进程删除RunFile文件,重新启动并在RunFile文件中写入pid. 复制代码 代码如下: <?php       function start($file){     $path = dirname(__FILE__).'/';     $runfile = $

  • 使用Python编写Linux系统守护进程实例

    守护进程(daemon)是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控.此类程序会被以进程的形式初始化.通常,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下.守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行.–维基百科 守护进程区别于普通用户登陆系统后运行的进程,它是直接由系统初始化,和系统用户没有关系,而用户开启的进程依存与用

  • ASP.NET Core在Linux下为dotnet创建守护进程

    什么是守护进程 在linux或者unix操作系统中,守护进程(Daemon)是一种运行在后台的特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件.由于在linux中,每个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端被称为这些进程的控制终端,当控制终端被关闭的时候,相应的进程都会自动关闭.但是守护进程却能突破这种限制,它脱离于终端并且在后台运行,并且它脱离终端的目的是为了避免进程在运行的过程中的信息在任何终端中显示并且进程也不会

随机推荐