創(chuàng)建Android守護進程實例(底層服務)
前言
Android底層服務,即運行在 linux 下的進程,是 Android 系統(tǒng)運行的基礎(chǔ),完成 Android 或者說計算機最基本的功能。比如連接服務(包括 WIFI,BT 等等);比如 Android 的 adb 功能;比如存儲監(jiān)控等等。沒有這些底層服務,上層也就沒有了對應的功能。
Android 底層服務往往是常駐內(nèi)存,時刻運行完成任務。底層服務進程,往往具有更多的權(quán)限,可能和驅(qū)動通信,可能和 linux 內(nèi)核通信,可能需要操作系統(tǒng)核心運行文件以及節(jié)點等等。所以,底層服務,可以幫你完成更多計算機基本功能。
本文所使用的 AOSP 是基于 Android 8.1。閱讀文本需要對 Android 的架構(gòu)、編譯系統(tǒng)、AOSP工程和 SeAndroid 有基本認識。
創(chuàng)建守護進程
創(chuàng)建目錄編寫代碼
創(chuàng)建目錄
我們在 Android 系統(tǒng)通用守護進程目錄下創(chuàng)建我們的守護進程,當然你也可以在其它目錄下放置你的守護進程。
/system/core/
在上面的目錄下,創(chuàng)建守護進程的文件夾 nativeservice,那么,我們的守護進程就存在如下目錄,下文中稱簡稱目錄代表如下目錄。
/system/core/nativeservice/
編寫代碼
在目錄中創(chuàng)建主代碼文件 native_main.cpp。另外,我們需要編譯,那么就需要 mk 文件,創(chuàng)建一個 Android.mk 文件。這時,目錄架構(gòu)就是如下這個樣子

編寫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 \ #引用一些函數(shù)庫 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 #編譯守護進程,也就是可執(zhí)行文件 #編譯后,在/system/bin/ 下,變多了 nativeservice 可執(zhí)行文件。 include $(BUILD_EXECUTABLE)
編寫native_main.cpp
在 Linux 中,一個開機啟動的服務,執(zhí)行完后會自動退出,而我們是守護進程,那么就需要一直運行。讓程序一直運行有很多種方法。在 native_main.cpp 中貼出了三種方式,它們分別是 epoll,有名管道(FIFO)和循環(huán)。
epoll 的方式是 Android 系統(tǒng)比較常見的方式,系統(tǒng)的電池狀態(tài)變化、USB 接口狀態(tài)變化等守護進程便是通過 epoll 的方式,實時鑒定并讀取新狀態(tài)。
有名管道,在 IPC 通信中比較簡單、便捷,適合輕量級任務。
循環(huán),這個是最老套的方式。
三種方式在 native_main.cpp 都貼出來了,本文側(cè)重使用有名管道(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 的方式,
// 監(jiān)聽一個 socket,如果 socket 被連接,便激活程序讀取數(shù)據(jù)。
// Android 驅(qū)動和用戶態(tài)程序較多使用這種方式交互。
//
/*
int eventct = 5;
struct epoll_event events[eventct];
struct epoll_event ev;
uevent_fd = uevent_open_socket(64*1024, true);
//創(chuàng)建 epoll 通道,監(jiān)聽 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;
// 監(jiān)聽 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/下創(chuàng)建一個名為 nativeservice 的管道,
// 監(jiān)聽管道的數(shù)據(jù)變化,如果有數(shù)據(jù)寫入管道,便讀取數(shù)據(jù)。
//
int res;
int bytes = 0;
char buffer[BUFFER_SIZE + 1];
// 創(chuàng)建 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,知道管道有數(shù)據(jù)寫入,激活程序,往下執(zhí)行
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){
//讀取管道數(shù)據(jù),如果沒有數(shù)據(jù),阻塞等待數(shù)據(jù)被寫入,激活
res = read(fifo_fd, buffer, BUFFER_SIZE);
bytes += res;
SLOGD("native_service result=%s", buffer);
}
} else {
SLOGD("native_service open failed");
}
//關(guān)閉管道資源。
close(fifo_fd);
//
// 3、循環(huán)的方式
// 這種方式代碼最簡單,但是耗資源,沒有實時性。
// 一個死循環(huán),每隔 5 秒運行一次
//
/* while(1){
SLOGD("native_service runnig");
sleep(5);
SLOGD("native_service wake");
}
*/
SLOGD("native_service die");
return 0;
}
推進編譯系統(tǒng)
編寫好 Android.mk 和 native_main.cpp 后,可以通過單邊命令 “mmm system/core/nativeservice” 編譯我們的守護進程了。但是此時用 make 編譯整個 AOSP 時,卻不會編譯我們的 nativeservice。因此,需要告訴編譯系統(tǒng),編譯工程時,同時編譯 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 #我們的代碼結(jié)束 service console /system/bin/sh
讀者可以查看 AOSP 中 system/core/init/README.md 文件了解 init.rc 的語法和配置方法。對于 class core 等不同類別的區(qū)別,讀者可以閱讀《Android加密之全盤加密》相關(guān)的闡述。
配置SeAndroid
至此,編譯整個工程,守護進程也可以被編譯了,也配置了開機自啟動。這個時候,刷到手機是否就可以運行守護進程了呢?不可以,我們知道 Android 繼用了 SeLinux 安全機制,同時發(fā)展出 SeAndroid 機制,所有文件和進程都需要配置 SeAndroid 才能有權(quán)限。因此,如果沒有給守護進程以及守護進程需要操作的目錄和文件賦予權(quán)限,都會被 SeAndroid 過濾或禁止。
由于 QCOM 和 Mediatek 的不同,在相關(guān)文件的放置路徑會不同,但是方法都是一樣的,不同的平臺,找到對應的路徑下的文件就可以了。本文以 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 文件,文件內(nèi)容如下
#守護進程 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目錄創(chuàng)建管道文件
allow nativeservice tmpfs:fifo_file create_file_perms;
#允許 nativeservice 在mnt目錄讀寫
allow nativeservice tmpfs:dir rw_dir_perms;
#允許 nativeservice 在mnt目錄創(chuàng)建目錄
allow nativeservice tmpfs:dir create_dir_perms;
刷機驗證
至此,需要編譯整個 AOSP 工程,當然,如果有編譯過,只需要增量編譯即可,很快就可以編譯完成。
1、刷機后在手機的 /system/bin/nativeservie 目錄下能看到守護進程;

