已开启
lock #31
wangdi创建于 8月25日
wangdi
9月16日 评论:
9月16日 评论:
napi_set_named_property(env, objValue, "serialNumber", CreateJsValue(env, screenProperty.GetSerialNumber()));
OHOS::Rosen::ScreenProperty::GetSerialNumber*;
OHOS::Rosen::ScreenProperty::SetSerialNumber*;
void SetSerialNumber(std::string &searilanumber);
std::string GetSerialNumber() const;
std::string serialNumber_{};
void ScreenProperty::SetSerialNumber(std::string &searilanumber)
{
serialNumber_ = searilanumber;
}
std::string ScreenProperty::GetSerialNumber() const
{
return serialNumber_;
}
property_.SetSerialNumber(serialNumber);


wangdi
9月19日 评论:
9月19日 评论:
SRXXX 台式机 0 屏启动(无显示器开机 + 虚拟屏兜底与物理屏顶替)
适用形态:台式机 / 无内置屏 PC(
const.product.has_buildin_screen == 0,g_isPcDevice == true)
代码基线:window_window_manager(window_scene 统一架构)
1. 需求分析
1.1 需求背景
窗口子系统显示模型以"默认屏 Display 0"为锚点:BootAnimation、SCB(SceneBoard)、SystemUI、Launcher 及 AMS/PowerMgr 在开机阶段即依赖默认 Display 完成渲染与调度。台式机无内置屏,显示器(HDMI/DP/Type-C)可能晚于系统启动接入,甚至长时间不接入。若 DMS 一直等待物理屏,默认 Display 缺失将阻塞开机动画与桌面启动,表现为"不开机显示器就卡在 logo/黑屏"。
1.2 需求描述
台式机 0 屏启动能力,包含四个子需求:
| # | 子需求 | 说明 |
|---|---|---|
| R1 | 0 屏开机兜底 | 开机无物理屏时,DMS 自动创建虚拟屏 PCVirtualScreen(1920×1080)占位 Display 0,系统正常走完开机动画并进入桌面 |
| R2 | 物理屏无缝顶替 | 显示器插入后,物理屏顶替 Display 0(复用原 RSDisplayNode/ScreenScene),显示无感切换,不重启 SCB/桌面 |
| R3 | 拔屏回退 | 唯一物理屏拔出后,重建虚拟屏顶回 Display 0,系统不黑屏、不卡死 |
| R4 | 异常自愈 | Composer(HWC)死亡/重启后,虚拟屏与物理屏状态可自动恢复 |
1.3 需求设计约束
- 等屏窗口:为兼顾慢速显示器协商,DMS 启动后等待物理屏 10s(
WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT_MS),超时才创建虚拟屏;期间物理屏接入则直接走物理屏流程; - ID 稳定:虚拟屏固定占用逻辑屏 ID 0(
SCREEN_ID_DEFAULT),对上层始终表现为"内置主屏",顶替/回退过程中 Display 0 身份不消失; - 一次性:虚拟屏兜底只建一次(
g_isVirtualScreenBoot标志),重复触发(定时器与热插入竞态、事件重放)不重复创建;创建前再向 RS 查询一次确认无屏(HasRealScreenConnect); - 链路复用:兜底虚拟屏与物理屏共用 ScreenSession → RSDisplayNode → SCB ScreenScene 同一套上屏链路,不新增旁路;
- 形态限定:仅无内置屏设备生效(
HasInternalScreen()==false),有内置屏设备(手机/平板/折叠)不进入该逻辑。
1.4 需求依赖
| 依赖项 | 依赖内容 |
|---|---|
| RenderService(graphic_2d) | 物理屏热插拔回调上报(SetScreenChangeCallback,含 composer 异常 HWCDEAD 事件);虚拟屏创建/销毁(CreateVirtualScreen / RemoveVirtualScreen);GetDefaultScreenId() 无屏时返回 INVALID_SCREEN_ID |
| HDI Composer | 显示器热插拔事件、composer 死亡/重启事件 |
| SCB(SceneBoard) | 接收 OnScreenConnectionChanged 创建/销毁 ScreenScene;SetDisplayNodeRSScreenId 支持 displayNode 换绑 |
| 系统参数 | const.product.has_buildin_screen(形态判别)、persist.sceneboard.ispcmode、XML externalScreenDefaultMode |
| Surface(图形缓冲) | 虚拟屏 Consumer/Producer Surface(渲染产物落虚拟 Surface,无物理输出) |
1.5 需求验收说明
| # | 验收项 | 预期 |
|---|---|---|
| V1 | 不接显示器开机 | 开机动画/桌面正常启动(Display 0 = 虚拟屏 1920×1080),无阻塞、无 crash |
| V2 | 开机 10s 内接入显示器 | 不创建虚拟屏,物理屏直接作为 Display 0 |
| V3 | 虚拟屏使用中接入显示器 | Display 0 切换为物理屏真实分辨率,无黑屏闪烁、桌面不重建 |
| V4 | 使用中拔掉唯一显示器 | Display 0 回退为虚拟屏,系统持续可用(可远程/盲操作) |
| V5 | 反复插拔/Composer 重启 | 无重复虚拟屏、无残留 session、无空指针 crash |
| V6 | 有内置屏设备 | 行为不变(回归) |
1.6 涉及规格约束(UX、性能、功耗及产品差异规格)
- UX:虚拟屏规格固定 1920×1080 / 物理尺寸 530×290mm(约 23.5″ 16:9)/ VPR=1.0(160dpi);插拔切换要求显示连续,桌面图标布局不因 Display 0 消失而重排;
- 性能:10s 超时为硬编码,超时后建屏 + SCB 上屏应在百毫秒级完成;顶替路径复用 displayNode,避免整棵 UI 树重建;
- 功耗:无屏时虚拟屏仍参与合成(渲染写入虚拟 Surface),功耗由电源域亮灭屏策略另行控制,本需求不额外处理;
- 产品差异:仅台式机/无内置屏 PC 形态生效;
soc.boot.mode == rescue(救援模式)下 DMS 不加 watchdog,兜底逻辑不受影响。
2. 竞品分析
2.1 竞品表现
| 竞品 | 无显示器开机表现 |
|---|---|
| Windows 10/11 | 主机先启动、显示器后开均正常进入桌面;无显示器时以基本显示适配器 + 默认分辨率维持桌面,插屏即亮 |
| Linux(GNOME/Wayland) | 合成器支持 headless/虚拟输出,无 HDMI 输出时桌面会话仍可建立(常用于远程/无头场景) |
| macOS | 无显示器时以虚拟输出维持桌面(远程管理场景依赖此能力),插屏即恢复 |
| Android | 均有内置屏,无同类场景;其 Cast/虚拟投屏(VirtualDisplay)机制与本方案虚拟屏技术同源 |
2.2 竞品实现方案
- 通用思路:合成器/显示服务层维护一个"默认虚拟输出"作为兜底,真实输出就绪后热切换,上层会话不感知输出设备更替;
- Windows:WDDM 在无物理目标时保留桌面合成目标(HEADLESS / Microsoft Basic Display),插屏后 enumeration 热更新;
- Wayland:合成器自带 headless backend,输出热插拔通过 wl_output 动态增删;
- 本方案对齐点:与竞品一致采用"虚拟输出占位 + 热插拔顶替"模型;差异点在于 OpenHarmony 以逻辑屏 ID 0 固定占位 + RSDisplayNode 复用换绑保证 Display 0 连续性,且限定无内置屏形态。
3. 需求功能设计
3.1 功能设计方案
总体模型:虚拟屏占位 → 物理屏顶替(双向对称)
DMS(ScreenSessionManager)作为显示状态机的唯一所有者:
- 启动等屏:DMS
Init末尾若判断无内置屏,启动 10s 延时任务WaitForDefaultDisplayReady(screen_session_manager.cpp:696); - 无屏信号:超时后以
OnScreenChange(INVALID_SCREEN_ID, CONNECTED)合成"无物理屏"信号(:705)。INVALID_SCREEN_ID(0xFFFF…FFFF,即 RS 侧 NONE_PHYSICAL_SCREEN_ID 语义)+ CONNECTED 是一个协议信号,不代表真实热插拔;composer 重启时 RS 亦会上报同型事件(reason=HWCDEAD),两路共用OnScreenChange统一入口(:1774); - 虚拟屏创建:
CreateScreenForBoot(:711)→CreateDefaultVirtualScreen(:734):在 RS 创建PCVirtualScreen(1920×1080,含 Surface 对)→CreateZeroScreenSession(:788)建 ScreenSession 并创建 RSDisplayNode → 占位screenSessionMap_[0]、注册 0↔rsId 映射 → 置g_isVirtualScreenBoot=true; - 上屏:经
clientProxy->OnScreenConnectionChangedIPC 通知 SCB,SCB 按白名单(PCVirtualScreen)接受连接,创建 SCB 侧 ScreenSession 并驱动 ScreenScene 上屏(screen_session_manager_client.cpp:134/1225),Display 0 对 BootAnimation/SystemUI 可见; - 物理屏顶替:RS 热插拔回调
OnScreenChange(rsId, CONNECTED)→OnScreenChangeForPC(:1821)→GetOrCreateScreenSession(:6034)建物理 session →OneScreenConnect(:813)删除虚拟 session、物理屏 session 重挂到 ID 0、复用原 displayNode,SetDisplayNodeRSScreenId通知 SCB 换绑(:908); - 拔屏回退:
OneScreenDisconnect(:919)镜像执行:重建虚拟屏(复用 displayNode)顶回 ID 0,NotifyInfoChange刷新上层; - 异常自愈:
INVALID + CONNECTED + HWCDEAD→ClearAllVirtualScreens+ 状态复位 + 重建(:1787);composer 死亡风暴(DISCONNECTED+HWCDEAD)直接忽略。
状态机:
stateDiagram-v2
[*] --> WaitPhysicalScreen: DMS启动(无内置屏),10s定时器
WaitPhysicalScreen --> PhysicalScreen: 10s内物理屏接入
WaitPhysicalScreen --> VirtualScreenBoot: 超时,创建PCVirtualScreen占位Display 0
VirtualScreenBoot --> PhysicalScreen: 物理屏插入,顶替Display 0
PhysicalScreen --> VirtualScreenBoot: 唯一物理屏拔出,回退
VirtualScreenBoot --> VirtualScreenBoot: Composer重启,清空重建
关键数据:
| 项 | 值 | 代码位置 |
|---|---|---|
| 虚拟屏名称/规格 | PCVirtualScreen / 1920×1080 / 530×290mm |
screen_session_manager.cpp:179-182 |
| 等屏超时 | 10000ms | :184 |
| 占位逻辑 ID | SCREEN_ID_DEFAULT = 0 |
:132 |
| 一次性标志 | g_isVirtualScreenBoot |
:168 |
| 屏幕三张表 | screenSessionMap_ / physicalScreenSessionMap_ / phyScreenPropMap_ |
:766-784 |
3.2 功能时序图
主时序:0 屏开机兜底 → 物理屏插入顶替
sequenceDiagram
autonumber
participant RS as RenderService
participant DMS as DMS(ScreenSessionManager)
participant SCB as SCB
participant App as 应用/开机动画
Note over DMS: 开机启动,注册RS热插拔回调
DMS->>DMS: 无内置屏,启动10s等屏定时器
alt 10s内物理屏接入
RS-->>DMS: 物理屏CONNECTED(真实rsId)
DMS->>DMS: 取消定时器,走物理屏常规流程
else 10s超时无屏
DMS->>DMS: 触发CreateScreenForBoot
DMS->>RS: CreateVirtualScreen("PCVirtualScreen",1920x1080)
DMS->>DMS: 建ScreenSession+DisplayNode,占位Display 0
DMS->>SCB: OnScreenConnectionChanged(CONNECTED)
SCB->>SCB: 创建ScreenSession/ScreenScene上屏
Note over App: Display 0就绪,开机动画/桌面正常启动
App->>DMS: GetDefaultDisplay(0)
DMS-->>App: 虚拟屏DisplayInfo(1920x1080)
end
Note over RS: 显示器随后插入
RS-->>DMS: 物理屏CONNECTED(真实rsId)
DMS->>DMS: 建物理session,删虚拟屏
DMS->>DMS: 物理屏顶替Display 0(复用DisplayNode)
DMS->>SCB: SetDisplayNodeRSScreenId换绑+连接通知
DMS->>App: Display 0切换为物理屏,显示无感
回退与异常:拔屏回退 / Composer 重启自愈
sequenceDiagram
autonumber
participant RS as RenderService
participant DMS as DMS(ScreenSessionManager)
participant SCB as SCB
alt 唯一物理屏拔出
RS-->>DMS: 物理屏DISCONNECTED
DMS->>RS: 重建PCVirtualScreen(复用DisplayNode)
DMS->>SCB: displayNode换绑+属性变更通知
Note over DMS: 虚拟屏顶回Display 0,系统持续可用
else Composer死亡→重启
RS-->>DMS: 各屏DISCONNECTED(HWCDEAD,忽略)
RS-->>DMS: INVALID+CONNECTED(HWCDEAD)
DMS->>DMS: 清空全部虚拟屏,状态复位
DMS->>RS: 重建PCVirtualScreen占位Display 0
end
3.3 接口设计
(1)DMS ↔ RS(RSInterfaces,外部依赖接口)
| 接口 | 方向 | 说明 |
|---|---|---|
SetScreenChangeCallback(cb) |
DMS→RS | 注册屏幕热插拔回调(CONNECTED/DISCONNECTED + reason:DEFAULT/HWCDEAD),失败 50ms 重试 |
CreateVirtualScreen(name, w, h, surface, ...) |
DMS→RS | 创建虚拟屏,返回 rsScreenId;0 屏场景 name 固定 PCVirtualScreen |
RemoveVirtualScreen(rsId) |
DMS→RS | 销毁虚拟屏(顶替/清空时) |
GetDefaultScreenId() |
DMS→RS | 查询首个已接入物理屏 ID;无屏返回 INVALID_SCREEN_ID,作为兜底前二次确认依据 |
(2)DMS 内部关键接口(screen_session_manager.cpp)
| 接口 | 签名要点 | 职责 |
|---|---|---|
OnScreenChange |
(ScreenId, ScreenEvent, ScreenChangeReason, token) |
统一分发入口:识别 INVALID+CONNECTED 协议信号(DEFAULT 首次兜底 / HWCDEAD 自愈)、过滤死亡风暴、按 g_isPcDevice 分流 OnScreenChangeForPC/Default(:1774) |
WaitForDefaultDisplayReady |
() |
无内置屏时挂 10s 延时任务;任何真实屏幕事件到达即取消(:696) |
CreateScreenForBoot |
() |
兜底总入口:查 RS 确认无屏 → 建虚拟屏 → 通知 SCB → 置位一次性标志(:711) |
CreateDefaultVirtualScreen |
(reason) |
Surface 对 + RS 虚拟屏 + InitScreenProperty 属性 + CreateZeroScreenSession + 三表注册(:734) |
GetOrCreateScreenSession |
(screenId, token) |
物理屏 session 创建/复用:属性初始化(含无内置屏专属原始分辨率)、VPR/ROG/HDR 等(:6034) |
OneScreenConnect / OneScreenDisconnect |
(session, screenId, ...) |
顶替/回退对称逻辑:ID 0 迁移、displayNode 复用、RS 虚拟屏清理/重建(:813/:919) |
ClearAllVirtualScreens |
() |
遍历销毁所有 VIRTUAL 类型 session(HWCDEAD 用,:1759) |
(3)DMS ↔ SCB(IScreenSessionManagerClient,IPC)
| 接口 | 方向 | 说明 |
|---|---|---|
SetClient(client) |
SCB→DMS | SCB 启动时注册客户端代理(screen_session_manager_client.cpp:63) |
OnScreenConnectionChanged(SessionOption, ScreenEvent) |
DMS→SCB | 屏幕(含 PCVirtualScreen)连接/断开通知;SessionOption 携带 rsId、名称、isBooting_ 等;SCB 白名单放行 PCVirtualScreen(:109-132) |
SetDisplayNodeRSScreenId(screenId, rsId) |
DMS→SCB | 顶替/回退时 displayNode 换绑到新 rsScreenId(client 侧 :571) |
(4)DMS ↔ 应用(DisplayManagerAgent,IPC)
| 通知 | Agent 类型 | 终点 |
|---|---|---|
OnDisplayCreate |
DISPLAY_EVENT_LISTENER | 应用 display_manager(display add) |
OnScreenConnect |
SCREEN_EVENT_LISTENER | screen_manager 客户端 |
OnDisplayChange / OnScreenChange |
同上 | display/screen change 回调 |
4. 测试用例设计
4.1 功能测试用例设计
| 用例编号 | 前置条件 | 操作 | 预期结果 | 对应验收 |
|---|---|---|---|---|
| TC01 | 台式机,不接显示器开机 | 开机观察 | 开机动画正常播放并进入桌面,Display 0 为 1920×1080 虚拟屏 | V1 |
| TC02 | 台式机,开机前接好显示器 | 开机观察 | 不创建虚拟屏(日志无 PCVirtualScreen),物理屏直接为 Display 0 | V2 |
| TC03 | 系统运行于虚拟屏(TC01 后) | 插入 HDMI/DP 显示器 | Display 0 切为物理分辨率,无黑屏闪烁、桌面不重建 | V3 |
| TC04 | 物理屏使用中 | 拔掉唯一显示器 | Display 0 回退虚拟屏,系统持续响应(hdcd/远程可控) | V4 |
| TC05 | 虚拟屏使用中 | 10s 定时器触发前后 1s 内插入显示器(竞态) | 只存在一个 Display 0,无重复建屏/残留 | V5 |
| TC06 | 物理屏使用中 | kill composer 进程使其重启 | 虚拟屏重建占位,物理屏恢复后重新顶替,无 crash | V5 |
| TC07 | 反复插拔 10+ 次 | 交替插拔 | session/映射表无泄漏(hilog 无 not found 报错堆积) | V5 |
| TC08 | 手机/折叠(有内置屏) | 开机回归 | 不进入 0 屏逻辑,行为与基线一致 | V6 |
单元测试(已有,screen_session_manager_test_five.cpp):
WaitForDefaultDisplayReady01(:171):有内置屏短路、已建虚拟屏不重建、DEFAULT/HWCDEAD 信号路径;OnScreenChange01(:200):INVALID+CONNECTED与g_isVirtualScreenBoot组合的幂等性;OnScreenChange02(:225):物理屏 CONNECTED/DISCONNECTED、HWCDEAD 各 reason 组合。
5. 遗留问题
| # | 问题 | 影响 | 建议 |
|---|---|---|---|
| 1 | 虚拟屏 VPR 恒为 1.0(CreateDefaultVirtualScreen 不走 SetupScreenDensityProperties) |
0 屏期间 Display 0 固定 160dpi,与物理屏 PPI 推荐档位机制不一致 | 虚拟屏按物理尺寸 530×290mm 计算默认密度 |
| 2 | 10s 等屏超时硬编码 | 慢显示器(>10s)先建虚拟屏再顶替,多一次建/删抖动 | 超时时间配置化(XML/系统参数) |
| 3 | CreateScreenForBoot 中 SCB 未注册(clientProxy==nullptr)时仍置 g_isVirtualScreenBoot=true 但跳过连接通知 |
存在 SCB 侧未建 ScreenScene 的窗口,依赖 SCB 重连全量同步补建 | 记录待补状态,SCB SetClient 时重放连接通知 |
| 4 | OnScreenChangeForPC 中 GetScreenSessionByRsId 结果未判空即解引用(:1838-1839) |
DISCONNECTED 且 session 已删场景有空指针 crash 风险 | 增加判空保护 |
| 5 | PC 连接路径 WaitUpdateAvailableAreaForPc 同步等待 CV |
SCB 可用区域回执慢时拖住 DMS 处理线程 | 评估改异步或缩短等待 |


lock