SuperAPI AI Directory

ohos-dev-distributed-mmi-wms-dms-simulation

Testing & quality

View repository

Updated 10-15 21:36

About this skill

WMS/DMS simulation

The test process directly constructs display groups and windows through InputManager IPC, replacing WMS (Window Management Service) and DMS (Display Management Service), to achieve independent functional verification of the input subsystem.

Prerequisite skill: For infrastructure such as permission acquisition, server bypass, compilation and deployment, and hidumper capture, see ohos-dev-distributed-mmi-device-test-harness; for virtual device creation and event injection, see ohos-dev-distributed-mmi-uinput-virtual-device。

When to Use

  • Simulate display groups, users, primary screens, extended screens, mirrored screens, and window stacks as required by the test
  • Test input functions that depend on display group/window state (device binding, cursor routing, focus dispatch, hit testing)
  • Verify scenarios such as a single display group with multiple screens, multiple display groups with multiple users, inconsistent mirror screen ratios, and foreground user switching
  • When you do not want to or cannot start the full WMS/DMS stack

Summary of verified capabilities

DAYU200 real device dual_group_interleave_test Already covers dual display group construction, multi-window injection, per-group cursor/button/keyboard/focus/captureMode/wheel isolation, hot-plug binding cleanup, cross-binding key state, and per-window cursor appearance. For detailed steps, dump evidence, and PARTIAL items, see evals/test-results.md, do not copy long empirical test tables into SKILL.md main flow.

Scenario-First modeling flow

Do not start from a fixed demo. First normalize the user requirements into this model table, then generate UserScreenInfo and window injection:

Dimension Required content Key constraints
User userId, and whether foreground/ACTIVE When simulating only the foreground user, all target UserScreenInfo should be writable to the active display group state
DisplayGroup groupId、type、mainDisplayId、focusWindowId There must be one GROUP_DEFAULT with id=0; other groups use GROUP_SPECIAL
Display The displayId, position, size, dpi, and direction of the primary/extended/mirrored screens screenArea must be consistent with the display coordinates/size; extended screen coordinates must not unintentionally overlap
Mirror Mirror source display, mirror display, scaling ratio, or letterbox/crop policy When the mirror screen ratio is inconsistent with the primary screen, coordinate mapping must be modeled explicitly; do not assume 1:1
Screen The ScreenInfo screens corresponding to each display; the count and ids must cover all displays
Window Focus window, background window, floating window, occluding window, and hotArea for each screen win.groupId/displayId must match the target display; zOrder must reflect the stacking relationship
Focus The focus window of each display, and the group-level keyboard focus DisplayGroupInfo.focusWindowId is key to keyboard routing; the focus window of each screen must also be reflected in the window stack

When outputting the simulation plan, first provide the model table, then provide C++ helper or pseudocode. Do not just paste a single fixed SetupDualDisplayGroups()。

Standard scenario template

Single display group: primary screen + extended screen + primary screen mirror

Used to verify multi-screen routing within one user/one display group, per-screen focus windows, mirror screen coordinate mapping, and multi-window stacking:

Object Suggested modeling
group groupId=0, GROUP_DEFAULT, mainDisplayId=0
Primary screen displayId=0, screenArea={0,0,720,1280}
Extended screen displayId=1, screenArea={720,0,1920,1080}, tiled horizontally with the primary screen
Mirror screen displayId=2, screenArea={2640,0,1024,768}, mirrors the primary screen but with a different ratio
Focus window At least one focus window per display, for example 1000/1100/1200
Stacked windows Each screen's background window, status bar/system layer, and floating window use different zOrder
hotArea Both the mirror screen and the extended screen must be configured pointerHotAreas, used for hit testing

When the mirror screen has an inconsistent aspect ratio, the expected coordinate strategy must be clearly stated in the test description:

  • fit/letterbox: maintain the main screen aspect ratio; some areas of the mirror screen have no valid hotArea.
  • stretch: main screen coordinates are mapped to the mirror screen using independent X/Y scaling.
  • crop: the mirror screen only covers the cropped area of the main screen.

