☰
RecoveryUI 圆角屏安全边距配置及代码调用链
2026/10/1 15:22:17 网站建设 项目流程

1. 背景与现象

在圆角屏设备上,Recovery 菜单和日志区域可能延伸到面板四角的不可视区域。例如,屏幕上沿的菜单首行、左上角提示文字,或底部的错误/启动日志会被圆角遮挡。

Recovery 使用 framebuffer 直接绘制界面,不会像 Android 普通应用的 Window/DecorView 那样自动获得系统窗口 Insets。Recovery UI 因此提供了自己的水平、垂直 margin:

  • margin_width_:左右安全边距。
  • margin_height_:顶部和底部安全边距。

Recovery 源码头文件对这两个成员的注释就是:

// The margin that we don't want to use for showing texts // (e.g. round screen, or screen with rounded corners). const int margin_width_; const int margin_height_;

定义位于 VENDOR RecoveryUI screen_ui.h。因此这两个配置就是为圆屏/圆角屏上的文字安全区域准备的。

2. 总体调用链

属性未定义时作为 fallback

BoardConfig 设置 TARGET_RECOVERY_UI_MARGIN_*

Makefile 生成 Recovery prop.default

Recovery ramdisk 提供 /prop.default

init 在 Recovery 模式加载 /prop.default

ScreenRecoveryUI 构造函数读取 ro.recovery.ui.margin_*

保存为 margin_width_ 和 margin_height_

菜单/标题/日志绘制函数计算坐标

kDefaultMarginWidth/Height

涉及的主要源码如下:

  • 构造函数和默认值:screen_ui.cpp
  • 成员声明及圆角屏用途注释:screen_ui.h
  • Recovery 构建变量转属性:Makefile
  • Recovery ramdisk 的default.prop链接:Makefile
  • Recovery 模式加载/prop.default:property_service.cpp
  • 菜单和底部日志绘制:screen_ui.cpp
  • 文本显示行列数:screen_ui.cpp

3. 属性读取:三种设置最终汇合的位置

ScreenRecoveryUI构造时执行以下初始化:

constexpr int kDefaultMarginHeight = 0; constexpr int kDefaultMarginWidth = 0; ScreenRecoveryUI::ScreenRecoveryUI(bool scrollable_menu) : margin_width_( android::base::GetIntProperty("ro.recovery.ui.margin_width", kDefaultMarginWidth)), margin_height_( android::base::GetIntProperty("ro.recovery.ui.margin_height", kDefaultMarginHeight)), ...

源码位置:screen_ui.cpp

关键点是GetIntProperty(property, default):

  1. 如果对应属性已经定义,使用属性值。
  2. 如果属性没有定义,使用第二个参数作为默认值,也就是kDefaultMarginWidth或kDefaultMarginHeight。
  3. 读取发生在ScreenRecoveryUI对象构造时,结果保存到const int margin_width_和const int margin_height_。
  4. 绘制时使用这些成员,不会每一帧重新读取属性。

因此三种方式会生效,是因为它们分别改变了同一个初始化过程的输入:

配置方式改变的内容何时生效
改kDefaultMarginWidth/Height属性不存在时使用的编译期 fallback编译并部署包含新 RecoveryUI 代码的镜像后
设置ro.recovery.ui.margin_width/height构造函数实际读取的属性值属性在 RecoveryUI 构造前已加载时
设置TARGET_RECOVERY_UI_MARGIN_WIDTH/HEIGHT构建时生成到 Recoveryprop.default的属性值使用包含该配置的 Recovery ramdisk 启动后

配置优先级

通常可以按以下优先关系理解:

Recovery 启动时加载到的 ro.recovery.ui.margin_* 属性 优先于 kDefaultMarginWidth / kDefaultMarginHeight 编译期默认值

例如,代码默认值是 0,但 Recovery 的/prop.default已包含:

ro.recovery.ui.margin_height=40 ro.recovery.ui.margin_width=3

那么构造函数得到的是 40 和 3,而不是 0。反过来,如果没有这些属性,构造函数就采用kDefaultMargin*。

由于 margin 是构造时读取且保存在成员变量中的,Recovery UI 创建以后再修改属性不会自动触发重新布局。要让运行中的 UI 更新,必须重启到使用新属性/代码的 Recovery。

4. 三种配置方式详解

4.1 方式一:修改screen_ui.cpp的默认值

示例:

