RK3568 AOSP 编译与快速部署实战

RK3568 AOSP 编译与快速部署实战

记录 RK3568 平台 Android 11 源码的编译体系与日常调试中最常用的增量编译 + 推送部署方法。告别每次改一行代码就全量烧录的低效循环。


一、编译环境

bash 复制代码
cd ~/aosp_sys/rk3568
source build/envsetup.sh
lunch rk3568-userdebug

make.sh 是日常编译的统一入口,封装了三种模式。


二、三种编译模式

2.1 全量编译

bash 复制代码
./make.sh            # 等价于 ./make.sh full

执行 ./build.sh -UKAu:uboot + kernel + android + 打包 update.img。通常 30~60 分钟,改 C/C++ 或内核代码时使用。

2.2 快速编译 + 部署(日常主力)

bash 复制代码
./make.sh quick

frameworks/base/ 下 Java 代码时使用,完整流程耗时约 2~5 分钟:

markdown 复制代码
1. 检查并清理 out/.lock 残留锁文件
2. make framework-minus-apex services.core -j$(nproc)    ← 增量编译
3. jar uf 合并 .class → services.jar
4. adb root + adb remount
5. 删除 /system/framework/{arm,arm64,oat} + /data/dalvik-cache  ← 清除旧 OAT
6. truncate boot-framework.{art,oat,vdex}  ← overlayfs 下无法删除,清零使其失效
7. push framework.jar + services.jar
8. adb reboot + wait-for-device

2.3 仅推送(编译产物已有,只部署)

bash 复制代码
./make.sh push

跳过编译,直接推送上次编译好的 jar 并重启。适合反复测试同一版本或设备 OAT 缓存未清理干净时重新推送。


三、make.sh 脚本完整源码

bash 复制代码
#!/bin/bash
# ============================================================
# RK3568 编译脚本
#   全量编译:            ./make.sh
#   仅编译+部署 jar:     ./make.sh quick
#   仅部署(跳过编译):    ./make.sh push
# ============================================================

set -e

source build/envsetup.sh
lunch rk3568-userdebug

MODE="${1:-full}"

BASE_DIR="$PWD"
FRAMEWORK_SRC="$BASE_DIR/out/target/product/rk3568/system/framework/framework.jar"
SERVICES_SRC="$BASE_DIR/out/target/product/rk3568/system/framework/services.jar"

# ====== 编译前清理锁文件 ======
LOCK_FILE="$BASE_DIR/out/.lock"
if [ -f "$LOCK_FILE" ]; then
    LOCK_PID=$(cat "$LOCK_FILE" 2>/dev/null | head -1)
    if [ -n "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2>/dev/null; then
        echo "!!! 检测到活跃的编译进程 (PID=$LOCK_PID),等待其完成..."
        while kill -0 "$LOCK_PID" 2>/dev/null; do sleep 2; done
        echo "前序编译已完成"
    else
        echo "清理残留锁文件 (PID=${LOCK_PID:-unknown} 已不存在)"
    fi
    rm -f "$LOCK_FILE"
fi

# ====== 编译 ======
build_jars() {
    echo "=== 编译 framework.jar + services.core ==="
    make framework-minus-apex services.core -j$(nproc)

    # services.core 编译后需手动合并到 services.jar
    echo "=== 合并 services.core → services.jar ==="
    local CLASSES_DIR="$BASE_DIR/out/soong/.intermediates/frameworks/base/services/core/services.core.unboosted/android_common/javac/classes"
    local SERVICES_JAR="$BASE_DIR/out/target/product/rk3568/system/framework/services.jar"
    if [ -d "$CLASSES_DIR/com/st/android/stethtether" ] && [ -f "$SERVICES_JAR" ]; then
        jar uf "$SERVICES_JAR" -C "$CLASSES_DIR" com/st/android/stethtether/ 2>/dev/null
        echo "services.jar 已更新 ($(ls -la $SERVICES_JAR | awk '{print $5}') bytes)"
    fi
}

# ====== 部署 ======
push_jars() {
    echo "=== 部署到设备 ==="

    adb root
    adb remount

    echo "清理 oat/vdex 缓存..."
    adb shell rm -rf /system/framework/arm      2>/dev/null || true
    adb shell rm -rf /system/framework/arm64    2>/dev/null || true
    adb shell rm -rf /system/framework/oat      2>/dev/null || true
    adb shell rm -rf /data/dalvik-cache         2>/dev/null || true

    # overlayfs 保护下无法删除基镜像的 boot-framework.art,用 truncate 使其失效
    for f in /system/framework/arm64/boot-framework.art \
             /system/framework/arm64/boot-framework.oat \
             /system/framework/arm64/boot-framework.vdex \
             /system/framework/arm/boot-framework.art \
             /system/framework/arm/boot-framework.oat \
             /system/framework/arm/boot-framework.vdex; do
        if adb shell "[ -f $f ]" 2>/dev/null; then
            echo "使 $f 失效..."
            adb shell "su root truncate -s 0 $f 2>/dev/null" || true
        fi
    done

    if [ -f "$FRAMEWORK_SRC" ]; then
        echo "推送 framework.jar..."
        adb push "$FRAMEWORK_SRC" /system/framework/framework.jar
    fi
    if [ -f "$SERVICES_SRC" ]; then
        echo "推送 services.jar..."
        adb push "$SERVICES_SRC" /system/framework/services.jar
    fi

    echo "重启..."
    adb reboot
    adb wait-for-device
    echo "=== 完成 ==="
}

# ====== 主流程 ======
case "$MODE" in
    full)
        echo "=== 全量编译 ==="
        ./build.sh -UKAu
        ;;
    quick)
        build_jars
        push_jars
        ;;
    push)
        push_jars
        ;;
    *)
        echo "用法: $0 {full|quick|push}"
        echo "  full  - 全量编译 (uboot+kernel+android+update.img)"
        echo "  quick - 编译 framework.jar + services.jar 并部署"
        echo "  push  - 跳过编译,仅推送已有 jar 并重启"
        exit 1
        ;;
