Loading...

文章背景图

给一加 Ace 3 Pro 换上赛博朋克开关机动画:从 EROFS 到 KernelSU 持久化模块

2026-08-05
23
-
- 分钟
title: "给一加 Ace 3 Pro 换上赛博朋克开关机动画:从 EROFS 到 KernelSU 持久化模块"
slug: "ace3pro-cyberpunk-boot-animation"
date: "2026-08-05"
categories:
  - "Android"
tags:
  - "OnePlus Ace 3 Pro"
  - "ColorOS"
  - "KernelSU Next"
  - "BootAnimation"
  - "Root"
description: "记录一次从动画文件分析、只读分区适配、bind mount 测试,到 KernelSU 持久化模块和三版本发布的完整过程。"
draft: false

最近给一加 Ace 3 Pro 折腾 Root 环境时,我顺手把开机和关机动画也换成了 Cyberpunk 2077 风格。

最终效果是:

电源菜单重启
→ Cyberpunk 关机动画
→ Bootloader unlocked 黄色提示
→ Cyberpunk 开机动画
→ 进入系统

看起来只是“换两个 ZIP”,真正动手以后才发现,ColorOS 16 下的动画文件位于只读的 EROFS 分区,普通替换根本行不通;不同重启方式、软重启和第三方预览工具还各有各的问题。

这篇文章记录一下整个适配过程,以及最后如何把它做成一个可安装、可禁用、可恢复原厂动画的 KernelSU 模块。

测试环境

项目 信息
设备 OnePlus Ace 3 Pro / PJX110
系统 ColorOS 16 / Android 16
固件 PJX110_16.0.5.701(CN01)
Root KernelSU Next 3.3.0
内核 WildKernel 6.1.141-android14-OP-WILD
模块机制 Magisk-style / systemless module

本文所有结论都基于上面这套环境。其他 ColorOS 版本、Magisk、APatch 或其他机型可能也能用,但我没有替它们做完整实机验证。

动画文件到底在哪里

一加 Ace 3 Pro 的开机和关机动画分别位于:

/my_product/media/bootanimation/bootanimation.zip
/my_product/media/bootanimation/rbootanimation.zip

先确认文件和 SELinux 标签:

su

ls -lZ \
  /my_product/media/bootanimation/bootanimation.zip \
  /my_product/media/bootanimation/rbootanimation.zip

问题在于 /my_product 是 EROFS,只读。

也就是说,下面这种最直接的思路并不可行:

cp bootanimation.zip \
  /my_product/media/bootanimation/bootanimation.zip

即使拿到了 Root 权限,也不能把 EROFS 当成普通可写分区处理。

为什么最后选择 bind mount

既然不能真正改写原文件,就让系统在运行时看到另一个文件。

手动测试时,可以这样做:

su

mount --bind \
  /data/local/tmp/bootanimation.zip \
  /my_product/media/bootanimation/bootanimation.zip

mount --bind \
  /data/local/tmp/rbootanimation.zip \
  /my_product/media/bootanimation/rbootanimation.zip

bind mount 生效以后,目标路径没有变化,但打开它时读到的已经是自定义动画。

这种方式有几个优点:

  • 不改写 /my_product
  • 不破坏原厂动画
  • 挂载消失后自动恢复原文件
  • 很适合封装为 systemless 模块
  • 出问题时禁用模块并重启即可回退

手动挂载确认开机、关机动画都能正常播放以后,剩下的工作就是让它在每次完整启动时自动执行。

模块结构

最终模块结构很简单:

cyberpunk-animation.zip
├── module.prop
├── post-fs-data.sh
├── skip_mount
├── bootanimation.zip
└── rbootanimation.zip

module.prop 用于声明模块信息:

id=cyberpunk_dual_animation
name=Cyberpunk Boot + Shutdown Animation - Flatline
version=1.1.0-flatline
versionCode=110
author=akizukishiro
description=Cyberpunk 2077 Flatline boot and shutdown animations for OnePlus Ace 3 Pro

三个动画版本都使用相同的模块 ID:

id=cyberpunk_dual_animation

这样切换版本时会替换当前模块,而不是出现三个模块同时争抢相同挂载路径。

skip_mount 是一个空文件,用来阻止模块管理器自动把模块目录映射进系统。真正的替换工作全部由 post-fs-data.sh 完成。

核心脚本

持久化模块的核心脚本如下:

#!/system/bin/sh

