
前一篇讲了背景和路线选择这篇讲最终方案是怎么落地的。hybrid 怎么合成先把结论摆出来normal 内核加 TWRP ramdisk。normal 内核驱动全TWRP ramdisk 不跑 Android 框架。为什么要这么组合看一组对照就清楚了。normal 环境跑框架大核 offline内存约 2GB驱动全。TWRP 环境不跑框架大核也 offline内存约 447MB但缺 fuel gauge 和 WiFi 驱动。hybrid 就是 normal 内核加 TWRP ramdisk大核 offline内存约 447MB驱动全。合成方法不复杂把 TWRP 的 ramdisk10666488 字节和 normal 内核8151028 字节md50018ac拼起来bootsize 改 0x2000000。合成后实测验证了三件事。调度没问题cpu0 在线cpu4-7 睡死内存 156-200Mgovernor 是 mtk_governor。fuelgauged 能跑capacity 读到 100这就证明了之前 capacity 是 -1 不是内核驱动缺失是用户态守护没起来。WiFi 也通网关 0% 丢包外网 223.5.5.5 通39ms。三个内核的对比normal 内核 md5 是0018ac驱动全TWRP 内核 md5 是8fb9c4缺 fuel gauge 和 WiFi 驱动hybrid 用的是 normal 内核所以驱动全。钩子机制怎么设计的思路是 Magisk 式的开机自动执行/data/local/tmp/twrp-hooks/*.sh放脚本即跑不改镜像。具体实现靠 init.rc 里的一个 service 声明。开机时 init 跑 init.rc启动service twrphook /sbin/twrp-hook.sh这是 class default 加 oneshot异步执行不阻塞启动。然后/sbin/twrp-hook.sh这个 runner 干活。它先等/data挂载最多等 60 秒。然后挂载/system只读给脚本提供 wpa_supplicant 这些工具。接着建/data/local/tmp/twrp-hooks目录。最后按文件名顺序遍历目录下的*.sh逐个执行。脚本内容是这样的sh#!/sbin/sh LOG/data/local/tmp/twrp-hook.log HOOK_DIR/data/local/tmp/twrp-hooks i0 while [ $i -lt 60 ]; do if [ -d /data/local ]; then break; fi sleep 1; i$((i 1)) done mkdir -p $HOOK_DIR 2/dev/null mount /system 2/dev/null || mount -o ro /system 2/dev/null export PATH/sbin:/system/bin export LD_LIBRARY_PATH/sbin:/system/lib echo twrp-hook start $(date) $LOG for f in $HOOK_DIR/*.sh; do [ -f $f ] || continue echo --- run $f --- $LOG sh $f 21 | grep -v ^Aborted $LOG done echo twrp-hook done $(date) $LOG那个grep -v ^Aborted后面会讲是用来过滤 wpa_cli segfault 刷屏的。六个钩子脚本钩子按文件名顺序执行耗时长的放后面。最终跑通的是六个脚本加上一个可选的 zram。01-fuelgauged.sh电池守护。这个脚本有点特殊fuelgauged 用后台启动不用 setsid。因为实测发现 setsid 在 hybrid 里启动失败进程数是 0改成简单就成功了。脚本逻辑是先确认/system挂载了没有的话挂上。然后把/system/vendor软链到/vendor因为有些路径依赖。检查/dev/wmtWifi没有的话跑一下 wmt_loader。设置好LD_LIBRARY_PATHkillall 掉旧的 fuelgauged等一秒然后后台启动。验证下来 capacity 读到 100成功。02-wifi.shWiFi 拉起。这个脚本是踩坑最多的地方。逻辑分七步。先挂/system软链/vendor跑 wmt_loader只能跑一次。然后后台起 wmt_launcher等 10 秒。写/dev/wmtWifi等 3 秒ip link set wlan0 up。接着建/data/misc/wifi/sockets目录权限 777写/data/wpa.conf配置文件。killall 掉旧的 wpa_supplicant等一秒用 64 位的库路径启动 wpa_supplicant。然后timeout 3 wpa_cli reassociate触发一次关联。最后等 20 秒。关键在LD_LIBRARY_PATH要设成/system/lib64:/system/vendor/lib64:/system/lib因为 wpa_supplicant 是 64 位的runner 默认设的只有 32 位库不设 lib64 会静默失败不关联。验证下来 WiFi 连上 COMPLETED外网通。20-chroot-mount.sh挂 Debian chroot。这个照抄面具的 chroot-mount 模块去掉等框架的循环。逻辑是等/data/debian/bin出现最多 30 秒然后用 busybox 挂 proc、sysfs、bind /dev、devpts。30-chroot-services.sh起 supervisord。这个也照抄面具的 chroot-services 模块同样去掉等框架的循环。逻辑是等/data/debian/proc/version出现然后 unset 掉LD_PRELOAD和LD_LIBRARY_PATH用setsid busybox chroot ... supervisord启动最多重试三次每次用supervisorctl pid检查是否起来了。这里踩了个坑我一开始瞎加了 rm socket、killall、、重定向还加了网络配置结果 supervisord 起不来。后来照抄面具原文才成功。教训是照抄验证过的原文不要瞎加。40-adbd-tcp.sh开无线 adb。很简单setprop service.adb.tcp.port 5555kill 掉 adbd等三秒让它自己重启。验证下来 adbd 监听 5555adb connect 192.168.31.213:5555能连上。50-static-ip.sh配静态 IP 和路由。这个脚本的关键是轮询ip link等LOWER_UP最多等 180 秒因为 WiFi 关联时间不定60 秒不够。等到之后等三秒ip addr add和ip route add。这里也踩了个坑一开始用后台配 IP结果 adb shell 退出后进程被杀了。改成阻塞式就好了。还有一个坑是ip命令也要显式设 lib64否则会 segfault。03-zram.shzram swap内存一半。这个脚本踩了三个坑。第一个坑是用awk {print $2}算 disksize但 recovery 的 PATH 里没有 awk直接报awk: not found算失败disksize 变成 0后面 mkswap 和 swapon 全失败。第二个坑是改用纯 shell$(( MemTotal * 1024 / 2 ))但 shell 是 32 位整数2855084 乘以 1024 除以 2 等于 1461795328超过 2^31 就变负数了写负的 disksize 失败。第三个坑是grep MemTotal /proc/meminfo输出是MemTotal: 2855084 kB带 kB 单位tr -d 只删空格没删 kB变成bad number 2855084kB。修法是分步算KB * 512这样中间结果不会溢出同时用tr -d kB去掉单位。验证下来 swap 1427540 kBpriority 100。这个坑挺有代表性写 recovery 脚本要考虑三件事PATH 里有什么命令、shell 是 32 位会不会溢出、/proc 输出带不带单位。踩的坑除了上面说的还有几个。wpa_cli 会 segfault这是最烦的坑。wpa_cli reassociate和wpa_cli status在 hybrid 里偶尔会 segfaultstderr 输出 Aborted用21都吞不掉会直接进钩子日志刷屏。修法是钩子里只跑一次 reassociate不循环runner 加grep -v ^Aborted过滤。后来还试过用 wpa_cli action 脚本做事件驱动也不可靠因为 wpa_cli 本身会 segfault。最终的稳定方案是轮询ip link。教训是 wpa_cli 在 hybrid 里任何形式reassociate、action、status都会 segfault唯一稳定的方案是轮询ip link。wpa_supplicant 不会自动关联去掉 wpa_cli 之后发现 wpa_supplicant 不会自动关联状态一直是 DISCONNECTED。所以还是要加回timeout 3 wpa_cli reassociate一次触发。IP 配置拆到独立钩子02-wifi.sh 里原本有 IP 配置但 wpa_cli 循环会 abortIP 配置没跑到。所以拆到 50-static-ip.sh不依赖 wpa_cli。不要照搬 Magisk 模块Magisk 模块开头都有while [ $(getprop sys.boot_completed) ! 1 ]在 recovery 里会死循环因为 recovery 没有框架sys.boot_completed永远不变成 1。所以照搬的时候要去掉这些循环。最终验证重启什么都不做等 120 秒ssh meilan-phone1就能连上uptime 显示 1 分钟load average 9.56。完整状态fuelgauged 正常capacity 是 100WiFi 正常LOWER_UP 加 COMPLETEDchroot 文件系统挂好了supervisord 里 cpa、freellmapi、sshd、batt 全 RUNNINGsshd 能连静态 IP 和路由正常无线 adb 能连省电调度正常cpu4-7 睡死日志干净0 个 Aborted。外网也通ping 223.5.5.5 是 39ms。部署刷镜像只刷 recovery 分区bashadb push hybrid-hooked.img /data/local/tmp/ adb shell su -c dd if/data/local/tmp/hybrid-hooked.img of/dev/block/mmcblk0p1; sync放钩子脚本bashadb push 01-fuelgauged.sh /data/local/tmp/twrp-hooks/ adb push 02-wifi.sh /data/local/tmp/twrp-hooks/ adb push 03-zram.sh /data/local/tmp/twrp-hooks/ adb push 20-chroot-mount.sh /data/local/tmp/twrp-hooks/ adb push 30-chroot-services.sh /data/local/tmp/twrp-hooks/ adb push 40-adbd-tcp.sh /data/local/tmp/twrp-hooks/ adb push 50-static-ip.sh /data/local/tmp/twrp-hooks/ adb shell chmod 755 /data/local/tmp/twrp-hooks/*.sh重启进 recovery等 120 秒ssh meilan-phone1就行。几个额外实验跑通之后又做了几个实验记录一下。冻结 TWRP UIkill -STOP $(pidof recovery)可以冻结 TWRP UI屏幕冻结触屏无响应但后台服务全不受影响。kill -CONT $(pidof recovery)可以恢复。试过 kill recoveryinit 会立刻重启它PID 变了但进程又回来。chmod -x 也没用init 以 root 跑忽略权限。所以 recovery 不能 kill 掉它是 class default 服务on boot 时 class_start default 拉起它死掉会自动重启。想真正禁用 recovery UI 要改 init.rc重打包 hybrid 镜像但风险高36MB 内存换可能不稳定的 recovery 环境不划算。屏幕控制echo 0 /sys/class/leds/lcd-backlight/brightness可以关屏echo 255可以开屏中间亮度 0-255 随便调。TWRP 不覆盖手动设置的值关屏后不会自动开屏。关屏实测温度降 0.6 度CPU ticks 减少但省电效果有限主要省屏幕功耗。试过echo 1 /sys/class/graphics/fb0/blank走 fbdev blank 接口写不进去TWRP 不响应。只能靠背光控制。U盘挂载插 U 盘能识别/dev/block/sda和sda1120GBvfat 格式。挂载mount -t vfat -o rw /dev/block/sda1 /mnt/usb就行。中文目录名会乱码挂载时加-o utf8可以解决。8 核压测空闲时 cpu0 是 1 核负载约 0温度 35°C。压测时 cpu0-7 全开loadavg 14.93温度 76.5°C小核 1GHz 加大核 1.8GHz。峰值 8 核满载loadavg 15.98温度 82.7°C。停后 10 秒回到 cpu0温度降到 42.5°C。之前误判过要手动唤醒 cpu1-7实际上 HPS 根据负载自动开核。手动唤醒的问题在于 HPS 主线程会顶回去关核循环唤醒对抗 HPS 是错的做法。正确做法是给足够负载让 HPS 自己开核。压测脚本很简单bashfor i in 0 1 2 3 4 5 6 7; do (while :; do :; done) /dev/null 21 done这验证了 TWRP 环境下 8 核全功能不只是小核。framebuffer 直接绘制fb0 参数是virtual_size 1088x3840bpp 32stride 4352可视分辨率 1080x1920。recovery 会 mmap fb0 持续重绘所以必须先kill -STOP recovery冻结才能写入不被覆盖。用 Python 生成 RGB565 转 32bpp 的数据画三个三角形红绿蓝推到设备冻结 recoverycat /tmp/fb_tri.rgb /dev/graphics/fb0恢复 recovery。实测屏幕显示了三个三角形加黑底加顶部灰条加白色边框恢复后 recovery 状态正常。关键点是 fb0 是线性 framebufferoffset 是y*stride x*4MTK 的 fbdev 是 BGRArecovery 会重绘必须先冻结double buffer 虚拟 1088x3840 里 1920 是 back buffer。这说明图形栈完全可控不需要 Android 框架任意图像都可以显示。最轻桌面Xorg jwmchroot 里装 Xorg 加 jwm 加 xterm--no-install-recommends省空间总共约 20MB。xorg.conf 用 fbdev 驱动指向/dev/graphics/fb0DefaultDepth 24Modes 1080x1920。启动脚本后台跑 Xorg等 5 秒后台跑 jwm等 2 秒后台跑 xterm。实测屏幕显示 jwm 桌面深灰背景顶部任务栏xterm 窗口。Xorg 占 fb0写 jwm 深灰背景。这说明 fb0 可以直接给 Xorg 用不需要 Android 框架最轻桌面框架跑起来了。远程桌面x11vnc背景是手机爆屏触屏碎了需要远程桌面。scrcpy 用不了因为它需要 Android 框架SurfaceFlinger 加 MediaProjectionTWRP 没框架连不上。爆屏本身不影响 scrcpy因为 scrcpy 是软件镜像不走物理屏但 TWRP 环境没框架。解决方案是 x11vnc 加 SSH 隧道加 vncviewer。x11vnc 装了之后启动命令是x11vnc -display :0 -nopw -noshm -forever -listen 0.0.0.0 -rfbport 5900 -noxdamage。这里踩了两个坑。一个是-nopass是无效选项正确的是-nopw。另一个是shmget: Function not implemented内核没 SYSV IPC 共享内存要加-noshm禁用 shm。SSH 隧道是ssh -f -N -L 5900:localhost:5900 meilan-phone1把本地 5900 转发到设备的 5900。电脑上用 vncviewer 连localhost:5900-SecurityTypes None无密码。实测设备上 Xorg、jwm、xterm、x11vnc 全跑电脑上 vncviewer 连上显示 jwm 桌面加 xterm 窗口。vncviewer 鼠标点击等于设备触屏键盘输入等于设备键盘。这套方案适合爆屏手机、无头服务器、远程协助。反思这次开发犯了不少错记录一下。反复在同一坑改。wpa_cli 改了五版setsid、、timeout、reassociate 一次、无 wpa_cli每次都以为这次对了其实还在同一个坑。该查资料总结的时候不要瞎试。瞎改 supervisord。我瞎加了 rm socket、killall、、重定向应该照抄验证过的原文。教训是照抄面具原文不要瞎加。不该跑诊断命令干扰系统。我跑wpa_cli status和ip addr诊断时干扰了系统状态。教训是不要跑诊断命令干扰系统直接测最终结果SSH、ping。总结hybrid recovery 加钩子等于 normal 内核驱动全加 TWRP ramdisk调度好加六个钩子脚本fuelgauged、wifi、chroot-mount、chroot-services、adbd-tcp、static-ip加 runner 过滤 Aborted。全自动化重启什么都不做等 120 秒ssh meilan-phone1即可。关键踩坑fuelgauged 用不用 setsidwpa_supplicant 是 64 位要显式设 lib64wpa_cli 会 segfault 要用 timeout 兜住只跑一次 runner 过滤 Abortedwpa_supplicant 不会自动关联需要 reassociate 一次触发IP 配置拆成独立钩子轮询ip link不用 wpa_clisupervisord 照抄面具原文不要瞎加ip命令也要显式设 lib64zram 有 64 位溢出、recovery 没 awk、/proc/meminfo 带单位三个坑。额外实验验证了 TWRP UI 可以冻结但不能 kill背光可以控制但 fb0/blank 写不进去U 盘能挂载但中文目录要加 utf88 核压测 HPS 自动开核不需要手动唤醒fb0 可以直接绘制图像Xorg 加 jwm 最轻桌面能跑x11vnc 可以做爆屏手机的远程桌面。