MMI's window hit testing only looks at the injected display/window geometry information; if you want to verify mirror coordinate conversion, the test code must convert the source screen coordinates to the mirror screen display coordinates before injecting events or verifying the target window.

Multiple display groups and multiple users: each group is main screen + extended screen + mirror screen

Used to verify display group isolation under different foreground users or parallel foreground scenarios:

Object Suggested modeling
user A group userId=100, groupId=0, GROUP_DEFAULT
user B group userId=101, groupId=10, GROUP_SPECIAL or assign a non-zero group according to the model under test
Each group contains all three display types: main/extended/mirror
displayId Globally unique; different groups cannot reuse the same displayId
windowId Globally unique; it is recommended to number them in segments by group/display
focus Each group sets a group-level focusWindowId; each display has its own hittable focus window
mirror mismatch At least one mirror screen in each group has a different aspect ratio, used to verify coordinate and hit isolation

If the underlying UserScreenInfo expresses only one user at a time, call UpdateDisplayInfoseparately for each user, and state in the report the simulated assumption that "multiple users are all foreground/ACTIVE". Do not mix windows from multiple users into the same group and then claim to have verified multi-user isolation.

Construct display groups (replacing DMS)

Below is the minimal construction snippet. A real test should generate multiple displays, screens, and groups according to the scenario model above.

#include "input_manager.h"

UserScreenInfo screenInfo;
screenInfo.userId = 100;

// Group 0: 默认组(主屏)
DisplayGroupInfo group0;
group0.id = 0;
group0.name = "default";
group0.type = GroupType::GROUP_DEFAULT;
group0.mainDisplayId = 0;
group0.focusWindowId = 1000;

DisplayInfo disp0;
disp0.id = 0;
disp0.x = 0; disp0.y = 0;
disp0.width = 720; disp0.height = 1280;
disp0.dpi = 240;
disp0.name = "main";
disp0.direction = DIRECTION0;
disp0.displayDirection = DIRECTION0;
disp0.screenArea = { .id = 0, .area = { 0, 0, 720, 1280 } };
group0.displaysInfo.push_back(disp0);

// Group 1: 特殊组(另一个显示组;单组多屏时可改为给 group0 增加 display1/display2)
DisplayGroupInfo group1;
group1.id = 1;
group1.name = "secondary";
group1.type = GroupType::GROUP_SPECIAL;
group1.mainDisplayId = 1;
group1.focusWindowId = 2000;

DisplayInfo disp1;
disp1.id = 1;
disp1.x = 0; disp1.y = 0;
disp1.width = 1920; disp1.height = 1080;
disp1.dpi = 160;
disp1.name = "secondary";
disp1.direction = DIRECTION0;
disp1.displayDirection = DIRECTION0;
disp1.screenArea = { .id = 1, .area = { 0, 0, 1920, 1080 } };
group1.displaysInfo.push_back(disp1);

// 单组多屏时,扩展屏/镜像屏也可以 push 到同一个 group:
// group0.displaysInfo.push_back(extendedDisplay);
// group0.displaysInfo.push_back(mirrorDisplay);

screenInfo.displayGroups.push_back(group0);
screenInfo.displayGroups.push_back(group1);

// 物理屏幕信息(必须与 displaysInfo 对应)
ScreenInfo screen0;
screen0.id = 0;
screen0.uniqueId = "default0";
screen0.width = 720; screen0.height = 1280;
screen0.physicalWidth = 62; screen0.physicalHeight = 110;
screen0.dpi = 240; screen0.ppi = 295;
screen0.tpDirection = DIRECTION0;
screenInfo.screens.push_back(screen0);

ScreenInfo screen1;
screen1.id = 1;
screen1.uniqueId = "secondary1";
screen1.width = 1920; screen1.height = 1080;
screen1.physicalWidth = 531; screen1.physicalHeight = 299;
screen1.dpi = 160; screen1.ppi = 92;
screen1.tpDirection = DIRECTION0;
screenInfo.screens.push_back(screen1);

InputManager::GetInstance()->UpdateDisplayInfo(screenInfo);

Key constraints