constexpr int kDefaultMarginWidth = 3; constexpr int kDefaultMarginHeight = 40;

这是直接改变GetIntProperty()的 fallback。它适合把某个 RecoveryUI 实现的默认布局永久改掉。

**作用条件:**对应的ro.recovery.ui.margin_width或ro.recovery.ui.margin_height没有定义。属性如果存在,属性值覆盖这个默认值。

**生效步骤:**编译 Recovery 相关目标,并确保最终刷入或打包的镜像包含新的librecovery_ui/Recovery ramdisk。

**特点:**只改 C++ 默认值,不需要属性构建配置;但设备或产品若提供了同名属性,改默认值可能看不到效果。

4.2 方式二:设置 Recovery 专用只读属性

属性名是:

ro.recovery.ui.margin_width ro.recovery.ui.margin_height

示例值:

ro.recovery.ui.margin_width=3 ro.recovery.ui.margin_height=40

属性必须在 Recovery UI 构造之前可用。可靠做法是把它们放进 Recovery ramdisk 使用的属性文件/prop.default,而不是只写普通 Android 系统启动时使用的某个build.prop。

在 Recovery 模式下,init 的PropertyLoadBootDefaults()会加载/prop.default,见 property_service.cpp。Recovery ramdisk 构建过程中default.prop链接到prop.default,见 Makefile。

**特点:**属性值直接进入 Recovery UI 的初始化;适合把配置和 Recovery 的启动属性关联起来。ro.*是只读属性,而且 Recovery UI 只在构造时读取,所以不要依赖进入界面后再用setprop改值。

4.3 方式三:设置TARGET_RECOVERY_UI_MARGIN_*构建变量

在产品实际使用的 BoardConfig 中设置:

TARGET_RECOVERY_UI_MARGIN_WIDTH := 3 TARGET_RECOVERY_UI_MARGIN_HEIGHT := 40

Recovery 构建 Makefile 中有显式映射:

TARGET_RECOVERY_UI_MARGIN_HEIGHT:margin_height TARGET_RECOVERY_UI_MARGIN_WIDTH:margin_width

并把变量名转换成属性名和值,写入 Recovery 的prop.default:

ro.recovery.ui.margin_height=<TARGET_RECOVERY_UI_MARGIN_HEIGHT 的值> ro.recovery.ui.margin_width=<TARGET_RECOVERY_UI_MARGIN_WIDTH 的值>

映射和写入函数见 VENDOR Makefile;SSI树也有对应映射,见 SSI Makefile。

这条路径把前两种方式串起来了:构建变量在编译期间生成 Recovery 属性;Recovery init 启动时读取属性;ScreenRecoveryUI构造函数读取属性并保存 margin。通常这是产品配置最清楚、最易维护的做法。

另外,TARGET_RECOVERY_UI_MARGIN_WIDTH还可能参与 Recovery 本地化图片宽度计算。若构建配置定义了TARGET_RECOVERY_UI_SCREEN_WIDTH,Makefile 会从屏幕宽度中减去 margin 和菜单缩进,计算 Recovery 文本图片宽度,见 recovery_image_width 计算。所以该构建变量除运行时 margin 属性外,还能影响部分 Recovery 资源的生成尺寸。

5. Margin 如何改变屏幕坐标

单位是Recovery framebuffer 像素,不是 dp。Recovery 使用ro.sf.lcd_density计算字体密度,但这两个 margin 作为整数直接参与屏幕坐标运算,没有经过PixelsFromDp()换算。

5.1 顶部菜单

菜单绘制函数开始时:

int y = margin_height_;

随后标题、帮助信息、菜单 header 和菜单项都继续从这个 y 坐标向下绘制。因此,margin_height_=40时,菜单内容整体从距 framebuffer 顶端 40 像素的位置起画。

菜单文字的 x 坐标是:

int x = margin_width_ + kMenuIndent;

当前kMenuIndent为 4,所以margin_width_=3时,菜单文字从 x=7 开始。

代码位置:draw_menu_and_text_buffer_locked()

5.2 底部日志和错误信息

Recovery 的文本日志从屏幕底部向上绘制:

