
由于 Android 15 及以上即使添加 sharedUserId“android.uid.system” 也不能实现系统级权限所以这一篇介绍的是如何在 Android 15 及以上版本中创建系统级 app 并测试驱动。实现方式有两种一种在系统白名单中添加 app 包名另一种是通过 magisk 来实现 app 白名单。一、开发环境LineageOS 23.2 Android 16Ubuntu 24.04.4 LTS红米9 lancelot已解锁 BL 并刷入 LineageOS 23.2 及 Magisk二、实现系统级别 APP下面的两种方法都需要先配置 android:sharedUserId“android.uid.system”。白名单的具体值也可以在手机上查看adb shellls-l/system/etc/permissions/package-shareduid-allowlist.xml# 或者adb shellls-l/system/etc/sysconfig/package-shareduid-allowlist.xml1. 在系统白名单中添加 app 包名在 YourSource/frameworks/base/data/etc/package-shareduid-allowlist.xml 的 config 节点中添加你的 app 包名例如allow-package-shareduidpackagecom.example.leddemoshareduidandroid.uid.system/2. 通过 magisk 来实现 app 白名单创建模块目录在手机存储的 Magisk 模块目录下通常是 /data/adb/modules/新建一个文件夹例如 my_shareduid_patch。构建文件结构在该文件夹内严格按照系统文件的路径创建子目录。对于你的需求路径为my_shareduid_patch/system/etc/sysconfig/package-shareduid-allowlist.xml放置修改后的文件将你已经编辑好的 package-shareduid-allowlist.xml 文件放入上述路径。设置正确的权限和 SELinux 上下文这是关键一步。执行以下命令确保文件能被系统正确识别adb shellsu-cchmod 644 /data/adb/modules/my_shareduid_patch/system/etc/sysconfig/package-shareduid-allowlist.xmladb shellsu-cchcon u:object_r:system_file:s0 /data/adb/modules/my_shareduid_patch/system/etc/sysconfig/package-shareduid-allowlist.xml如果 adb shell su 命令执行失败可以到开发者选项中开启 root 权限。这里推荐 magisk 来实现 app 白名单因为 magisk 可以在不修改系统文件的情况下实现 app 白名单不需要要编译系统文件。三、测试驱动编写测试代码packagecom.example.leddemo;publicclassLedNative{static{System.loadLibrary(leddemo);}publicstaticfinalintLED_OFF0;publicstaticfinalintLED_ON1;/** 打开 /dev/myled返回 fd失败返回负值 */publicstaticnativeintopenLed();/** 写 1 字节控制 LED0灭 1亮 */publicstaticnativeintsetLed(intfd,intstate);/** 关闭 fd */publicstaticnativevoidcloseLed(intfd);}编译 JNI 代码#includeled_jni.h#includejni.h#includefcntl.h#includeunistd.h#includeerrno.h#includeandroid/log.h#includecstring#defineLOG_TAGLedJNI#defineLOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#defineLOGE(...)__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)#defineLED_DEV_PATH/dev/myled/* 包名 com.example.leddemo.LedNative 对应 JNI 函数名 */#defineJNI_CLASScom/example/leddemo/LedNative/* ---------- openLed ---------- */externCJNIEXPORT jint JNICALLJava_com_example_leddemo_LedNative_openLed(JNIEnv*env,jclass clazz){intfdopen(LED_DEV_PATH,O_RDWR);if(fd0){LOGE(open %s failed: %s,LED_DEV_PATH,strerror(errno));return-errno;}LOGI(open %s ok, fd%d,LED_DEV_PATH,fd);returnfd;}/* ---------- setLed ---------- */externCJNIEXPORT jint JNICALLJava_com_example_leddemo_LedNative_setLed(JNIEnv*env,jclass clazz,jint fd,jint state){if(fd0)return-EBADF;/* 驱动 write 要求 count 1只写入 1 个字节 */charbuf(state1)?1:0;ssize_tretwrite((int)fd,buf,1);if(ret!1){LOGE(write led failed: ret%zd, errno%s,ret,strerror(errno));return-errno;}LOGI(set LED state%d ok,state);return0;}/* ---------- closeLed ---------- */externCJNIEXPORTvoidJNICALLJava_com_example_leddemo_LedNative_closeLed(JNIEnv*env,jclass clazz,jint fd){if(fd0){close((int)fd);LOGI(close fd%d,fd);}}将上面的代码编译成 app然后用系统签名签名后就可以在手机上运行了。四、总结虽然通过 JNI 访问操作简单但是这只适用于单个 APP 访问硬件的情况如果是多个 APP 访问会造成许多麻烦例如多个 APP 同时对一个设备节点进行 open 或者 write 会造成数据混乱因此建议采用通过 service manager 访问APP 只需要给 service manager 发送请求service manager 对硬件的访问进行统一管理。这将在下一篇详细介绍。