Field Constraint Consequence
group0.type There must be one GROUP_DEFAULT InitDisplayGroupInfo validating groupId==MAIN_GROUPID
group0.id The DEFAULT group id must be 0 Non-zero will be rejected
focusWindowId Must be preset in DisplayGroupInfo Keyboard event routing depends on this value; if not set, it becomes -1, causing keyboard events to fail to dispatch
screenArea Must be consistent with width/height Cursor boundary clamping depends on this value
screens count and ID Must correspond to displaysInfo Screen information is used for DPI/PPI calculation
userState Default USER_ACTIVE, no need to set When not ACTIVE, displayGroupInfoMap_ is not written
displayId Globally unique Reusing displayId across multiple groups/users will cause confusion in routing and dump interpretation
Mirror screen aspect ratio The mapping strategy must be explicitly stated Mirror screens with different aspect ratios cannot be directly asserted for hits using main screen coordinates

Return code

UpdateDisplayInfo returning -201 is normal — this is UpdateUIExtensionInfo 's error code and does not affect the writing of display group data.

Inject virtual windows (replacing WMS)

Each display needs at least one hittable window. For complex scenarios, inject each display's own background window, focus window, and stacked windows that need verification:

WindowGroupInfo wgi0;
wgi0.focusWindowId = 1000;
wgi0.displayId = 0;

WindowInfo win0;
win0.id = 1000;
win0.pid = getpid();
win0.uid = getuid();
win0.area = { 0, 0, 720, 1280 };
win0.defaultHotAreas = { { 0, 0, 720, 1280 } };
win0.pointerHotAreas = { { 0, 0, 720, 1280 } };
win0.agentWindowId = -1;
win0.flags = 0;
win0.action = WINDOW_UPDATE_ACTION::ADD;
win0.displayId = 0;
win0.groupId = 0;
win0.zOrder = 1.0f;
wgi0.windowsInfo.push_back(win0);

InputManager::GetInstance()->UpdateWindowInfo(wgi0);

Key constraints

Field Constraint
win.groupId Must match the target display group ID
win.displayId Must match the display ID within the display group
win.pid Use getpid() — required for permission checks
win.action Use ADDfor the first time, use CHANGE
wgi.focusWindowId Takes effect for MAIN_GROUPID; non-main groups are read from displayGroupInfoMap_
pointerHotAreas Mouse event hit testing depends on this area
win.id Globally unique
zOrder Explicitly distinguished by business hierarchy

Per-screen window stack pattern

For each display, it is recommended to construct at least:

Window zOrder Purpose
background/app 10 Default hit target
panel/status/system 100 Verify system-layer priority
popup/floating 200+ Verify overlapping hits and fallback after DEL

The per-screen focus window can be the background or a dedicated input window; this needs to be indicated in the model table.

Incremental window operations

ADD/CHANGE/DEL for a single window, without needing to re-inject all windows:

static void InjectSingleWindow(int32_t winId, int32_t groupId, int32_t displayId,
    Rect area, float zOrder, WINDOW_UPDATE_ACTION action, int32_t focusWinId = -1)
{
    WindowGroupInfo wgi;
    wgi.focusWindowId = focusWinId;
    wgi.displayId = displayId;
    WindowInfo win;
    win.id = winId;
    win.pid = getpid();
    win.uid = getuid();
    win.area = area;
    win.defaultHotAreas = { area };
    win.pointerHotAreas = { area };
    win.agentWindowId = -1;
    win.flags = 0;
    win.action = action;
    win.displayId = displayId;
    win.groupId = groupId;
    win.zOrder = zOrder;
    wgi.windowsInfo.push_back(win);
    InputManager::GetInstance()->UpdateWindowInfo(wgi);
}

UpdateDisplayInfoByIncrementalInfo handling:ADD append (replace if it already exists),CHANGE replace in place,DEL remove from the list.

Dynamic focus switching

Main group (groupId=0)

UpdateWindowInfo uses the passed-in wgi.focusWindowId, taking effect directly.

Non-main group (groupId!=0)

UpdateWindowInfo ignores the passed-in focusWindowId, always reading from displayGroupInfoMap_ .Must update focus via UpdateDisplayInfo :

