把 Android App 打包成系统应用(System App)

一句话结论

只有被平台私钥签名的 APK,放到 /system/priv-app/ 下并设置 android:sharedUserId="android.uid.system",才能成为真正的系统应用,获得 READ_PRIVILEGED_PHONE_STATE、MOUNT_UNMOUNT_FILESYSTEMS 等特权权限。


适用场景

  • 需要预装到定制设备
  • 需要访问普通应用无法申请的特权权限
  • ROM 厂商、ODM/OEM 项目、行业终端调试

01. 拿到 platform key 的三个文件

系统签名的核心就是这三个文件,必须和设备 ROM 对应:

文件 作用 常见来源
platform.x509.pem 公钥证书 AOSP 源码 build/target/product/security/
platform.pk8 私钥 同上,或设备厂商提供的 releasekey
signapk.jar AOSP 签名工具 源码路径 build/tools/signapk/

厂商 ROM 的 key 文件名可能是 releasekey.pk8、releasekey.x509.pem 或 vendor_xxxx.pk8,必须和设备实际刷入的 ROM 匹配,否则签名无效。


02. 把 APK 重新签名为 platform 签名

2.1 先用 q5.jks 出 v1-only 的 release APK

在 app/build.gradle 的 release 配置里强制 v1 签名:

arduino 复制代码
android {
    signingConfigs {
        config {
            // q5.jks 配置
            storeFile file("q5.jks")
            storePassword "xxx"
            keyAlias "xxx"
            keyPassword "xxx"
​
            v1SigningEnabled true
            v2SigningEnabled false
        }
    }
​
    buildTypes {
        release {
            signingConfig signingConfigs.config
        }
    }
}

debug 版本必须保持 v1SigningEnabled true 和 v2SigningEnabled true 都开启,否则 adb install 在 7.0+ 设备上会失败。

然后在 Android Studio 里:

rust 复制代码
Build -> Generate Signed APK -> 选择 q5Release flavor

2.2 用 signapk 重新签名

拿到 app\build\outputs\apk\q5\release\q5_xxx_release.apk 后,执行:

arduino 复制代码
java -jar signapk.jar \
    -w \
    platform.x509.pem \
    platform.pk8 \
    app\build\outputs\apk\q5\release\q5_xxx_release.apk \
    app-system-signed.apk

signapk.jar 默认只生成 v1 签名,正好符合系统应用要求。签完不要再拿 jks 工具二次签名,否则会覆盖 platform 签名。


03. 推送到设备的 /system/priv-app/

bash 复制代码
adb root
adb remount
​
# 1) 建包名目录(注意用 applicationId,不是 package)
adb shell mkdir -p /system/priv-app/com.qhsoft.q5
​
# 2) 推送 APK
adb push app-system-signed.apk /system/priv-app/com.qhsoft.q5/app.apk
​
# 3) 关键权限设置,这三步很多人漏
adb shell chmod 644 /system/priv-app/com.qhsoft.q5/app.apk
adb shell chown root:root /system/priv-app/com.qhsoft.q5/app.apk
adb shell restorecon /system/priv-app/com.qhsoft.q5/app.apk
​
# 4) 重启生效
adb reboot

chmod 644、chown root:root、restorecon 必须做,否则 SELinux 或文件权限检查会导致系统应用权限不生效。


04. 验证是否真正成为系统应用

4.1 确认是系统应用(userId=1000)

go 复制代码
adb shell dumpsys package com.qhsoft.q5 | findstr "userId"

期望输出包含 userId=1000。

4.2 确认从 priv-app 路径安装

lua 复制代码
adb shell pm path com.qhsoft.q5

期望:

bash 复制代码
/system/priv-app/com.qhsoft.q5/app.apk

4.3 确认特权权限已授权

go 复制代码
adb shell dumpsys package com.qhsoft.q5 | findstr "READ_PRIVILEGED_PHONE_STATE"

期望:

ini 复制代码
permission grant=true

配套改动清单

AndroidManifest.xml

ini 复制代码
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qhsoft.q5"
    android:sharedUserId="android.uid.system">
​
    <application
        android:persistent="true"
        ... />
​
</manifest>

android:sharedUserId="android.uid.system" 是核心,否则即使放到 priv-app 也拿不到系统权限。

app/build.gradle

java 复制代码
signingConfigs {
    config {
        storeFile file("q5.jks")
        storePassword "xxx"
        keyAlias "xxx"
        keyPassword "xxx"
​
        v1SigningEnabled true
        v2SigningEnabled false
    }
}

总结

  1. 系统应用必须用设备的 platform 私钥签名。
  2. 签名后的 APK 要放到 /system/priv-app/<applicationId>/app.apk。
  3. 配置目录权限 644、root:root、restorecon 后重启。
  4. AndroidManifest.xml 必须声明 android:sharedUserId="android.uid.system"。

按这套流程走下来,dumpsys package 看到 userId=1000 和 permission grant=true,就说明系统应用模式已经跑通。

相关推荐
JMchen2 小时前
【Jetpack 内核实战 01】开篇:Jetpack 组件全家桶核心逻辑与专栏完整规划
android·kotlin·android jetpack
骑着蜗牛撵大象3274 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
hunterandroid5 小时前
Retrofit 网络请求的异常处理与重试策略实战
android
sejinan5 小时前
极致轻量与秒级起播:NativeWasmTv 开源硬核技术解析与升级版体验
android·开源·webview·tv
小羊没烦恼!5 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
mmsx6 小时前
Android 静默失败实录:五个没有日志的线上事故
android·kotlin
hai_android6 小时前
从零实现一个支持跨进程(IPC)的自定义 Toast
android·kotlin·android jetpack
龙腾-虎跃6 小时前
基于 SIP 协议的自研电话终端:FreeSWITCH 语音与视频通信实战
android·音视频·freeswitch·sip协议终端·wertc
imDwAaY7 小时前
操作系统如何管理程序运行?从线程、进程到内存隔离与用户态切换:CS162 Note2
android·java·redis