已开启
lock #31
wangdi创建于  8月25日
wangdi成员
8月25日 创建

lock

likedislike
wangdi成员
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);
likedislike
wangdi成员
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)作为显示状态机的唯一所有者:

  1. 启动等屏:DMS Init 末尾若判断无内置屏,启动 10s 延时任务 WaitForDefaultDisplayReady(screen_session_manager.cpp:696);
  2. 无屏信号:超时后以 OnScreenChange(INVALID_SCREEN_ID, CONNECTED) 合成"无物理屏"信号(:705)。INVALID_SCREEN_ID(0xFFFF…FFFF,即 RS 侧 NONE_PHYSICAL_SCREEN_ID 语义)+ CONNECTED 是一个协议信号,不代表真实热插拔;composer 重启时 RS 亦会上报同型事件(reason=HWCDEAD),两路共用 OnScreenChange 统一入口(:1774);
  3. 虚拟屏创建:CreateScreenForBoot(:711)→ CreateDefaultVirtualScreen(:734):在 RS 创建 PCVirtualScreen(1920×1080,含 Surface 对)→ CreateZeroScreenSession(:788)建 ScreenSession 并创建 RSDisplayNode → 占位 screenSessionMap_[0]、注册 0↔rsId 映射 → 置 g_isVirtualScreenBoot=true;
  4. 上屏:经 clientProxy->OnScreenConnectionChanged IPC 通知 SCB,SCB 按白名单(PCVirtualScreen)接受连接,创建 SCB 侧 ScreenSession 并驱动 ScreenScene 上屏(screen_session_manager_client.cpp:134/1225),Display 0 对 BootAnimation/SystemUI 可见;
  5. 物理屏顶替:RS 热插拔回调 OnScreenChange(rsId, CONNECTED) → OnScreenChangeForPC(:1821)→ GetOrCreateScreenSession(:6034)建物理 session → OneScreenConnect(:813)删除虚拟 session、物理屏 session 重挂到 ID 0、复用原 displayNode,SetDisplayNodeRSScreenId 通知 SCB 换绑(:908);
  6. 拔屏回退:OneScreenDisconnect(:919)镜像执行:重建虚拟屏(复用 displayNode)顶回 ID 0,NotifyInfoChange 刷新上层;
  7. 异常自愈: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 处理线程 评估改异步或缩短等待
likedislike