for (int ty = ScreenHeight() - margin_height_ - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { DrawTextLine(margin_width_, ty, text_[row], false); }

因此,底部第一行日志的绘制基线会比原先向上收margin_height_像素;margin_width_则使日志文字从左边向内缩进。照片中底部的ERROR: logwrapper...属于 Recovery 文本日志绘制区域,因此上下、左右 margin 都可能影响其是否落入圆角遮挡区。

代码位置:底部文本日志循环

5.3 可用文本区域

初始化字体参数时,Recovery 根据 margin 限制可使用的最大行列:

text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_; text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;

即上下各扣除一次margin_height_,左右各扣除一次margin_width_。边距越大,可用文字行列越少;屏幕内容不会无限向中间挤而保持原有行数。

代码位置:InitTextParams()

5.4 注意:不是所有图形都按 margin 平移

这些 margin 主要保护文本布局。Recovery 代码中的居中图标、进度动画、进度条或 PCBA 彩色测试块有各自的坐标计算,不能假设它们都会按margin_width_/margin_height_一起平移。当前照片所示菜单文字和底部文本日志使用了 margin,因此适用这套配置。

6. 示例值与调试方法

曾验证的示例值是:

TARGET_RECOVERY_UI_MARGIN_WIDTH := 3 TARGET_RECOVERY_UI_MARGIN_HEIGHT := 40

按当前坐标代码,其几何效果大致是:

  • 菜单顶部起点:屏幕 y=40。
  • 底部日志绘制基线:从屏幕高度 - 40 - 字体行高开始。
  • 左侧日志起点:x=3。
  • 菜单文字起点:x=3 + 4,即 x=7。
  • 可用文本高度减少 80 px,可用文本宽度减少 6 px。

这些值是 framebuffer 像素。不同面板分辨率、Recovery framebuffer 方向和圆角半径不同,建议以实际 Recovery 图像为准逐步调整:

  1. 先加margin_height,确认顶部菜单和底部日志都离开圆角区域。
  2. 再加margin_width,确认左右首列文字安全。
  3. 检查菜单项、错误日志和长文本是否仍能显示;margin 过大可能减少可显示行数。
  4. 如果宽度 margin 很大,同时检查由它生成的本地化 Recovery 文本图片是否仍能完整显示。

7. SSI 与 VENDOR 源码树

当前工作区存在两份 Android 源码树:SSI和VENDOR。本次核对发现两份recovery_ui/screen_ui.cpp中以下逻辑一致:

  • kDefaultMarginHeight和kDefaultMarginWidth默认值。
  • GetIntProperty()对属性和默认值的读取。
  • 菜单顶部、底部日志和可用行列对 margin 的使用。

两份 build/make Makefile 也都包含TARGET_RECOVERY_UI_MARGIN_*到ro.recovery.ui.margin_*的映射。修改要真正生效,必须修改当前产品构建脚本实际使用的源码树和产品 BoardConfig;修改未参与本次构建的另一份树不会进入设备镜像。排查时可检查 build 命令的工作目录、TARGET_PRODUCT/TARGET_DEVICE,以及生成的 Recoveryprop.default中是否有目标属性。

8. 推荐配置与验证

对于产品级的圆角屏适配,建议优先在实际产品的 BoardConfig 设置构建变量,而不是只改 C++ 默认值:

TARGET_RECOVERY_UI_MARGIN_WIDTH := 3 TARGET_RECOVERY_UI_MARGIN_HEIGHT := 40

这样构建系统会自动生成相应 Recovery 属性,不必手工维护 C++ fallback 和属性文件两套配置。以上仅为已测试值示例;若当前面板要求更多安全区,按实际遮挡范围调整。

构建后可检查 Recovery ramdisk 中生成的prop.default是否包含:

ro.recovery.ui.margin_width=3 ro.recovery.ui.margin_height=40

如果值没有出现,通常表示变量没有进入实际产品构建配置,或这次生成的镜像没有使用预期的 BoardConfig/源码树。确认属性进入 Recovery ramdisk 后,再启动 Recovery 检查菜单顶部、底部错误日志和左右文本是否都避开圆角。

9. 配置作用范围小结

修改 constexpr 默认值 -> 只改变属性缺失时的 fallback 设置 ro.recovery.ui.margin_* -> 直接改变 RecoveryUI 构造时读到的 margin 设置 TARGET_RECOVERY_UI_MARGIN_* -> 构建生成 ro.recovery.ui.margin_* 到 Recovery prop.default -> Recovery init 加载属性 -> RecoveryUI 构造时读取并用于绘制

三种方式都能生效,是因为最终都影响同一对成员变量;差别在于配置发生在 C++ 编译期默认值、Recovery 启动属性,还是产品构建生成属性的阶段。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询