SetupDualDisplayGroups(1000, 2002);  // 重新调用 UpdateDisplayInfo
sleep(1s);
InjectVirtualWindows();               // 重新注入窗口(同步 focusWinId 全局变量)
sleep(1s);

In multi-screen scenarios, distinguish between:

  • group-level keyboard focus: determined by DisplayGroupInfo.focusWindowId .
  • Expected focus window per screen: reflected by the test window stack and hit coordinates; do not mistakenly assume that MMI automatically maintains an independent keyboard focus for each display.
  • If the requirement claims that "each screen has its own focus window," the test should clarify whether this is the window stack/hit-target focus or the input framework's actual keyboard focus. The two must not be conflated.

Implementation pattern: use a global variable to track focus,SetupDualDisplayGroups and InjectVirtualWindows share:

static int32_t g_focusWin0 = 1000;
static int32_t g_focusWin1 = 2000;

static void SetupDualDisplayGroups(int32_t focusWin0 = 1000, int32_t focusWin1 = 2000)
{
    g_focusWin0 = focusWin0;
    g_focusWin1 = focusWin1;
    // ... UpdateDisplayInfo with focusWin0/focusWin1
}

static void InjectVirtualWindows()
{
    // wgi.focusWindowId = g_focusWin0;  // 不再硬编码
    // ...
}

Multi-window z-order hit testing

Basic pattern: different areas, different layers

// Group 0: 3 个窗口
// win1000: 应用窗口(背景)  area={0,80,720,1200}   zOrder=10
// win1001: 状态栏          area={0,0,720,80}       zOrder=100
// win1002: 悬浮球          area={600,600,120,120}   zOrder=200

SelectWindowInfo Traverse in descending zOrder; the first window hit by pointerHotAreas is the target. In overlapping areas, the higher zOrder takes priority.

Full-screen overlap pattern: position-independent verification

ADD multiple full-screen windows (with different zOrder), no cursor positioning needed — full-screen windows are hit at any cursor position:

InjectSingleWindow(1005, 0, 0, {0,0,720,1280}, 50.0f, WINDOW_UPDATE_ACTION::ADD, g_focusWin0);
InjectSingleWindow(1006, 0, 0, {0,0,720,1280}, 150.0f, WINDOW_UPDATE_ACTION::ADD, g_focusWin0);
InjectSingleWindow(1007, 0, 0, {0,0,720,1280}, 250.0f, WINDOW_UPDATE_ACTION::ADD, g_focusWin0);
// 点击 → Cursor Info.windowId = 1007 (z=250 最高)
// DEL win1007 → 点击 → windowId 变为原有最高层(可能是 win1002=z200,不一定是 win1006=z150)

Note: after DEL of a full-screen window, the hit target depends on the zOrder of all remaining windows, including existing non-full-screen windows. In the example above, after DEL win1007(z=250), if the cursor position is within the hotArea of win1002(z=200), then win1002 is hit rather than win1006(z=150).

Hot area pass-through

area and pointerHotAreas can differ. When the cursor is inside area but outside pointerHotAreas , the click passes through to the lower window:

// win1004: area 大于 hotArea
WindowInfo win;
win.area = { 100, 200, 400, 400 };
win.defaultHotAreas = { { 200, 300, 200, 200 } };  // hotArea 小于 area
win.pointerHotAreas = { { 200, 300, 200, 200 } };
// 点击 (150,250) — area 内但 hotArea 外 → 穿透到下层
// 点击 (350,450) — hotArea 内 → 命中此窗口

Window lifecycle (ADD → CHANGE → DEL)

// ADD: 新增弹窗
InjectSingleWindow(1003, 0, 0, {200,200,320,320}, 300.0f, WINDOW_UPDATE_ACTION::ADD, 1000);
// 点击 (360,360) → 命中 win1003

// CHANGE: 移动弹窗位置
InjectSingleWindow(1003, 0, 0, {400,800,320,320}, 300.0f, WINDOW_UPDATE_ACTION::CHANGE, 1000);
// 点击原位置 (360,360) → 穿透到 win1000

// DEL: 删除弹窗
InjectSingleWindow(1003, 0, 0, {0,0,0,0}, 0.0f, WINDOW_UPDATE_ACTION::DEL, 1000);
// 窗口列表恢复

