本文轉載自:https://blog.csdn.net/myfriend0/article/details/80016739linux
Android底層服務,即運行在 linux 下的進程,是 Android 系統運行的基礎,完成 Android 或者說計算機最基本的功能。好比鏈接服務(包括 WIFI,BT 等等);好比 Android 的 adb 功能;好比存儲監控等等。沒有這些底層服務,上層也就沒有了對應的功能。android
Android 底層服務每每是常駐內存,時刻運行完成任務。底層服務進程,每每具備更多的權限,可能和驅動通訊,可能和 linux 內核通訊,可能須要操做系統核心運行文件以及節點等等。因此,底層服務,能夠幫你完成更多計算機基本功能。安全
本文所使用的 AOSP 是基於 Android 8.1。閱讀文本須要對 Android 的架構、編譯系統、AOSP工程和 SeAndroid 有基本認識。bash
咱們在 Android 系統通用守護進程目錄下建立咱們的守護進程,固然你也能夠在其它目錄下放置你的守護進程。markdown
/system/core/
在上面的目錄下,建立守護進程的文件夾 nativeservice,那麼,咱們的守護進程就存在以下目錄,下文中稱簡稱目錄表明以下目錄。架構
/system/core/nativeservice/
在目錄中建立主代碼文件 native_main.cpp。另外,咱們須要編譯,那麼就須要 mk 文件,建立一個 Android.mk 文件。這時,目錄架構就是以下這個樣子dom
我在代碼中儘量的註釋清楚重要語句的做用,讀者若是對 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)
在 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; }
編寫好 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加密之全盤加密》相關的闡述。
至此,編譯整個工程,守護進程也能夠被編譯了,也配置了開機自啓動。這個時候,刷到手機是否就能夠運行守護進程了呢?不能夠,咱們知道 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
二、在 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 工程,固然,若是有編譯過,只須要增量編譯便可,很快就能夠編譯完成。
一、刷機後在手機的 /system/bin/nativeservie 目錄下能看到守護進程;
二、看一下 SeAndroid 的 SContext
三、看一下 FIFO 管道文件
四、prwx 前面的 p 表明是一個管道文件
五、管道文件 SeAndroid 的 tcontext
六、守護進程啓動,啓動後打開管道,等待管道數據寫入。因爲守護進程比抓 log 的工具啓動還早,所以,開機時前面的 log 沒法抓取,以下 log 是手動 kill 掉守護進程打印的 log
七、經過終端給管道寫入數據
八、守護進程激活,讀取數據
Android 守護進程能夠作不少上層沒法完成的功能,可是,爲了安全,要運用好 SeAndroid,以最小能力的原則去配置安全權限。建立守護進程,要編寫對應代碼,配置 rc 文件,配置 SeAndroid。