esac

关键设计点

机制 说明
锁文件清理 上次编译崩溃残留 out/.lock,检查 PID 是否存在,不存在则删除
jar uf 合并 services.core 编译产物是独立 class 目录,需手动合并到 services.jar
truncate 清零 overlayfs 挂载下无法删除基镜像的 .art/.oat/.vdex,用 truncate -s 0 清零使其失效
错误容忍 rm -rftruncate 失败不中断流程(`

四、为什么必须删除 OAT 缓存

这是 make.sh quick 中最容易被忽略但最关键的一步。

Android 11 的 ART 运行时会优先使用预编译的 .odex/.vdex 文件。如果只替换 jar 而不删除 OAT 缓存:

复制代码
系统优先加载旧的 .odex → 新 jar 中的代码不生效 → 甚至可能崩溃

正确做法:

bash 复制代码
# 1. 删除 OAT 目录
adb shell rm -rf /system/framework/arm
adb shell rm -rf /system/framework/arm64
adb shell rm -rf /system/framework/oat
adb shell rm -rf /data/dalvik-cache

# 2. overlayfs 下的 boot-framework 基镜像文件无法删除,用 truncate 清零
adb shell truncate -s 0 /system/framework/arm64/boot-framework.art
adb shell truncate -s 0 /system/framework/arm64/boot-framework.oat
adb shell truncate -s 0 /system/framework/arm64/boot-framework.vdex

# 3. 推送新 jar
adb push framework.jar /system/framework/
adb push services.jar /system/framework/

# 4. 重启(首次启动稍慢,ART 需重新 AOT 编译)
adb reboot

五、不同类型代码的编译与部署

4.1 Java 系统服务 (services.jar)

改动 frameworks/base/services/core/java/ 下的代码:

bash 复制代码
source build/envsetup.sh && lunch rk3568-userdebug
make services -j$(nproc)

# 然后按第三步操作推送

4.2 Framework API (framework.jar)

改动 frameworks/base/core/java/ 下的代码:

bash 复制代码
make framework-minus-apex -j$(nproc)

# 同样需要删除 OAT + 推送

⚠️ framework.jar 是 bootclasspath 成员。如果只推 framework.jar 不删 OAT,zygote 启动时会因 Native registration unable to find class 崩溃,系统无法启动。

4.3 C/C++ Native 库 (libinputreader.so 等)

Native 库没有 OAT 缓存问题,直接推送和重启即可:

bash 复制代码
# 编译
make libinputflinger -j$(nproc)

# 推送
adb root && adb remount
adb push out/target/product/rk3568/system/lib64/libinputreader.so /system/lib64/
adb push out/target/product/rk3568/system/lib/libinputreader.so /system/lib/
adb reboot

4.4 SystemUI APK

bash 复制代码
make SystemUI -j$(nproc)

adb root && adb remount
adb push out/target/product/rk3568/system/priv-app/SystemUI/SystemUI.apk \
    /system/priv-app/SystemUI/
adb reboot

4.5 SELinux 策略

bash 复制代码
# 仅重编 selinux 策略
./make.sh --selinux

# 或
make selinux_policy -j$(nproc)

4.6 内核驱动

内核驱动是内置(obj-y),不是独立 .ko 模块,需要编译 boot.img 并烧录:

bash 复制代码
# 编译 kernel
make bootimage -j$(nproc)
# 或全量编译
./make.sh
# 烧录 boot.img 到设备

修改后需全量编译或单独编译 kernel 并烧录 boot.img。


六、编译产物路径

模块 编译后位置
framework.jar out/target/product/rk3568/system/framework/framework.jar
services.jar out/target/product/rk3568/system/framework/services.jar
services.core class out/soong/.intermediates/frameworks/base/services/core/services.core.unboosted/android_common/javac/classes/
libinputreader.so (64位) out/target/product/rk3568/system/lib64/libinputreader.so
libinputreader.so (32位) out/target/product/rk3568/system/lib/libinputreader.so
SystemUI.apk out/target/product/rk3568/system/priv-app/SystemUI/SystemUI.apk
boot.img out/target/product/rk3568/boot.img
update.img rockdev/Image-rk3568/update.img

七、编译排错速查

症状 原因 解决
[Syntax Error] 在正常 Java 代码行 Javadoc 中的 */ 被误解析 用空格替代
cannot find symbol 新增常量未编译 touch 修改文件再编译
out/.lock 编译超时 soong_build 残留 rm -f out/.lockmake.sh quick 自动处理)
services.jar 推送后不生效 OAT 缓存未清理 推送前删除 arm/arm64/oat
Native registration unable to find class 推送 framework.jar 未删除 oat 删除 OAT 后重启
neverallow violated 新 allow 撞已有 neverallow 审 neverallow 策略 → 放宽或换 domain
freeze_test 文件差异 改了 sepolicy 未同步 prebuilts cp → prebuilts/api/30.0/
Wdeclaration-after-statement 内核 C90 不允许语句中声明变量 变量声明移到函数顶部
jar uf 后代码不生效 boot-framework.art 缓存 truncate 清零或全量烧录

八、实用技巧

7.1 验证 jar 是否更新

bash 复制代码
# 确认 class 已合并到 jar 中
strings services.jar | grep "新添加的方法名或类名"

7.2 查看编译进度

bash 复制代码
# 查看当前编译任务
ps aux | grep ninja

# 查看编译输出
tail -f out/verbose.log 2>/dev/null

7.3 设备无法启动时的抢救

jar 推送后系统崩溃(system_server 不断重启),仍可通过 adb root && adb remount 在早期启动阶段修复:

bash 复制代码
# 设备崩溃后强制进入 recovery 或等待 adb 可用
adb wait-for-device
adb root && adb remount

# 删除坏 jar 的 OAT 并替换为旧版本
adb shell rm -rf /system/framework/arm*
adb push services.jar /system/framework/
adb reboot

7.4 跳过编译直接测试

bash 复制代码
# 如果只是设备重启后 OAT 缓存没清理干净
./make.sh push    # 跳过编译,直接推 jar + 清缓存 + 重启

本文基于 RK3568 Android11 平台实际开发经验整理。

相关推荐
码农coding2 小时前
android12 SystemUI之通知
android
summerkissyou19873 小时前
android - 性能 - Perfetto - cpu 分析教程及例子
android·性能
AFinalStone4 小时前
Android 7系统休眠唤醒(七)内核层—wakelock与autosleep机制
android·休眠唤醒
starvapour5 小时前
在关机键损坏的情况下让手机关机
android·adb·手机
AFinalStone6 小时前
Android 7系统休眠唤醒(五)休眠全链路
android·电源管理·休眠唤醒
zzq77977 小时前
Android 风险识别机制实测:从录屏悬浮窗到远程控制的边界评估
android·安全·安卓·app加固·御盾安全
码云数智-园园7 小时前
MySQL 慢查询排查完整流程
android
码云骑士7 小时前
【Android Performance】进程关联启动治理详解——从唤醒链分析到自启动管控的完整方案
android