2、看一下 SeAndroid 的 SContext

3、看一下 FIFO 管道文件

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

5、管道文件 SeAndroid 的 tcontext

6、守護進程啟動,啟動后打開管道,等待管道數(shù)據(jù)寫入。由于守護進程比抓 log 的工具啟動還早,因此,開機時前面的 log 無法抓取,如下 log 是手動 kill 掉守護進程打印的 log

7、通過終端給管道寫入數(shù)據(jù)

8、守護進程激活,讀取數(shù)據(jù)

總結(jié)
Android 守護進程可以做很多上層無法完成的功能,但是,為了安全,要運用好 SeAndroid,以最小能力的原則去配置安全權(quán)限。創(chuàng)建守護進程,要編寫對應代碼,配置 rc 文件,配置 SeAndroid。
以上這篇創(chuàng)建Android守護進程實例(底層服務)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android省市區(qū)三級聯(lián)動控件使用方法實例講解
最近有需求需要實現(xiàn)省市區(qū)三級聯(lián)動,但是發(fā)現(xiàn)之前的實現(xiàn)不夠靈活,自己做了一些優(yōu)化。下面通過實例代碼給大家介紹下Android省市區(qū)三級聯(lián)動控件使用方法2017-01-01
Ubuntu中為Android系統(tǒng)上編寫Linux內(nèi)核驅(qū)動程序?qū)崿F(xiàn)方法
本文主要介紹在Ubuntu 上為Android系統(tǒng)編寫Linux內(nèi)核驅(qū)動程序, 這里對編寫驅(qū)動程序做了詳細的說明,對研究Android源碼和HAL都有巨大的幫助,有需要的小伙伴可以參考下2016-08-08
Android SQLite數(shù)據(jù)庫的增 刪 查找操作
這篇文章主要介紹了Android SQLite數(shù)據(jù)庫的增 刪 查找操作,需要的朋友可以參考下2017-02-02
Android自定義控件實現(xiàn)圓形進度CircleProgressBar
這篇文章主要為大家詳細介紹了Android自定義控件實現(xiàn)圓形進度CircleProgressBar,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-05-05
詳解Recyclerview item中有EditText使用刷新遇到的坑
這篇文章主要介紹了詳解Recyclerview item中有EditText使用刷新遇到的坑,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05