MODDIR=${0%/*}

BOOT_SRC="$MODDIR/bootanimation.zip"
BOOT_DST="/my_product/media/bootanimation/bootanimation.zip"

SHUTDOWN_SRC="$MODDIR/rbootanimation.zip"
SHUTDOWN_DST="/my_product/media/bootanimation/rbootanimation.zip"

LOG="$MODDIR/bootanimation.log"

echo "start" > "$LOG"

prepare_source() {
    SOURCE_FILE="$1"

    chown 0:0 "$SOURCE_FILE" 2>> "$LOG"
    chmod 0644 "$SOURCE_FILE" 2>> "$LOG"
    chcon u:object_r:system_file:s0 "$SOURCE_FILE" 2>> "$LOG"
}

bind_animation() {
    SOURCE_FILE="$1"
    TARGET_FILE="$2"
    LABEL="$3"

    if [ ! -f "$SOURCE_FILE" ]; then
        echo "$LABEL source missing" >> "$LOG"
        return
    fi

    if [ ! -f "$TARGET_FILE" ]; then
        echo "$LABEL target missing" >> "$LOG"
        return
    fi

    prepare_source "$SOURCE_FILE"

    if mount --bind "$SOURCE_FILE" "$TARGET_FILE"; then
        echo "$LABEL bind ok" >> "$LOG"
    else
        echo "$LABEL bind failed" >> "$LOG"
    fi
}

bind_animation "$BOOT_SRC" "$BOOT_DST" "boot"
bind_animation "$SHUTDOWN_SRC" "$SHUTDOWN_DST" "shutdown"

exit 0

启动后可以查看日志:

su
cat /data/adb/modules/cyberpunk_dual_animation/bootanimation.log

正常情况下会看到:

start
boot bind ok
shutdown bind ok

还可以通过 PID 1 的 mountinfo 确认挂载确实存在:

su

grep -E \
  " /my_product/media/bootanimation/(bootanimation|rbootanimation)\.zip " \
  /proc/1/mountinfo

为什么先做 One-Shot 测试模块

开机动画模块最麻烦的地方在于:测试失败时,可能要等到下一次启动才能处理。

为了避免一个错误模块持续生效,我先做了 One-Shot 测试版。它在第一次启动时完成挂载,同时自动创建:

/data/adb/modules/<module-id>/disable

这样本次启动仍然能播放测试动画,但下一次启动模块会自动禁用,恢复原厂动画。

One-Shot 版本额外包含这一行:

touch "$MODDIR/disable"

完整测试流程是:

安装测试模块
→ 第一次重启播放自定义开机动画
→ 进入系统后检查日志和哈希
→ 使用电源菜单重启,检查关机动画
→ 第二次启动自动恢复原厂动画

三个版本都通过了开机、关机、挂载和自动回退测试以后,才生成正式持久化包。

三种动画版本

动画素材来自 sodasoba1/ONEPLUS9-OOS13-BootAnimation。我为 Ace 3 Pro 打包了三个版本。

OnePlus Glitch

将原本的 Cyberpunk 标志替换为 OnePlus 标志,并加入故障效果和配套关机动画。

开机动画 关机动画
OnePlus Glitch 开机动画 OnePlus Glitch 关机动画

CyberGlitch

在 OnePlus Glitch 的基础上增加了更多故障效果。

它的 desc.txt 是:

g 1080 2400 0 0 60

c 1 0 part1
c 1 0 part2

两个部分都只播放一次。实机表现不是播放结束后黑屏,而是停留在最后一帧,直到 Android 进入下一阶段。

开机动画 关机动画
CyberGlitch 开机动画 CyberGlitch 关机动画

Cyberpunk Flatline

这是我最后选择长期使用的版本。

它重新编辑了启动文字、存储设备文字和控制台动画,第二部分会持续循环,关机动画也带有 Flatlined 循环效果。

g 1080 2400 0 0 60

c 1 0 part1
c 0 0 part2

其中 part1 播放一次,part2 循环到 Android 启动完成。

关机动画为 30 FPS:

g 1080 300 0 641 30

p 1 0 part0
p 0 0 part1
开机动画 关机动画
Cyberpunk Flatline 开机动画 Cyberpunk Flatline 关机动画Cyberpunk Flatline 循环动画

原仓库 README 中把 Flatline 写成过 Flateline,这里统一使用正确拼写 Flatline

实际重启表现

不同重启方式并不完全一样。

电源菜单普通重启

Cyberpunk 关机动画
→ unlocked 黄色提示
→ Cyberpunk 开机动画

这是最完整的效果。

KernelSU Next 普通重启

表现和电源菜单一致:

Cyberpunk 关机动画
→ unlocked 黄色提示
→ Cyberpunk 开机动画

终端执行 reboot

短暂黑屏
→ unlocked 黄色提示
→ Cyberpunk 开机动画

终端中的 reboot 可能直接跳过关机动画。

KernelSU Next 软重启

不建议使用。

在这台设备上,软重启会让原厂和自定义开机动画都只显示左上角区域,右侧和下方被裁切。既然原厂动画也会复现,就说明问题不在动画文件,而在软重启后的显示管线状态。

一个非常坑的预览功能

某些工具箱带有“播放当前开机动画”的预览功能。我测试后,整个系统画面被缩到了左上角,右侧和底部出现大块黑边,但触控坐标仍按原分辨率计算。

最危险的是,这时不能盲目执行:

wm size reset
wm density reset

ColorOS 在这台设备上本来就使用逻辑分辨率和密度覆盖,直接 reset 反而会把显示参数改错。

正常参数为:

Physical size: 1264x2780
Override size: 1080x2376

Physical density: 560
Override density: 480

对应恢复命令:

su
wm size 1080x2376
wm density 480
reboot

这些数值只适用于本文测试的 Ace 3 Pro,不应照搬到其他设备。

黄色 unlocked 提示为什么换不掉

开机时出现的黄色 unlocked 提示属于 Bootloader 阶段。

Android 的 bootanimation.zip 还没开始播放时,它就已经显示了。因此这个模块无法修改或隐藏该提示。

完整顺序实际上是:

Bootloader 提示
→ Android BootAnimation 服务
→ 系统界面

开关机动画模块只能控制中间的 Android BootAnimation 阶段。

导出 MP4 预览时遇到的另一个问题

为了发博客和朋友圈,我还把 Flatline 动画导出成了四段 MP4:

开机初始动画:part1
开机循环动画:part2
关机初始动画:part0
关机循环动画:part1

循环段导出 20 秒,初始段完整播放一次。

需要注意的是,desc.txt 中的 60 FPS 和 30 FPS 是目标帧率。实际开机时还要解码 PNG、上传纹理和渲染;设备处理某些帧超过目标间隔时,BootAnimation 不一定会跳帧追赶。

所以用 FFmpeg 严格导出 60 FPS 的视频,可能会比手机实机播放稍快。想完全复刻实机速度,最好用手机录像测出每一段实际时长,再按:

实际平均帧率 = 该段总帧数 ÷ 实机播放时长

重新导出。

发布方式

完整模块包比较大,不适合直接塞进 Git 仓库历史。我把脚本、README 和预览 GIF 放在仓库中,把可安装 ZIP 放到 GitHub Releases。

项目仓库:

Release 中提供三个安装包:

OnePlus-Ace3Pro-Cyberpunk-OnePlusGlitch-v1.1.0.zip
OnePlus-Ace3Pro-Cyberpunk-CyberGlitch-v1.1.0.zip
OnePlus-Ace3Pro-Cyberpunk-Flatline-v1.1.0.zip

三个包只能选择一个安装。由于它们使用相同模块 ID,安装另一个版本会覆盖当前版本。

GitHub 自动生成的:

Source code (zip)
Source code (tar.gz)

只是仓库快照,不是完整可刷入模块。

如何恢复原厂动画

最简单的方法是在 KernelSU Next 中关闭或卸载模块,然后普通重启。

也可以手动禁用:

su
touch /data/adb/modules/cyberpunk_dual_animation/disable
reboot

重新启用:

su
rm -f /data/adb/modules/cyberpunk_dual_animation/disable
reboot

因为原厂文件从未被真正改写,模块不再挂载后,系统自然会重新读取原厂动画。

总结

这次适配表面上只是“换开关机动画”,实际涉及了:

  • 确认 ColorOS 的真实动画路径
  • 处理 EROFS 只读分区
  • 使用 bind mount 做 systemless 替换
  • 处理权限与 SELinux 标签
  • 区分普通重启、终端重启和软重启
  • 使用 One-Shot 模块做安全测试和自动回退
  • 为三套动画分别验证开机与关机效果
  • 打包持久化模块并发布 Release
  • 处理 GitHub README、预览 GIF 和双语说明

最终的方案并没有直接修改系统分区,也不需要永久覆盖原厂动画文件。关闭模块并重启,一切就会恢复原样。

对我来说,这也是 systemless 模块最有意思的地方:不是强行把系统文件改掉,而是在合适的启动阶段,让系统临时看到我们希望它看到的内容。

来源与致谢

动画素材和预览 GIF 来自:

原仓库向原始模块作者 天伞桜 & PanL 致谢。

本文中的一加 Ace 3 Pro 路径适配、KernelSU 模块封装、bind mount 逻辑、权限与 SELinux 处理,以及三个版本的实机测试由 akizukishiro 完成。

Cyberpunk 2077 及相关名称和素材归各自权利方所有。本文与项目均为非商业社区分享,与 OnePlus、OPPO 或 CD PROJEKT RED 不存在官方关联。

风险提示

Root 模块始终存在风险。安装前请备份重要数据,了解 KernelSU 或其他 Root 管理器的安全模式,不要同时启用多个替换相同动画路径的模块。

本文中的显示恢复参数只适用于测试使用的一加 Ace 3 Pro,请勿在其他设备上直接照搬。

原创

给一加 Ace 3 Pro 换上赛博朋克开关机动画:从 EROFS 到 KernelSU 持久化模块

评论交流

文章目录