Available verification APIs

// 设备绑定(详见 ohos-dev-distributed-mmi-device-test-harness)
InputManager::GetInstance()->BindDeviceToDisplayGroupByDisplay(deviceId, displayId, msg);
InputManager::GetInstance()->UnbindDeviceFromDisplayGroup(deviceId, msg);

// 光标样式(per-window)
PointerStyle style;
style.id = 13;            // MOUSE_ICON::CROSS
style.size = 3;
style.color = 0xFF0000;   // red
InputManager::GetInstance()->SetPointerStyle(windowId, style);
InputManager::GetInstance()->GetPointerStyle(windowId, style);

// 全局光标大小/颜色
InputManager::GetInstance()->SetPointerSize(4);
InputManager::GetInstance()->SetPointerColor(0x00FF00);

// 捕获模式
InputManager::GetInstance()->EnterCaptureMode(windowId);
InputManager::GetInstance()->LeaveCaptureMode(windowId);

Execution order

1. InitNativeToken()           — 权限 (见 ohos-dev-distributed-mmi-device-test-harness)
2. 建立 scenario model         — 用户、显示组、主/扩展/镜像屏、窗口栈、焦点
3. UpdateDisplayInfo()         — 注入 display group/screen 信息(等 1 秒)
4. UpdateWindowInfo() x N      — 每个 display 注入窗口栈(等 1 秒)
5. 创建虚拟设备 + FindDevice   — 见 ohos-dev-distributed-mmi-uinput-virtual-device
6. BindDeviceToDisplayGroupByDisplay()
7. warm-up 事件注入(2 次,初始化 group state)
8. 测试逻辑 + DumpPhase()     — dump 捕获见 ohos-dev-distributed-mmi-device-test-harness
9. 清理:解绑 → UI_DEV_DESTROY → close(fd)

Known limitations

Limitation Cause Workaround
Cursor positioning is imprecise libinput mouse acceleration causes uinput relative movement to be unable to reach exact absolute coordinates Use a fullscreen window to avoid depending on cursor position
focusWindowId=-1 In the DisplayGroups dump dump reads displayGroupInfoMap_, the focus of MAIN_GROUPID is passed in from UpdateWindowInfo passed in Verify the actual focus via KeyboardStateByGroup or SequenceSnapshots to verify the actual focus
Non-main-group focus switching UpdateWindowInfo ignores the non-main-group focusWindowId Must be updated via UpdateDisplayInfo update
After DEL fullscreen, hits an unexpected window Among the remaining windows, the one with the highest zOrder is hit, including pre-existing non-fullscreen windows Pay attention to the zOrder relationships of existing windows

Common Mistakes

Error Consequence Fix
If focusWindowId is not set, keyboard events are not dispatched to any window Preset in DisplayGroupInfo
groupId != 0 groups with GROUP_DEFAULT InitDisplayGroupInfo reject Non-0 groups use GROUP_SPECIAL
The window's groupId does not match the display group Events are routed to the wrong group Ensure consistency
Multiple screens/groups reuse displayId or windowId dump and routing evidence cannot be audited Globally unique numbering
Mirrored screens have different scales but continue to use the primary screen's coordinates An assertion is hit incorrectly First define the fit/stretch/crop mapping strategy
Claim independent keyboard focus per screen but only set group focus The conclusion does not hold Distinguish between group-level keyboard focus and per-screen window hit targets
Non-main groups switch focus via UpdateWindowInfo focusWindowId is ignored Focus must be switched via UpdateDisplayInfo
All windows use the same zOrder Hit order is indeterminate Assign different zOrder values according to business hierarchy
Forgetting to inject windows Mouse events have no hit test target At least one fullscreen window per group
If UpdateDisplayInfo returns -201, consider it a failure The data has actually been written -201 comes from UIExtension and can be ignored
InjectVirtualWindows hardcodes focusWindowId Overwritten after dynamic focus switching Use global variables to synchronize focus
After DEL fullscreen window, assume the second-highest fullscreen window is hit Pre-existing non-fullscreen windows may have higher zOrder Check the zOrder relationships of all windows