• 投稿

一个模块即可实现开机实时打印内核日志动画

2335
1
2026-10-19 02:00:04
75
6
54
2
原理总结 1. 启动链路 KSU post-fs-data.sh └─ detached start.sh(不阻塞开机) ├─ 等 surfaceflinger 进程出现 ├─ 从 /sys/class/drm/card0-DSI-1/modes 读 1440x3200 └─ app_process + klog.dex 启动 KLogTop(uid=0) service.sh 再执行一次 start.sh 作为兜底(幂等,按 pid 判断) 不安装 APK,也不经过 ActivityManager/WindowManager。app_process 直接启动 ART 和 main(),因此在 Zygote 之后、系统 UI 还没完全起来时就能运行。早期阶段不需要 system_server,只依赖 SurfaceFlinger、/dev/kmsg 和 DRM 面板模式。 2. 为什么能显示在最顶层 KLogTop 用 SurfaceControl.Builder 创建一个 BLAST Layer,然后通过 SurfaceControl.Transaction: - setLayer(INT_MAX):放到最高 Z 序 - setLayerStack(0):挂到主显示 - show() / apply():提交给 SurfaceFlinger 这个 Layer 没有父 Layer。uid 0 在 SurfaceFlinger 的 addToRoot 检查中拥有 ACCESS_SURFACE_FLINGER(PermissionCache 对 root 直接放行),因此它成为 display 的根层,位于 bootanimation、SystemUI、状态栏、导航栏之上。之前的 dumpsys 里能看到它在 Composition list 的第一个。 3. 为什么触摸能穿透 全屏 Layer 默认会被 InputDispatcher 当成“不可信遮挡层”: - 它虽然 NOT_TOUCHABLE,但 alpha=1 且覆盖全屏 - 遮挡判断用的是 Layer 的 frame,不是 touchableRegion - BLOCK_UNTRUSTED 模式下,下方窗口的触摸会被拦截 所以必须调用: transaction.setTrustedOverlay(sc, true) 加上 TRUSTED_OVERLAY 后,InputDispatcher.canBeObscuredBy() 会直接跳过它,触摸继续传给下面的应用。这个操作同样依赖 root 的 ACCESS_SURFACE_FLINGER 权限。 4. 透明背景与字体 - Layer 使用 PixelFormat.TRANSLUCENT,并设置 setOpaque(false) - 每帧用 PorterDuff.CLEAR 清空 Surface,像素缓冲区背景填 0x00000000 - 只有文字、阴影、光标和半透明状态条有 alpha,其余区域完全透明,由 SurfaceFlinger 与下层内容混合 - 文字加了 2px 黑色阴影,保证在透明背景上也可读 字体没有使用 Android Typeface(在 app_process 环境下字体系统未初始化,会抛异常甚至 native abort),而是内置了一套 public domain 8x8 点阵字库: - Base64 解码成 128 个 ASCII 字形 - 每个像素放大 4 倍(32px 字高) - 先写进 int[],再 Bitmap.setPixels(),最后一次性 drawBitmap() 到 Surface - 当前布局为 45 列 × 96 行日志 5. 内核日志来源 - 以 root 打开 /dev/kmsg,它按记录返回整个内核环形缓冲区,之后阻塞等待新日志 - 每条格式为:priority,seq,usec,flags;message - 解析出: - priority:决定颜色(错误红、警告黄、notice 青、info 白、debug 灰) - usec:格式化成 [秒.微秒] - 清除 caller=...;、ANSI 转义和控制字符,按 45 列自动换行 - 读者线程维护一个最多 96 行的环形缓冲,UI 线程在内容变化或光标闪烁时重绘
‮‮‮ ‮‮‮ ‮‮‮ ‮‮‮ ‮‮‮ ‮‮‮
客服
顶部
赛事库 课堂 2021拜年纪