Level 3:加一个 native 服务开机自启动
我想做
在系统里加一个自己的可执行文件,开机自动跑起来,logcat 能看到它的输出。
为什么要先做这个再做驱动?因为它最快让你体验完整的"加代码→配构建→配 init.rc→写 SELinux policy→编译→烧录→验证"闭环。不到你手写驱动的时间的 1/10,但建立的操作习惯后面每个 Level 都在用。
先回答三个问题
-
Android 系统启动后,用户态的入口是什么?
- init 进程是所有用户态进程的祖先(PID 1)
- init 解析
init.rc文件,按规则启动服务 - 厂商自己的服务可以通过
init_rc属性添加到 vendor 分区
-
vendor 分区和 system 分区是什么关系?
- system 分区存 Android 通用组件
- vendor 分区存厂商(Amlogic/你的公司)的专有部分
- Treble 架构强制隔离二者——你的服务应该放 vendor 分区
-
为什么加了服务还要写 SELinux policy?
- Android 的 SELinux 默认不让任何未定义的进程运行
- 不加 .te 文件,你的服务一启动就被 kernel 杀掉
- 不是 bug,是机制
需要懂的知识
Android.bp(Soong 构建)
AOSP 从 Android 7 开始引入 Soong 替代 Makefile,配置文件是 Android.bp。常用模块类型:
cc_binary { // 编译为可执行文件
name: "luojService",
srcs: ["luojService.cpp"],
shared_libs: [
"liblog",
],
vendor: true, // 安装到 vendor 分区
}
vendor: true 决定了产物的安装路径。
- 源码在
vendor/giec/apps/luojTest/luojHello.c - 编译产物在
out/target/product/ross/vendor/bin/luojHello - 烧录后出现在板子的
/vendor/bin/luojHello
流程:Android.bp 中的 vendor: true → Soong 将产物输出到 out/.../vendor/bin/ → 打包 vendor.img 时这个目录成为 /vendor/ 分区 → 板子上就是 /vendor/bin/luojHello。
如果去掉 vendor: true,默认会安装到 system/bin/,但 Treble 架构下厂商模块应该放 vendor 分区。
Android.mk 写法(项目中同样常见)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := luojService
LOCAL_SRC_FILES := luojService.cpp
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_INIT_RC := luojService.rc
LOCAL_VENDOR_MODULE := true
include $(BUILD_EXECUTABLE)
这里 LOCAL_VENDOR_MODULE := true 等同于 Android.bp 的 vendor: true。
init.rc 语法
init.rc 是 init 进程的配置文件:
service luojService /vendor/bin/luojService
class late_start
user root
group root
disabled
on property:sys.boot_completed=1
start luojService
核心概念:
service——定义如何启动一个进程class late_start——late_start 类服务在系统基本启动完成后拉起on property:X=Y——当系统属性 X 等于 Y 时触发操作
SELinux policy 最小模板
每个新增的服务都需要三个声明:
type luojService, domain;
type luojService_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(luojService)
allow 语句的格式:allow source_type target_type:class { permission };
什么时候需要 .te 文件? 只有当进程以独立 domain 身份后台自启动(通过 init.rc)时才需要配 SELinux policy。 如果是
adb shell手动执行,进程继承 shell domain ——userdebug/eng版本中 shell 域权限宽松。
file_contexts —— 第三步不可少 .te 文件只定义了 domain 和 exec_type 的标签名,但没告诉 SELinux “哪个文件路径对应哪个标签”。
环节 在哪配置 作用 定义 domain 和 exec_type .te文件声明这两个标签存在 声明 init 允许执行 init_daemon_domain(domain)允许 init 执行对应 exec_type 并转到新 domain 路径→标签映射 file_contexts/vendor/bin/xxx → xxx_exec检查方法:
adb shell ls -Z /vendor/bin/xxx,看标签是否为自己定义的 exec_type。
动手方案
# 第 1 步:创建模块目录
mkdir -p vendor/giec/apps/luojService/
# 第 2 步:写源码
cat > vendor/giec/apps/luojService/luojService.cpp << 'EOF'
#include <android/log.h>
#include <unistd.h>
#define LOG_TAG "luojService"
int main() {
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "luojService started");
int count = 0;
while (1) {
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "heartbeat #%d", count++);
sleep(5);
}
return 0;
}
EOF
# 第 3 步:写 Android.bp
cat > vendor/giec/apps/luojService/Android.bp << 'EOF'
cc_binary {
name: "luojService",
srcs: ["luojService.cpp"],
shared_libs: [
"liblog",
],
init_rc: ["luojService.rc"],
vendor: true,
}
EOF
# 第 4 步:写 init.rc
cat > vendor/giec/apps/luojService/luojService.rc << 'EOF'
service luojService /vendor/bin/luojService
class late_start
user root
group root
disabled
on property:sys.boot_completed=1
start luojService
EOF
# 第 5 步:写 SELinux policy
cat > vendor/giec/common/sepolicy/luojService.te << 'EOF'
type luojService, domain;
type luojService_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(luojService)
EOF
# 第 6 步:加 file_contexts
# 编辑 vendor/giec/common/sepolicy/file_contexts,添加:
# /vendor/bin/luojService u:object_r:luojService_exec:s0
# 第 7 步:在 PRODUCT_PACKAGES 中注册
# 编辑 vendor/giec/device-giec.mk,在 PRODUCT_PACKAGES 中追加 luojService
# 第 8 步:编译烧录(参考 level-2)
# 第 10 步:验证
adb shell ps -A | grep luojService
adb shell logcat -s luojService
adb shell dmesg | grep avc | grep luojService
验收清单
| 项目 | 验证方式 |
|---|---|
| 服务开机自启动 | adb shell ps -A | grep luojService 能看到进程 |
| logcat 有输出 | adb shell logcat -s luojService 看到 heartbeat 日志 |
| 没有 SELinux 拒绝 | adb shell dmesg | grep avc | grep luojService 无输出 |
| 理解每层的作用 | 你能说清楚:Android.bp 做什么、init.rc 做什么、.te 做什么 |
| SELinux 标签正确 | adb shell ls -Z /vendor/bin/luojService 输出 u:object_r:luojService_exec:s0 |
实操中遇到的问题记录在 debug.md