diff --git a/AGENTS.md b/AGENTS.md index 9512d83c7e..7b2aa97872 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,55 @@ # AGENTS.md — Window Manager (OpenHarmony) -This repo is a window sussystem of OpenHarmony. -The Window Manager subsystem provides basic capabilities of window and display management. It is the basis for UI display. The following figure shows the architecture of the Window Manager subsystem. +This repo is the window management subsystem of OpenHarmony. It provides core capabilities for window and display management, serving as the foundational subsystem for UI display. See [README.md](./README.md) for full architecture details. ## What is `window_manager` -You can read [README](./README.md) to obtain detail information. + +`window_manager` is a `Client-Server` architecture subsystem. Key modules: +- `wm/` — Window Manager Client +- `dm/` — Display Manager Client +- `wmserver/` — Window Manager Server (separated arch) +- `dmserver/` — Display Manager Server +- `window_scene/` — Window Manager Server (unified arch) +- `interfaces/innerkits/` — Native APIs +- `interfaces/kits/` — JS/NAPI APIs +- `extension/` — Ability Component window integration +- `utils/` — Shared utilities + +Two compile-time architectures are selected via `window_manager_use_sceneboard` in `product/define.gni`: + +| Value | Architecture | Key module compiled | +|-------|-------------|---------------------| +| `false` | Separated | `wmserver/` | +| `true` | Unified | `window_scene/` | ## WorkFlow ### CodeStyle specification You must follow the [CodeStyle](./docs/CodeStyle.md) specification when you write code. +Key rules: +- **C++ standard**: C++11; **Column limit**: 120; **Indent**: 4 spaces, no tabs +- **Namespace**: always `OHOS::Rosen` +- **Naming**: Classes `PascalCase`, methods `camelCase`, member vars `camelCase_`, constants `UPPER_SNAKE_CASE`, files `snake_case` +- **Include order**: corresponding header → stdlib → OH framework → `interfaces/` → other internal +- **Logging**: `TLOGD` / `TLOGI` / `TLOGW` / `TLOGE` +- **Error handling**: return `WMError`/`WmErrorCode`; early-return with `WLOGFE` log +- **Memory**: `sptr` for IPC/singletons, `wptr` for weak refs; prefer RAII +- Every source file must begin with the Apache 2.0 license header + ### Testing specification You must follow the [Testing](./docs/Testing.md) specification after you write testing code and keep testing suite pass. + +### Git Commit Rules + +- **User approval required**: Ask user before `git commit`. Use `git commit -s` after approval. +- **Angular format**: `type(scope): subject` (feat, fix, docs, style, refactor, test, chore) +- **Co-authored footer**: Append `Co-Authored-By: Agent` to every commit message. + +Example: +``` +feat(auth): add user login feature + +Signed-off-by: Your Name +Co-Authored-by: Agent +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..9cc67b7562 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,151 @@ +# CLAUDE.md — Window Manager (OpenHarmony) + +This file provides guidance for AI coding agents working in this repository. + +## What is this repo + +`window_manager` is the window management subsystem of OpenHarmony. It provides core capabilities for window and display management, serving as the foundational subsystem for UI display. See [README.md](./README.md) for full architecture details. + +## Architecture + +Two compile-time architectures are supported, selected via `window_manager_use_sceneboard` in `product/define.gni`: + +| Value | Architecture | Key module compiled | +|-------|-------------|---------------------| +| `false` | Separated | `wmserver/` | +| `true` | Unified | `window_scene/` | + +Key modules: +- `wm/` — Window Manager Client +- `dm/` — Display Manager Client +- `wmserver/` — Window Manager Server (separated arch) +- `dmserver/` — Display Manager Server +- `window_scene/` — Window Manager Server (unified arch) +- `interfaces/innerkits/` — Native APIs +- `interfaces/kits/` — JS/NAPI APIs +- `extension/` — Ability Component window integration +- `utils/` — Shared utilities + +## Build + +This project uses the OpenHarmony `gn` + `ninja` build system. + +Feature flags are declared via `declare_args()` in `windowmanager_aafwk.gni`. The global architecture switch is: +```gni +window_manager_use_sceneboard = true # unified arch +window_manager_use_sceneboard = false # separated arch +``` + +## Code Style + +Full rules: [docs/CodeStyle.md](./docs/CodeStyle.md) + +- **C++ standard**: C++11 +- **Column limit**: 120 characters +- **Indent**: 4 spaces, no tabs +- **Braces**: Same line for classes/structs/control flow; new line for functions +- **Pointer alignment**: Left — `int* ptr` +- **Namespace**: always `OHOS::Rosen` + +### Naming conventions + +| Element | Convention | Example | +|---------|-----------|---------| +| Classes / Structs | PascalCase | `WindowManagerService` | +| Methods | camelCase | `GetWindowById()` | +| Member variables | camelCase + trailing `_` | `windowManagerMap_` | +| Constants / Enums | UPPER_SNAKE_CASE | `DEFAULT_SPACING` | +| Namespaces | PascalCase | `OHOS::Rosen` | +| File names | snake_case | `window_manager.cpp` | +| Test classes | PascalCase + `Test` suffix | `WindowManagerServiceTest` | +| Macros | UPPER_SNAKE_CASE | `WMSERVER_LOG_TAG` | + +### Include order (per `.cpp`) + +1. Corresponding header +2. Standard library headers (``, ``, …) +3. OpenHarmony framework headers (``, ``, …) +4. Project headers from `interfaces/` +5. Other project-internal headers + +Use `#pragma once` or `#ifndef OHOS_ROSEN_*_H` include guards in headers. + +### Logging + +```cpp +TLOGD("message %d", value); // Debug +TLOGI("message"); // Info +TLOGW("warning"); // Warn +TLOGE("error: %s", err); // Error +``` + +### Error handling + +- Return `WMError` / `WmErrorCode` enum values from public APIs. +- Use early-return for error cases; log with `WLOGFE` before returning. + +### Memory management + +- Use `sptr` for IPC objects and singletons; `wptr` for weak refs. +- Prefer RAII; avoid raw `new`/`delete`. + +### License header + +Every source file must begin with: +```cpp +/* + * Copyright (c) xxxx-xxxx Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * ... + */ +``` + +## Testing + +Full rules: [docs/Testing.md](./docs/Testing.md) + +- Test files use `*_test.cpp` suffix, named after the class under test. +- Use `HWTEST_F` macro; namespace must be `OHOS::Rosen`. +- `EXPECT_*` for non-fatal checks; `ASSERT_*` for fatal. + +### Test structure template + +```cpp +using namespace testing; +using namespace testing::ext; + +namespace OHOS { +namespace Rosen { +class MyFeatureTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; +}; + +HWTEST_F(MyFeatureTest, TestSomething, TestSize.Level1) +{ + EXPECT_EQ(result, expected); + ASSERT_NE(ptr, nullptr); +} +} // namespace Rosen +} // namespace OHOS +``` + +### Build targets per module + +| Module | Build target | +|--------|-------------| +| wm | `wm:test` | +| wmserver | `wmserver:test` | +| dm | `dm:test` | +| dmserver | `dmserver:test` | +| dm_lite | `dm_lite:test` | +| window_scene | `window_scene:test` | +| snapshot | `snapshot:test` | +| extension/window_extension | `extension/window_extension:test` | + +## IPC / ZIDL + +IPC proxy/stub code lives in `src/zidl/` subdirectories within each module. IDL definitions are at the module root (e.g. `dmserver/IDisplayManager.idl`). diff --git a/README.md b/README.md index bfddb48793..90a5c6c804 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,562 @@ -# Window Manager +# window_manager -## Introduction +- [Introduction](#1-introduction) +- [Architecture Overview](#2-architecture-overview) +- [Separated vs. Unified Architecture](#3-separated-vs-unified-architecture) +- [Sub-module Architecture Details](#4-sub-module-architecture-details) +- [Development Guide](#5-development-guide) +- [Directory Structure](#6-directory-structure) +- [Constraints](#7-constraints) +- [Available APIs](#8-available-apis) +- [Repositories Involved](#9-repositories-involved) -The Window Manager subsystem provides basic capabilities of window and display management. It is the basis for UI display. The following figure shows the architecture of the Window Manager subsystem. +## 1. Introduction -**Figure 1** Architecture of the Window Manager subsystem +### 1.1 Window Subsystem Overview +The Window Manager subsystem provides core capabilities for window and display management in OpenHarmony. It is the foundational subsystem for UI display, responsible for coordinating and managing the creation, destruction, layout, rendering, and interaction of all windows in the system. -![WindowManager-subsystem-architecture](./docs/figures/WindowManager_EN.png) +### 1.2 Core Capabilities +#### Screen Management +- **Display-Screen Mapping**: Manages the mapping relationship between logical Displays and physical Screens +- **Display Management**: Multi-display management and information query +- **Screen Control**: Screen on/off control and brightness adjustment +- **Screenshot**: Full-screen screenshot capability -- **Window Manager Client** +#### Window Management +- **Window Lifecycle Management**: Window creation, show, hide, and destruction +- **Window Relationships and Structure**: Parent-child window relationship management with support for window nesting +- **Window Layout Management**: Window position, size, and Z-order control +- **Window Interaction**: Window drag, resize, and move operations +- **Window Snapshot**: Window content screenshot capability +- **Focus Management**: Window focus switching and input event dispatching +- **Multimodal Input Support**: Provides window layout and focus window information for the multimodal input system - Provides window object abstraction and window management interfaces, and connects to the ability and UI framework. +### 1.3 Parts and Relationships +![Parts and Relationships](./docs/figures/window_parts_and_relationships.png) -- **Display Manager Client** +The window subsystem consists of 3 parts: +1. `window_manager`: The current part, hosting the window management service and the application-layer window framework. + 1. It is the core foundation of the entire window subsystem. +2. `scene_board_core`: Hosts the intermediate-layer framework between desktop-related system applications / system UI and the window management service. + 1. It serves as the middle layer between `window_manager` and `scene_board`. +3. `scene_board`: Hosts the desktop-related system applications and system UI implementations, such as the launcher, wallpaper, lock screen, status bar, navigation bar, and control center. + 1. It is the topmost module of the window subsystem and the entry point for users to interact with the system UI. - Provides display information abstraction and display management interfaces. +The window subsystem is primarily associated with the following subsystems: +- **Applications**: Applications can manage windows and screens through the window or display related APIs +- **Multimodal Input Subsystem**: The multimodal input system relies on the window subsystem for event dispatching +- **Graphics Rendering Subsystem**: The window subsystem works in coordination with the graphics rendering system to accomplish window management and UI rendering -- **Window Manager Server** +In addition, the window subsystem also serves the following consumers: +- **System Applications**: Provides system window and application window management capabilities for system-level UI applications such as the launcher and wallpaper +- **UI Framework**: The `ArkUI` framework implements UI rendering through windows - Provides capabilities such as window layout, Z-order control, window tree structure, window dragging, and window snapshot, and offers the window layout and focus window for multimodal input. +## 2. Architecture Overview -- **Display Manager Server** +### 2.1 Overall Architecture - Provides display information, screenshot, screen on/off, and brightness processing control, and processes the mapping between the display and screen. +The window subsystem adopts a `Client-Server` architecture, achieving client-server separation through IPC (Inter-Process Communication). +The overall architecture is shown below: -## Architectures -The current `window_manager` part, incorporates two architectures of the window subsystem. Respectively referred to as the "separated architecture" and the "unified architecture". +![Window Subsystem Overall Architecture](./docs/figures/WindowManager.png) -The main differences are shown in the **Figure 2**: ![The differences between two architectures](./docs/figures/WindowManager-Architectures-EN.png) +### 2.2 Architecture Design Principles +Layered design: +- **Interface Layer**: + - Provides Native API and JS/NAPI interfaces for application use +- **Client Layer**: + - Window Manager Client and Display Manager Client, responsible for interface encapsulation, application framework implementation, and IPC communication +- **Server Layer**: + - WindowManagerService and DisplayManagerService, acting as system services (ServiceAbility) to provide core business logic for window management and screen management + - SceneSessionManager and ScreenSessionManager are the core business implementation modules for window management and screen management at the system service layer -- **Separated Architecture** -The "desktop-related" system Apps, such as the desktop and wallpaper, are all located in the application layer and operate independently in separate processes.Therefore, under this architecture, the APPs task management, such as the startup and shutdown of applications, involve multiple IPC communication between desktop-related system APPs and system services, as well as between system services and APPs. +Collaborative relationships under the layered design: +- **Application Window Creation Flow** + ``` + Application → window API → Window Manager Client → IPC → WindowManagerService + ``` -- **Unified Architecture** -The desktop-related system Apps and the window management service process are integrated together. They no longer run as independent processes but have transformed into individual system "[WindowScene Component](https://gitcode.com/openharmony/arkui_ace_engine/blob/master/frameworks/core/components_ng/pattern/window_scene/scene)". Therefore, the window management subsystem provides new "WindowScene Component" for implementing the management of windows and the layout management of windows. -In addition, screen management are also provided by the "[Screen Component](https://gitcode.com/openharmony/arkui_ace_engine/blob/master/frameworks/core/components_ng/pattern/window_scene/screen)". +- **Display Information Query Flow** + ``` + Application → display API → Display Manager Client → IPC → DisplayManagerService + ``` -### Key Differences in Different Architectures -On one hand, the management of windows has shifted to being accomplished through window controls. On the other hand, system applications related to the desktop have transformed into system window controls. As a result, there have been significant changes in the task management processes such as startup and shutdown. +### 2.3 Dual Architectures +The window subsystem currently supports two base architectures: the Separated Architecture and the Unified Architecture. They can be switched via compile-time feature configuration. +- **Global feature configuration key**: `window_manager_use_sceneboard` +- **Configuration file**: `product/define.gni` or the system feature configuration file +- **Configuration method**: + ```gni + # Select separated architecture + window_manager_use_sceneboard = false -- **Separated Architecture** -![start-up under separated architecture](./docs/figures/WindowManager-StartSteps-Separated-EN.png) + # Select unified architecture + window_manager_use_sceneboard = true + ``` +- **Scope of impact**: + - Separated Architecture: compiles the `window_manager/wmserver` module + - Unified Architecture: compiles the `window_manager/window_scene` module, `scene_board_core`, and `scene_board` -- **Unified Architecture** -![start-up under unified architecture](./docs/figures/WindowManager-StartSteps-Unified-EN.png) +#### Architecture Differences +![Window Subsystem Dual Architecture Comparison](./docs/figures/WindowManager-Architectures.png) -## Directory Structure +Both architectures expose exactly the same API interfaces to the outside. There is no perceptible difference at the application layer; the differences are mainly in internal implementation and process model. + +## 3. Separated vs. Unified Architecture + +### 3.1 Separated Architecture + +#### 3.1.1 Architecture Characteristics + +The Separated Architecture is the traditional window management implementation approach, with the following characteristics: +- **Independent Process Model**: Desktop, wallpaper, and other system applications run as independent processes +- **Traditional IPC Communication**: Application startup/shutdown involves multiple IPC communications + +#### 3.1.2 Process Model +![window_process_model](./docs/figures/window_process_model.png) + +#### 3.1.3 Startup Flow + +![Separated Architecture Startup Flow](./docs/figures/WindowManager-StartSteps-Separated.png) + +**Application Startup Steps**: +1. Tap the icon to launch the application. +2. Via IPC, other system services create the process; the window service creates a splash window and loads the splash screen. +3. Through multiple IPC calls, the application and system services establish connections and schedule different lifecycle callbacks (e.g., `onCreate`, `onForeground`). +4. The application creates an application window during startup and loads the application UI. +5. Throughout the process, the launcher controls the startup animation of the application window via IPC through the window management service. + +#### 3.1.4 Pros and Cons + +**Advantages**: +- Good process isolation; a system application crash does not affect the window service +- Clear architecture with well-defined separation of responsibilities +- Suitable for traditional desktop systems + +**Disadvantages**: +- High IPC communication overhead +- Complex startup flow involving multiple IPC calls +- Cross-process window management is complex + +### 3.2 Unified Architecture + +#### 3.2.1 Architecture Characteristics + +The Unified Architecture is the new window management implementation approach, with the following characteristics: +- **Unified Process**: Desktop, wallpaper, and other system applications are merged into the same process as the window service +- **Component-based Management**: System applications are transformed into system window components +- **Layout-driven**: Window layout management is driven by the ArkUI layout pipeline + +#### 3.2.2 Process Model +![window_process_model_unified](./docs/figures/window_process_model_unified.png) + +#### 3.2.3 Startup Flow + +![Unified Architecture Startup Flow](./docs/figures/WindowManager-StartSteps-Unified.png) + +**Startup Steps**: +1. Tap the icon to launch the application. +2. The window management service creates the window first and loads the splash screen. +3. The window management service notifies the Ability Manager Service to start the application and schedule different lifecycle callbacks (e.g., `onCreate`, `onForeground`). +4. After the application starts, it loads the UI and connects to the window management service to replace the splash screen. +5. Throughout the process, the launcher and the window management service are in the same process and directly control the startup animation of the application window. + +#### 3.2.4 Core Components + +**WindowScene Component**: +- Responsible for component-based window management +- Implements layout management for window components +- Provides window lifecycle control + +**Screen Component**: +- Responsible for component-based screen management +- Manages the mapping between physical screens and logical Displays +- Provides screen control capabilities + +#### 3.2.5 Pros and Cons + +**Advantages**: +- Fewer IPC communications, better performance +- Simplified startup flow, faster launch speed +- Leverages the ArkUI layout pipeline for more flexible layout management +- Suitable for mobile and embedded systems + +**Disadvantages**: +- High process coupling +- A system application crash may affect the window service +- Increased debugging complexity + +## 4. Sub-module Architecture Details + +### 4.1 Window Manager Client (wm) + +#### 4.1.2 Module Responsibilities + +1. **Window Object Abstraction**: Provides the Window class, encapsulating all window operations +2. **Interface Encapsulation**: Wraps lower-level IPC communication into easy-to-use APIs +3. **Lifecycle Management**: Manages the creation and destruction of window objects +4. **Event Callbacks**: Handles window state change events +5. **IPC Communication**: Communicates with the server side via IPC + +#### 4.1.3 Collaborative Relationship + +``` +Application Code + ↓ +Window API (interfaces/kits) + ↓ +Window Manager Client (wm) + ↓ +IPC Communication + ↓ +Window Manager Server +``` + +### 4.2 Display Manager Client (dm) + +#### 4.2.1 Module Structure + +``` +dm/ +├── include/ # Header files +│ ├── display.h # Display interface definitions +│ └── display_info.h # Display information structures +└── src/ # Source files + ├── display.cpp # Display implementation + └── display_manager.cpp # Display manager +``` + +#### 4.2.2 Module Responsibilities + +1. **Display Information Abstraction**: Provides the Display class, encapsulating Display information queries +2. **Interface Encapsulation**: Provides Display management APIs +3. **IPC Communication**: Communicates with Display Manager Server +4. **Event Listening**: Listens for Display change events + +#### 4.2.3 Collaborative Relationship + +``` +Application Code + ↓ +Display API (interfaces/kits) + ↓ +Display Manager Client (dm) + ↓ +IPC Communication + ↓ +Display Manager Server +``` + +### 4.3 Window Manager Server (wmserver) + +#### 4.3.1 Module Structure + +``` +wmserver/ +├── include/ # Header files +│ ├── window_root.h # Window root node +│ ├── window_node.h # Window node +│ ├── window_layout.h # Window layout manager +│ └── ... +└── src/ # Source files + ├── window_root.cpp # Window root node implementation + ├── window_node.cpp # Window node implementation + ├── window_layout.cpp # Window layout implementation + └── ... +``` + +#### 4.3.2 Module Responsibilities + +1. **Window Tree Management**: Maintains the window tree structure and manages parent-child window relationships +2. **Window Layout**: Calculates window positions and sizes, handles window layout +3. **Z-order Management**: Manages window Z-order and controls window display sequence +4. **Focus Management**: Manages window focus and handles focus switching +5. **Input Dispatching**: Provides focus window information to the input system +6. **Window Dragging**: Handles window drag logic +7. **Window Snapshot**: Provides window screenshot capability + +#### 4.3.3 Core Class Descriptions + +- **WindowRoot**: The root node of the window tree, managing all top-level windows +- **WindowNode**: A window node representing a window instance +- **WindowLayout**: The window layout manager, responsible for layout calculation +- **FocusController**: The focus controller, managing window focus + +#### 4.3.4 Collaborative Relationship + +``` +IPC Communication + ↓ +Window Manager Service + ├── WindowRoot (Window Tree) + ├── WindowLayout (Layout Management) + ├── FocusController (Focus Management) + └── ... + ↓ +Graphics System (RenderService) +``` + +### 4.4 Display Manager Server (dmserver) + +#### 4.4.1 Module Structure + +``` +dmserver/ +├── include/ # Header files +│ ├── abstract_display.h # Abstract Display +│ ├── abstract_screen.h # Abstract Screen +│ ├── display_controller.h # Display controller +│ └── ... +└── src/ # Source files + ├── abstract_display.cpp # Abstract Display implementation + ├── abstract_screen.cpp # Abstract Screen implementation + └── ... +``` + +#### 4.4.2 Module Responsibilities + +1. **Display Management**: Manages logical Displays and provides Display information queries +2. **Screen Management**: Manages physical Screens and provides Screen control +3. **Mapping Management**: Maintains the mapping relationship between Displays and Screens +4. **Screen Control**: Controls screen on/off and brightness +5. **Screenshot**: Provides full-screen screenshot capability + +#### 4.4.3 Core Class Descriptions + +- **AbstractDisplay**: Abstract Display class, representing a logical display +- **AbstractScreen**: Abstract Screen class, representing a physical screen +- **DisplayController**: Display controller, managing Display lifecycle + +#### 4.4.4 Collaborative Relationship + +``` +IPC Communication + ↓ +Display Manager Service + ├── AbstractDisplay (Logical Display) + ├── AbstractScreen (Physical Screen) + └── DisplayController (Controller) + ↓ +Hardware Abstraction Layer (HDI) +``` + +### 4.5 WindowScene (window_scene) + +#### 4.5.1 Module Structure + +``` +window_scene/ +├── include/ # Header files +│ ├── scene_root.h # Scene root node +│ ├── scene_board.h # Scene board +│ └── ... +└── src/ # Source files + ├── scene_root.cpp # Scene root node implementation + └── scene_board.cpp # Scene board implementation +``` + +#### 4.5.2 Module Responsibilities + +1. **Scene Management**: Manages window scenes, serving as the container for window components +2. **Component-based Management**: Manages windows as `ArkUI` components +3. **Layout Integration**: Integrates the `ArkUI` layout pipeline to enable layout pipeline reuse +4. **System Component Management**: Manages system window components such as the launcher and wallpaper + +#### 4.5.4 Collaborative Relationship + +``` +IPC Communication + ↓ +WindowScene (ArkUI Component) + ├── RootScene (Scene Root) + ├── SystemWindowScene - Launcher Component + ├── SystemWindowScene - Wallpaper Component + └── WindowScene - Application Window Component + ↓ +ArkUI Layout Pipeline + ↓ +Graphics Rendering System +``` + +### 4.6 Extension (extension) + +#### 4.6.1 Module Structure + +``` +extension/ +├── extension_connection/ # ExtensionAbility component connection part +│ ├── ability_connection.cpp +│ └── ... +└── window_extension/ # ExtensionAbility component window part + ├── window_extension.cpp + └── ... +``` + +#### 4.6.2 Module Responsibilities + +1. **Ability Binding**: Implements the binding relationship between an Ability and a window +2. **Lifecycle Synchronization**: Synchronizes the lifecycle of an Ability and its window +3. **Property Propagation**: Passes properties between an Ability and its window + +#### 4.6.3 Collaborative Relationship + +``` +Ability Framework + ↓ +Extension + ├── ExtensionConnection (Connection Management) + └── WindowExtension (Window Extension) + ↓ +Window Manager +``` + +## 5. Development Guide + +### 5.1 Window Properties + +**Customizable Window Properties**: +```cpp +// Window type +enum class WindowType { + TYPE_APP, // Application window + TYPE_SYSTEM_ALERT, // System alert window + TYPE_INPUT_METHOD, // Input method window + TYPE_STATUS_BAR, // Status bar window + TYPE_PANEL, // Panel window + TYPE_FLOAT, // Floating window + // ... extensible as needed +}; + +// Window mode +enum class WindowMode { + UNDEFINED, + FULLSCREEN, // Full-screen mode + PRIMARY, // Split-screen primary window + SECONDARY, // Split-screen secondary window + FLOATING, // Floating mode +}; + +// Window layout properties +struct WindowLayoutProperty { + Rect rect; // Window position and size + uint32_t zOrder; // Window Z-order + WindowMode mode; // Window mode + // ... extensible as needed +}; +``` + +**Development Steps**: +1. Extend the `WindowType` enum to add a custom window type +2. Add the corresponding window type handling logic in the Window Manager Server +3. Modify the window layout algorithm to support the new window type + +#### Adding a Custom Window Type + +**Step 1**: Extend the window type enum +```cpp +// In interfaces/innerkits/native/include/window/window_type.h +enum class WindowType { + // ... existing types + TYPE_CUSTOM_WINDOW = 1000, // Custom window type +}; +``` + +**Step 2**: Add window type handling logic +```cpp +// In wmserver/src/window_type.cpp +bool IsSystemWindow(WindowType type) +{ + // ... existing logic + if (type == WindowType::TYPE_CUSTOM_WINDOW) { + return true; + } + return false; +} +``` + +**Step 3**: Handle the new type in the layout algorithm +```cpp +// In wmserver/src/window_layout.cpp +void WindowLayout::CalculateLayout(WindowNode* node) +{ + if (node->GetType() == WindowType::TYPE_CUSTOM_WINDOW) { + // Custom layout logic + CalculateCustomWindowLayout(node); + } else { + // Default layout logic + CalculateDefaultLayout(node); + } +} +``` + +### 5.2 Window Layout Algorithm + +**Customizable Layout Algorithm**: +```cpp +// In window_layout.h +class WindowLayout { +public: + // Overridable layout calculation functions + virtual void CalculateLayout(WindowNode* node); + virtual void CalculateZOrder(std::vector& nodes); + +protected: + // Layout strategy + LayoutStrategy layoutStrategy_; + + // Customization: add a custom layout strategy + void ApplyCustomLayout(WindowNode* node); +}; +``` + +**Customization Steps**: +1. Inherit from the `WindowLayout` class +2. Override the `CalculateLayout` method to implement a custom layout algorithm +3. Use the custom layout class in the Window Manager Server + +### 5.3 Notes + +1. **Compatibility**: Maintain compatibility with existing interfaces +2. **Performance**: Business logic must not impact system performance +3. **Stability**: Code must be thoroughly tested to ensure it does not affect system stability +4. **Maintainability**: Code must have good comments and documentation +5. **Version Upgrades**: Compatibility must be considered when upgrading the system + +## 6. Directory Structure ``` foundation/window/window_manager/ -├── dm # Dislplay Manager Client -├── dmserver # Dislplay Manager Service -├── extension # Ability Component Window -│   ├── extension_connection # Ability Component -│   └── window_extension # Ability Component 被嵌入部分 -├── interfaces # apis -│   ├── innerkits # native apis -│   └── kits # js/napi apis -├── previewer # lite previewer for IDE -├── resources # resources -├── sa_profile # system ability configs -├── snapshot # snapshow util -├── test # tests like fuzz -├── utils # utils functions -├── window_scene # Unified Window Manager Service -├── wm # Window Manager Client -└── wmserver # Separated Window Manager Service +├── dm # Display Manager Client implementation +├── dmserver # Display Manager Service implementation +├── extension # Ability Component window-related code +│ ├── extension_connection # Ability Component embedding part +│ └── window_extension # Ability Component embedded part +├── interfaces # External API directory +│ ├── innerkits # Native API directory +│ └── kits # JS/NAPI API directory +├── previewer # Lightweight IDE simulator window implementation +├── resources # Framework resource files +├── sa_profile # System service configuration files +├── snapshot # Screenshot command-line tool implementation +├── test # Fuzz tests and system test cases +├── utils # Utility classes +├── window_scene # Unified Architecture Window Manager Service implementation +├── wm # Window Manager Client implementation +└── wmserver # Separated Architecture Window Manager Service implementation ``` -## Constraints +## 7. Constraints +- Language version + - C++11 or later -- Programming language version - - C++ 11 or later +## 8. Available APIs -## Available APIs +- [Window](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-arkui/arkts-apis-window.md) +- [Display](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-arkui/js-apis-display.md) -- [Window](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis/js-apis-window.md) -- [Display](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis/js-apis-display.md) +## 9. Repositories Involved -## Repositories Involved - -- [graphic_graphic_2d](https://gitee.com/openharmony/graphic_graphic_2d) -- [arkui_ace_engine](https://gitee.com/openharmony/arkui_ace_engine) -- [ability_ability_runtime](https://gitee.com/openharmony/ability_ability_runtime) -- [multimodalinput_input](https://gitee.com/openharmony/multimodalinput_input) +- [graphic_graphic_2d](https://gitcode.com/openharmony/graphic_graphic_2d) +- [arkui_ace_engine](https://gitcode.com/openharmony/arkui_ace_engine) +- [ability_ability_runtime](https://gitcode.com/openharmony/ability_ability_runtime) +- [multimodalinput_input](https://gitcode.com/openharmony/multimodalinput_input) diff --git a/README_zh.md b/README_zh.md index af3291d292..40c35ff701 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,59 +1,527 @@ # window_manager -- [简介](#简介) -- [目录](#目录) -- [约束](#约束) -- [接口说明](#接口说明) -- [相关仓](#相关仓) +- [简介](#1-简介) +- [架构说明](#2-架构说明) +- [分离架构与合一架构详解](#3-分离架构与合一架构详解) +- [各子模块架构详解](#4-各子模块架构详解) +- [开发方式](#5-开发方式) +- [目录](#6-目录) +- [约束](#7-约束) +- [接口说明](#8-接口说明) +- [相关仓](#9-相关仓) -## 简介 +## 1. 简介 -**窗口子系统** 提供窗口管理和Display管理的基础能力,是系统图形界面显示所需的基础子系统。 +### 1.1 窗口子系统概述 +窗口管理子系统为 OpenHarmony 系统提供窗口管理和显示管理的核心能力,是UI显示的基础子系统,负责协调和管理系统中所有窗口的创建、销毁、布局、显示和交互。 -其主要的结构如下图所示: +### 1.2 核心能力 +#### 屏幕管理能力 +- **Display-Screen映射**:逻辑Display与物理Screen的映射关系管理 +- **Display管理**:多Display管理、信息查询 +- **屏幕控制**:屏幕亮灭控制、亮度调节 +- **屏幕截图**:全屏截图功能 -![窗口子系统架构图](./docs/figures/WindowManager.png) +#### 窗口管理能力 +- **窗口生命周期管理**:窗口的创建、显示、隐藏、销毁 +- **窗口关系与结构**:父子窗口关系管理,支持窗口嵌套 +- **窗口布局管理**:窗口的位置、大小、层级控制 +- **窗口交互能力**:窗口拖拽、缩放、移动等交互操作 +- **窗口快照**:窗口内容截图能力 +- **焦点管理**:窗口焦点切换和输入事件分发 +- **多模态输入支持**:为多模态输入系统提供窗口布局和焦点窗口信息 -- **Window Manager Client** +### 1.3 部件与关系 +![部件与关系](./docs/figures/window_parts_and_relationships.png) - 应用进程窗口管理接口层,提供窗口对对象抽象和窗口管理接口,对接原能力和UI框架。 +窗口子系统共有3个部件,分别是: +1. `window_manager`: 当前部件,承载了窗口管理服务与应用层窗口框架。 + 1. 是整个窗口子系统的核心底座。 +2. `scene_board_core`: 承载了桌面相关系统应用和系统UI与窗口管理服务的中间层框架。 + 1. 是 `window_manager` 与 `scene_board` 的中间层。 +3. `scene_board`: 承载了桌面相关系统应用和系统UI实现,例如桌面、壁纸、锁屏、状态栏、导航条、控制中心等。 + 1. 是窗口子系统的最上层模块,是用户与系统UI的入口。 -- **Display Manager Client** +窗口子系统主要与这些子系统关联,分别是: +- **应用**:应用可通过 window 或 display 相关API管理窗口和屏幕 +- **多模输入子系统**:多模输入系统依赖窗口子系统进行事件分发 +- **图形渲染子系统**:窗口需要与图形渲染系统协同工作,完成窗口管理和UI渲染。 - 应用进程Display管理接口层,提供Display信息抽象和Display管理接口。 +此外,窗口子系统还为以下对象服务: +- **系统应用**:为桌面、壁纸等系统级UI应用提供系统窗口和应用窗口管理的能力。 +- **UI框架**:`ArkUI` 框架通过窗口实现UI渲染。 -- **Window Manager Server** +## 2. 架构说明 - 窗口管理服务,提供窗口布局、Z序控制、窗口树结构、窗口拖拽、窗口快照等能力,并提供窗口布局和焦点窗口给多模输入 +### 2.1 整体架构 -- **Display Manager Server** +窗口子系统采用 `Client-Server` 架构,通过IPC(进程间通信)实现客户端和服务端的分离。 +整体架构图如下: - Display管理服务,提供Display信息、屏幕截图、屏幕亮灭和亮度处理控制,并处理Display与Screen映射关系 +![窗口子系统整体架构](./docs/figures/WindowManager.png) -## 架构说明 -当前部件,即`window_manager`,同时包含了窗口管理服务的两种架构,分别称为“分离架构”和“合一架构”。 +### 2.2 架构设计原理 +分层设计: +- **接口层**: + - 提供 Native API 和 JS/NAPI 接口,供应用调用 +- **客户端层**: + - Window Manager Client 和 Display Manager Client,负责接口层的封装、应用框架实现和 IPC 通信 +- **服务端层**: + - WindowManagerService 和 DisplayManagerService,作为系统服务(ServiceAbility)负责提供窗口管理和屏幕管理的核心业务逻辑 + - SceneSessionManager 和 ScreenSessionManager,是系统服务层的窗口管理和屏幕管理的核心业务实现模块 -主要区别如下图: -![窗口子系统新老架构差异](./docs/figures/WindowManager-Architectures.png) +分层设计下的协同关系: +- **应用创建窗口流程** + ``` + 应用 → window API → Window Manager Client → IPC → WindowManagerService + ``` -- **分离架构** -桌面相关的系统应用,例如桌面、壁纸等,均位于应用层,且各自独立进程。因此在这种架构下,应用的启动退出等管理过程就包含了桌面相关的系统应用到系统服务、系统服务到应用的多次IPC通信过程。 +- **显示信息查询流程** + ``` + 应用 → display API → Display Manager Client → IPC → DisplayManagerService + ``` -- **合一架构** -桌面相关的系统应用,和窗口管理服务进程合一,不再是以独立的进程运行,而是转变为一个个系统**窗口控件**。因此,窗口管理子系统提供了[**窗口控件**](https://gitcode.com/openharmony/arkui_ace_engine/blob/master/frameworks/core/components_ng/pattern/window_scene/scene),用于实现窗口的控件化管理和窗口控件的布局管理。 -此外,也提供了[**屏幕控件**](https://gitcode.com/openharmony/arkui_ace_engine/blob/master/frameworks/core/components_ng/pattern/window_scene/screen)用于屏幕的控件化管理。 +### 2.3 双架构 +窗口子系统当前共有两个基础架构,分别是分离架构和合一架构。可通过编译时特性配置进行切换。 +- **全局特性配置项**:`window_manager_use_sceneboard` +- **配置文件**:`product/define.gni` 或系统特性配置文件 +- **配置方式**: + ```gni + # 选择分离架构 + window_manager_use_sceneboard = false -### 切换方式 -**分离架构**和**合一架构**可以通过全局特性配置项 `window_manager_use_sceneboard` 实现切换。 -具体配置方式,请参考:[特性配置规则](https://gitcode.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-build-feature.md) + # 选择合一架构 + window_manager_use_sceneboard = true + ``` +- **影响范围**: + - 分离架构:编译 `window_manager/wmserver` 模块 + - 合一架构:编译 `window_manager/window_scene` 模块、`scene_board_core` 和 `scene_board` -### 不同架构的关键区别 -一方面窗口的管理方式转变为通过窗口控件完成,另一方面桌面相关的系统应用转变为系统窗口控件,所以二者在启动退出等任务管理流程上发生了关键性改变。 -- **分离架构** -![分离架构下的启动流程](./docs/figures/WindowManager-StartSteps-Separated.png) +#### 架构差异 +![窗口子系统双架构对比](./docs/figures/WindowManager-Architectures.png) -- **合一架构** -![合一架构下的启动流程](./docs/figures/WindowManager-StartSteps-Unified.png) +两种架构对外提供完全相同的API接口,应用层无感知,差异主要体现在内部实现和进程模型上。 + +## 3. 分离架构与合一架构详解 + +### 3.1 分离架构 + +#### 3.1.1 架构特点 + +分离架构是传统的窗口管理实现方式,具有以下特点: +- **独立进程模型**:桌面、壁纸等系统应用作为独立进程运行 +- **传统IPC通信**:应用启动/退出涉及多次IPC通信 + +#### 3.1.2 进程模型 +![window_process_model](./docs/figures/window_process_model.png) + +#### 3.1.3 启动流程 + +![分离架构启动流程](./docs/figures/WindowManager-StartSteps-Separated.png) + +**应用启动步骤**: +1. 点击图标,启动应用。 +2. 经过IPC,由元能力管理子系统创建应用进程、由窗口管理服务先创建启动窗口并加载启动界面。 +3. 经过多次IPC,应用和系统服务建连并调度不同的生命周期(例如: `onCreate`,`onForeground`)。 +4. 应用在启动过程中创建应用窗口,并加载应用UI界面。 +5. 过程中由桌面以IPC的方式,利用窗口管理服务实现控制应用窗口的启动动画。 + +#### 3.1.4 优缺点 + +**优点**: +- 进程隔离性好,系统应用崩溃不影响窗口服务 +- 架构清晰,职责分离明确 +- 适合传统桌面系统 + +**缺点**: +- IPC通信开销大 +- 启动流程复杂,涉及多次IPC +- 跨进程窗口管理复杂 + +### 3.2 合一架构 + +#### 3.2.1 架构特点 + +合一架构是新的窗口管理实现方式,具有以下特点: +- **进程合一**:桌面、壁纸等系统应用与窗口服务合并在同一进程 +- **控件化管理**:系统应用转变为系统窗口控件 +- **布局驱动**:通过ArkUI的布局管线驱动窗口布局管理 + +#### 3.2.2 进程模型 +![window_process_model_unified](./docs/figures/window_process_model_unified.png) + +#### 3.2.3 启动流程 + +![合一架构启动流程](./docs/figures/WindowManager-StartSteps-Unified.png) + +**启动步骤**: +1. 点击图标,启动应用。 +2. 由窗口管理服务先创建窗口,并加载启动界面。 +3. 由窗口管理服务通知元能力管理服务,启动应用并调度不同的生命周期(例如: `onCreate`,`onForeground`)。 +4. 应用启动后,加载UI界面,并与窗口管理服务建连,替换UI界面。 +5. 过程中桌面和窗口管理服务同进程,直接控制应用窗口的启动动画。 + +#### 3.2.4 核心组件 + +**WindowScene组件**: +- 负责窗口的控件化管理 +- 实现窗口控件的布局管理 +- 提供窗口生命周期控制 + +**Screen组件**: +- 负责屏幕的控件化管理 +- 管理物理屏幕和逻辑Display的映射 +- 提供屏幕控制能力 + +#### 3.2.5 优缺点 + +**优点**: +- 减少IPC通信,性能更好 +- 启动流程简化,启动速度快 +- 利用ArkUI布局管线,布局管理更灵活 +- 适合移动和嵌入式系统 + +**缺点**: +- 进程耦合度高 +- 系统应用崩溃可能影响窗口服务 +- 调试复杂度增加 + +## 4. 各子模块架构详解 + +### 4.1 Window Manager Client(wm) + +#### 4.1.2 模块职责 + +1. **窗口对象抽象**:提供Window类,封装窗口的所有操作 +2. **接口封装**:将底层IPC通信封装为易用的API +3. **生命周期管理**:管理窗口对象的创建和销毁 +4. **事件回调**:处理窗口状态变化事件 +5. **IPC通信**:与服务端进行IPC通信 + +#### 4.1.3 协同关系 + +``` +应用代码 + ↓ +Window API (interfaces/kits) + ↓ +Window Manager Client (wm) + ↓ +IPC通信 + ↓ +Window Manager Server +``` + +### 4.2 Display Manager Client(dm) + +#### 4.2.1 模块组成 + +``` +dm/ +├── include/ # 头文件 +│ ├── display.h # Display接口定义 +│ └── display_info.h # Display信息结构 +└── src/ # 实现文件 + ├── display.cpp # Display实现 + └── display_manager.cpp # Display管理器 +``` + +#### 4.2.2 模块职责 + +1. **Display信息抽象**:提供Display类,封装Display信息查询 +2. **接口封装**:提供Display管理API +3. **IPC通信**:与Display Manager Server通信 +4. **事件监听**:监听Display变化事件 + +#### 4.2.3 协同关系 + +``` +应用代码 + ↓ +Display API (interfaces/kits) + ↓ +Display Manager Client (dm) + ↓ +IPC通信 + ↓ +Display Manager Server +``` + +### 4.3 Window Manager Server(wmserver) + +#### 4.3.1 模块组成 + +``` +wmserver/ +├── include/ # 头文件 +│ ├── window_root.h # 窗口根节点 +│ ├── window_node.h # 窗口节点 +│ ├── window_layout.h # 窗口布局管理 +│ └── ... +└── src/ # 实现文件 + ├── window_root.cpp # 窗口根节点实现 + ├── window_node.cpp # 窗口节点实现 + ├── window_layout.cpp # 窗口布局实现 + └── ... +``` + +#### 4.3.2 模块职责 + +1. **窗口树管理**:维护窗口树结构,管理父子窗口关系 +2. **窗口布局**:计算窗口位置、大小,处理窗口布局 +3. **Z序管理**:管理窗口层级,控制窗口显示顺序 +4. **焦点管理**:管理窗口焦点,处理焦点切换 +5. **输入分发**:为输入系统提供焦点窗口信息 +6. **窗口拖拽**:处理窗口拖拽逻辑 +7. **窗口快照**:提供窗口截图能力 + +#### 4.3.3 核心类说明 + +- **WindowRoot**:窗口树的根节点,管理所有顶层窗口 +- **WindowNode**:窗口节点,表示一个窗口实例 +- **WindowLayout**:窗口布局管理器,负责窗口布局计算 +- **FocusController**:焦点控制器,管理窗口焦点 + +#### 4.3.4 协同关系 + +``` +IPC通信 + ↓ +Window Manager Service + ├── WindowRoot (窗口树) + ├── WindowLayout (布局管理) + ├── FocusController (焦点管理) + └── ... + ↓ +图形系统 (RenderService) +``` + +### 4.4 Display Manager Server(dmserver) + +#### 4.4.1 模块组成 + +``` +dmserver/ +├── include/ # 头文件 +│ ├── abstract_display.h # 抽象Display +│ ├── abstract_screen.h # 抽象Screen +│ ├── display_controller.h # Display控制器 +│ └── ... +└── src/ # 实现文件 + ├── abstract_display.cpp # 抽象Display实现 + ├── abstract_screen.cpp # 抽象Screen实现 + └── ... +``` + +#### 4.4.2 模块职责 + +1. **Display管理**:管理逻辑Display,提供Display信息查询 +2. **Screen管理**:管理物理Screen,提供Screen控制 +3. **映射管理**:维护Display与Screen的映射关系 +4. **屏幕控制**:控制屏幕亮灭、亮度等 +5. **屏幕截图**:提供全屏截图功能 + +#### 4.4.3 核心类说明 + +- **AbstractDisplay**:抽象Display类,表示逻辑显示器 +- **AbstractScreen**:抽象Screen类,表示物理屏幕 +- **DisplayController**:Display控制器,管理Display生命周期 + +#### 4.4.4 协同关系 + +``` +IPC通信 + ↓ +Display Manager Service + ├── AbstractDisplay (逻辑Display) + ├── AbstractScreen (物理Screen) + └── DisplayController (控制器) + ↓ +硬件抽象层 (HDI) +``` + +### 4.5 WindowScene(window_scene) + +#### 4.5.1 模块组成 + +``` +window_scene/ +├── include/ # 头文件 +│ ├── scene_root.h # 场景根节点 +│ ├── scene_board.h # 场景面板 +│ └── ... +└── src/ # 实现文件 + ├── scene_root.cpp # 场景根节点实现 + └── scene_board.cpp # 场景面板实现 +``` + +#### 4.5.2 模块职责 + +1. **场景管理**:管理窗口场景,作为窗口控件的容器 +2. **控件化管理**:将窗口作为 `ArkUI` 控件进行管理 +3. **布局集成**:集成 `ArkUI` 布局管线,实现布局管线复用 +4. **系统控件管理**:管理桌面、壁纸等系统窗口控件 + +#### 4.5.4 协同关系 + +``` +IPC通信 + ↓ +WindowScene (ArkUI Component) + ├── RootScene (场景根) + ├── SystemWindowScene - 桌面控件 + ├── SystemWindowScene - 壁纸控件 + └── WindowScene - 应用窗口控件 + ↓ +ArkUI布局管线 + ↓ +图形渲染系统 +``` + +### 4.6 Extension(extension) + +#### 4.6.1 模块组成 + +``` +extension/ +├── extension_connection/ # ExtensionAbility组件连接部分 +│ ├── ability_connection.cpp +│ └── ... +└── window_extension/ # ExtensionAbility组件窗口部分 + ├── window_extension.cpp + └── ... +``` + +#### 4.6.2 模块职责 + +1. **Ability绑定**:实现Ability与窗口的绑定关系 +2. **生命周期同步**:同步Ability和窗口的生命周期 +3. **属性传递**:在Ability和窗口之间传递属性 + +#### 4.6.3 协同关系 + +``` +Ability框架 + ↓ +Extension + ├── ExtensionConnection (连接管理) + └── WindowExtension (窗口扩展) + ↓ +Window Manager +``` + +## 5. 开发方式 + +### 5.1 窗口属性 + +**可定制窗口属性**: +```cpp +// 窗口类型 +enum class WindowType { + TYPE_APP, // 应用窗口 + TYPE_SYSTEM_ALERT, // 系统提示窗口 + TYPE_INPUT_METHOD, // 输入法窗口 + TYPE_STATUS_BAR, // 状态栏窗口 + TYPE_PANEL, // 面板窗口 + TYPE_FLOAT, // 浮动窗口 + // ... 可根据需求扩展 +}; + +// 窗口模式 +enum class WindowMode { + UNDEFINED, + FULLSCREEN, // 全屏模式 + PRIMARY, // 分屏主窗口 + SECONDARY, // 分屏副窗口 + FLOATING, // 浮动模式 +}; + +// 窗口布局属性 +struct WindowLayoutProperty { + Rect rect; // 窗口位置和大小 + uint32_t zOrder; // 窗口层级 + WindowMode mode; // 窗口模式 + // ... 可根据需求扩展 +}; +``` + +**开发方式**: +1. 扩展 `WindowType` 枚举,添加自定义窗口类型 +2. 在Window Manager Server中添加对应的窗口类型处理逻辑 +3. 修改窗口布局算法,支持新的窗口类型 + +#### 添加自定义类型 + +**步骤1**:扩展窗口类型枚举 +```cpp +// 在 interfaces/innerkits/native/include/window/window_type.h 中 +enum class WindowType { + // ... 原有类型 + TYPE_CUSTOM_WINDOW = 1000, // 自定义窗口类型 +}; +``` + +**步骤2**:添加窗口类型处理逻辑 +```cpp +// 在 wmserver/src/window_type.cpp 中 +bool IsSystemWindow(WindowType type) +{ + // ... 原有逻辑 + if (type == WindowType::TYPE_CUSTOM_WINDOW) { + return true; + } + return false; +} +``` + +**步骤3**:在布局算法中处理新类型 +```cpp +// 在 wmserver/src/window_layout.cpp 中 +void WindowLayout::CalculateLayout(WindowNode* node) +{ + if (node->GetType() == WindowType::TYPE_CUSTOM_WINDOW) { + // 自定义布局逻辑 + CalculateCustomWindowLayout(node); + } else { + // 默认布局逻辑 + CalculateDefaultLayout(node); + } +} +``` + + +### 5.2 窗口布局算法 + +**可定制布局算法**: +```cpp +// 在 window_layout.h 中 +class WindowLayout { +public: + // 可重写的布局计算函数 + virtual void CalculateLayout(WindowNode* node); + virtual void CalculateZOrder(std::vector& nodes); + +protected: + // 布局策略 + LayoutStrategy layoutStrategy_; + + // 定制:添加自定义布局策略 + void ApplyCustomLayout(WindowNode* node); +}; +``` + +**定制步骤**: +1. 继承 `WindowLayout` 类 +2. 重写 `CalculateLayout` 方法,实现自定义布局算法 +3. 在Window Manager Server中使用自定义布局类 + +### 5.3 注意事项 + +1. **兼容性**:需要保持与原有接口的兼容性 +2. **性能**:业务逻辑不能影响系统性能 +3. **稳定性**:代码需要充分测试,确保不影响系统稳定性 +4. **可维护性**:代码需要良好的注释和文档 +5. **版本升级**:系统升级时需要考虑兼容性 ## 目录 ``` diff --git a/dm/BUILD.gn b/dm/BUILD.gn index ed66686d59..856cba5d21 100644 --- a/dm/BUILD.gn +++ b/dm/BUILD.gn @@ -159,6 +159,7 @@ ohos_shared_library("libdm") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } group("test") { @@ -189,7 +190,7 @@ ohos_shared_library("libdm_ndk") { include_dirs = [ ".", "${window_base_path}/interfaces/kits/dmndk/dm", - "${window_base_path}/interfaces/inner_kits/dm", + "${window_base_path}/interfaces/innerkits/dm", ] sources = [ "src/oh_display_manager.cpp" ] diff --git a/dm/include/display_manager_adapter.h b/dm/include/display_manager_adapter.h index e7c62edd48..8749d05d09 100644 --- a/dm/include/display_manager_adapter.h +++ b/dm/include/display_manager_adapter.h @@ -92,6 +92,7 @@ public: virtual bool ConvertScreenIdToRsScreenId(ScreenId screenId, ScreenId& rsScreenId); virtual bool IsFoldable(); virtual bool IsCaptured(); + virtual bool IsCapturedByBundleNameList(const std::vector& bundleNameList); virtual FoldStatus GetFoldStatus(); virtual FoldDisplayMode GetFoldDisplayMode(); virtual void SetFoldDisplayMode(const FoldDisplayMode); diff --git a/dm/src/display_manager.cpp b/dm/src/display_manager.cpp index 764b7e806e..e560caac9f 100644 --- a/dm/src/display_manager.cpp +++ b/dm/src/display_manager.cpp @@ -88,6 +88,7 @@ public: bool ConvertScreenIdToRsScreenId(ScreenId screenId, ScreenId& rsScreenId); bool IsFoldable(); bool IsCaptured(); + bool IsCapturedByBundleNameList(const std::vector& bundleNameList); FoldStatus GetFoldStatus(); FoldDisplayMode GetFoldDisplayMode(); FoldDisplayMode GetFoldDisplayModeForExternal(); @@ -1289,6 +1290,16 @@ bool DisplayManager::Impl::IsCaptured() return SingletonContainer::Get().IsCaptured(); } +bool DisplayManager::IsCapturedByBundleNameList(const std::vector& bundleNameList) +{ + return pImpl_->IsCapturedByBundleNameList(bundleNameList); +} + +bool DisplayManager::Impl::IsCapturedByBundleNameList(const std::vector& bundleNameList) +{ + return SingletonContainer::Get().IsCapturedByBundleNameList(bundleNameList); +} + FoldStatus DisplayManager::GetFoldStatus() { return pImpl_->GetFoldStatus(); diff --git a/dm/src/display_manager_adapter.cpp b/dm/src/display_manager_adapter.cpp index afa73deb24..8fb9677f4b 100644 --- a/dm/src/display_manager_adapter.cpp +++ b/dm/src/display_manager_adapter.cpp @@ -1245,6 +1245,17 @@ bool DisplayManagerAdapter::IsCaptured() return false; } +bool DisplayManagerAdapter::IsCapturedByBundleNameList(const std::vector& bundleNameList) +{ + INIT_PROXY_CHECK_RETURN(false); + + if (screenSessionManagerServiceProxy_) { + return screenSessionManagerServiceProxy_->IsCapturedByBundleNameList(bundleNameList); + } + + return false; +} + FoldStatus DisplayManagerAdapter::GetFoldStatus() { INIT_PROXY_CHECK_RETURN(FoldStatus::UNKNOWN); diff --git a/dm/src/screen_manager.cpp b/dm/src/screen_manager.cpp index f71b6dc569..f5f9ffe680 100644 --- a/dm/src/screen_manager.cpp +++ b/dm/src/screen_manager.cpp @@ -729,6 +729,9 @@ ScreenId ScreenManager::Impl::CreateVirtualScreen(VirtualScreenOption option) if (virtualScreenAgent_ == nullptr) { virtualScreenAgent_ = new DisplayManagerAgentDefault(); } + if (option.caller_ == VirtualScreenCaller::UNKNOWN) { + option.caller_ = VirtualScreenCaller::NATIVE_SCREEN_MANAGER; + } return SingletonContainer::Get().CreateVirtualScreen(option, virtualScreenAgent_); } diff --git a/dm/test/unittest/display_manager_test.cpp b/dm/test/unittest/display_manager_test.cpp index d5c0124e98..cdfd0636bb 100644 --- a/dm/test/unittest/display_manager_test.cpp +++ b/dm/test/unittest/display_manager_test.cpp @@ -1040,6 +1040,18 @@ HWTEST_F(DisplayManagerTest, IsCaptured01, TestSize.Level1) ASSERT_FALSE(ret); } +/** + * @tc.name: IsCapturedByBundleNameList01 + * @tc.desc: IsCapturedByBundleNameList01 fun + * @tc.type: FUNC + */ +HWTEST_F(DisplayManagerTest, IsCapturedByBundleNameList01, TestSize.Level1) +{ + std::vector bundleNameList; + auto ret = DisplayManager::GetInstance().IsCapturedByBundleNameList(bundleNameList); + ASSERT_FALSE(ret); +} + /** * @tc.name: isinsideof * @tc.desc: isinside0f fun diff --git a/dm_lite/BUILD.gn b/dm_lite/BUILD.gn index 451d701a40..c1fbd93477 100644 --- a/dm_lite/BUILD.gn +++ b/dm_lite/BUILD.gn @@ -90,6 +90,7 @@ ohos_shared_library("libdm_lite") { if (window_manager_feature_screenless) { defines += [ "SCREENLESS_ENABLE" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } group("test") { diff --git a/dmserver/BUILD.gn b/dmserver/BUILD.gn index 98d7afd2f8..5c2465aa53 100644 --- a/dmserver/BUILD.gn +++ b/dmserver/BUILD.gn @@ -137,6 +137,7 @@ ohos_shared_library("libdms") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } group("test") { diff --git a/dmserver/include/display_manager_interface_code.h b/dmserver/include/display_manager_interface_code.h index 4362c8db50..5fcc2cc1ec 100644 --- a/dmserver/include/display_manager_interface_code.h +++ b/dmserver/include/display_manager_interface_code.h @@ -151,6 +151,7 @@ enum class DisplayManagerMessage : unsigned int { TRANS_ID_GET_DEVICE_SCREEN_CONFIG, TRANS_ID_SET_VIRTUAL_SCREEN_REFRESH_RATE, TRANS_ID_DEVICE_IS_CAPTURE, + TRANS_ID_DEVICE_IS_CAPTURE_BY_BUNDLE_LIST, TRANS_ID_GET_SNAPSHOT_BY_PICKER, TRANS_ID_SWITCH_USER, TRANS_ID_SET_VIRTUAL_SCREEN_BLACK_LIST, diff --git a/docs/figures/WindowManager-Architectures-EN.png b/docs/figures/WindowManager-Architectures-EN.png index 2853ca6ee3..2069fcadcb 100644 Binary files a/docs/figures/WindowManager-Architectures-EN.png and b/docs/figures/WindowManager-Architectures-EN.png differ diff --git a/docs/figures/WindowManager-Architectures.png b/docs/figures/WindowManager-Architectures.png index 0f85ddfbc3..e697f7548c 100644 Binary files a/docs/figures/WindowManager-Architectures.png and b/docs/figures/WindowManager-Architectures.png differ diff --git a/docs/figures/WindowManager.png b/docs/figures/WindowManager.png index e6ca1d5ceb..c9e5bbc5d8 100644 Binary files a/docs/figures/WindowManager.png and b/docs/figures/WindowManager.png differ diff --git a/docs/figures/window_parts_and_relationships.png b/docs/figures/window_parts_and_relationships.png new file mode 100644 index 0000000000..5828144728 Binary files /dev/null and b/docs/figures/window_parts_and_relationships.png differ diff --git a/docs/figures/window_process_model.png b/docs/figures/window_process_model.png new file mode 100644 index 0000000000..3d0d1d0de2 Binary files /dev/null and b/docs/figures/window_process_model.png differ diff --git a/docs/figures/window_process_model_unified.png b/docs/figures/window_process_model_unified.png new file mode 100644 index 0000000000..69653e0fa0 Binary files /dev/null and b/docs/figures/window_process_model_unified.png differ diff --git a/docs/figures/window_subsystem_parts.png b/docs/figures/window_subsystem_parts.png new file mode 100644 index 0000000000..12872e7add Binary files /dev/null and b/docs/figures/window_subsystem_parts.png differ diff --git a/extension/extension_connection/BUILD.gn b/extension/extension_connection/BUILD.gn index 197a86b8e6..19eb74ff59 100644 --- a/extension/extension_connection/BUILD.gn +++ b/extension/extension_connection/BUILD.gn @@ -71,6 +71,7 @@ ohos_shared_library("libwindow_extension_client") { "ipc:ipc_single", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk_indirect" ] part_name = "window_manager" subsystem_name = "window" diff --git a/extension/modal_system_ui_extension/BUILD.gn b/extension/modal_system_ui_extension/BUILD.gn index 7da0a5b868..131f0612a2 100644 --- a/extension/modal_system_ui_extension/BUILD.gn +++ b/extension/modal_system_ui_extension/BUILD.gn @@ -68,4 +68,5 @@ ohos_shared_library("libmodal_system_ui_extension_client") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/extension/window_extension/BUILD.gn b/extension/window_extension/BUILD.gn index 65cb590bf7..ec78fee0ae 100644 --- a/extension/window_extension/BUILD.gn +++ b/extension/window_extension/BUILD.gn @@ -94,6 +94,7 @@ ohos_shared_library("libwindow_extension") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } config("window_extension_module_private_config") { @@ -129,6 +130,7 @@ ohos_shared_library("window_extension_module") { "ipc:ipc_napi", "napi:ace_napi", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] relative_install_dir = "extensionability/" subsystem_name = "window" part_name = "window_manager" diff --git a/extension/window_extension/test/unittest/BUILD.gn b/extension/window_extension/test/unittest/BUILD.gn index 54ec33f767..8c8c8b2a62 100755 --- a/extension/window_extension/test/unittest/BUILD.gn +++ b/extension/window_extension/test/unittest/BUILD.gn @@ -29,6 +29,12 @@ group("unittest") { } } +const_deps = [ + ":window_extension_unittest_common", + "${window_base_path}/utils:libwmutil_base", + "${window_base_path}/window_scene/common:window_scene_common", +] + ohos_unittest("extension_window_extension_proxy_test") { module_out_path = module_out_path @@ -39,11 +45,14 @@ ohos_unittest("extension_window_extension_proxy_test") { include_dirs = [ "${window_base_path}/window_scene/test/mock" ] - deps = [ ":window_extension_unittest_common" ] + deps = const_deps external_deps = [ + "accessibility:accessibility_common", "graphic_2d:librender_service_base", + "graphic_2d:librender_service_client", "hilog:libhilog", + "input:libmmi-client", "napi:ace_napi", ] } @@ -55,12 +64,16 @@ ohos_unittest("extension_window_extension_stub_impl_test") { include_dirs = [ "${window_base_path}/test/common/mock" ] - deps = [ ":window_extension_unittest_common" ] + deps = const_deps external_deps = [ "ability_runtime:ability_manager", + "accessibility:accessibility_common", "ace_engine:ace_uicontent", + "googletest:gmock", "graphic_2d:librender_service_base", + "graphic_2d:librender_service_client", + "input:libmmi-client", "napi:ace_napi", ] } @@ -111,6 +124,8 @@ config("window_extension_unittest_common_public_config") { "${window_base_path}/wm/test/mock", "${window_base_path}/wmserver/include", "${window_base_path}/wmserver/include/window_snapshot", + "${window_base_path}/interfaces/innerkits", + "${window_base_path}/interfaces/innerkits/dm", "${window_base_path}/interfaces/innerkits/wm", "${window_base_path}/utils/include", "${window_base_path}/window_scene", @@ -131,7 +146,7 @@ ohos_static_library("window_extension_unittest_common") { "../../../../resources/config/build:testcase_flags", ] - public_deps = [ + deps = [ "${window_base_path}/dm:libdm", "${window_base_path}/dmserver:libdms", "${window_base_path}/utils:libwmutil", @@ -141,37 +156,31 @@ ohos_static_library("window_extension_unittest_common") { "${window_base_path}/wm:libwm_static", "${window_base_path}/wm:libwm_lite", "${window_base_path}/wmserver:libwms", - ] - - deps = [ "../..:libwindow_extension", - "../../../../interfaces/kits/napi/window_runtime:window_native_kit_static", - "../../../../window_scene/session:scene_session_static", - "../../../../wm:libwm", - "../../../extension_connection:libwindow_extension_client", - ] - - public_external_deps = [ - "ability_base:want", - "c_utils:utils", - "googletest:gmock", - "googletest:gtest_main", - "graphic_2d:librender_service_client", - "image_framework:image_native", - "input:libmmi-client", - "libjpeg-turbo:turbojpeg_static", + "${window_base_path}/interfaces/kits/napi/window_runtime:window_native_kit_static", + "${window_base_path}/window_scene/session:scene_session_static", + "${window_base_path}/wm:libwm", + "${window_base_path}/extension/extension_connection:libwindow_extension_client", ] external_deps = [ "ability_base:configuration", + "ability_base:want", "ability_runtime:ability_context_native", "ability_runtime:ability_manager", "ability_runtime:app_context", "ability_runtime:runtime", "accessibility:accessibility_common", "ace_engine:ace_uicontent", + "c_utils:utils", + "googletest:gmock", + "googletest:gtest_main", + "graphic_2d:librender_service_client", "hilog:libhilog", + "image_framework:image_native", + "input:libmmi-client", "ipc:ipc_single", + "libjpeg-turbo:turbojpeg_static", "napi:ace_napi", ] subsystem_name = "window" diff --git a/interfaces/innerkits/dm/display_manager.h b/interfaces/innerkits/dm/display_manager.h index fb3e982623..3c23cca03e 100644 --- a/interfaces/innerkits/dm/display_manager.h +++ b/interfaces/innerkits/dm/display_manager.h @@ -737,6 +737,14 @@ public: */ bool IsCaptured(); + /** + * @brief Check whether the device is captured by apps in bundle name list. + * + * @param bundleNameList The list of bundle names to check. + * @return true means the device is captured by apps in the list. + */ + bool IsCapturedByBundleNameList(const std::vector& bundleNameList); + /** * @brief Get the current fold status of the foldable device. * diff --git a/interfaces/innerkits/dm/screen.h b/interfaces/innerkits/dm/screen.h index b905721f26..aa940c6c0c 100644 --- a/interfaces/innerkits/dm/screen.h +++ b/interfaces/innerkits/dm/screen.h @@ -28,6 +28,15 @@ namespace OHOS::Rosen { class ScreenInfo; +enum class VirtualScreenCaller : uint32_t { + UNKNOWN = 0, + JS_DISPLAY_MANAGER, + JS_SCREEN_MANAGER, + ANI_DISPLAY_MANAGER, + ANI_SCREEN_MANAGER, + NATIVE_SCREEN_MANAGER +}; + struct VirtualScreenOption { std::string name_; uint32_t width_; @@ -44,11 +53,12 @@ struct VirtualScreenOption { bool supportsInput_ {true}; std::string bundleName_; std::string serialNumber_; - uint32_t phyWidth_ { 0 }; - uint32_t phyHeight_ { 0 }; - int32_t userId_ {INVALID_USERID}; - int32_t screenId_ {-1}; -}; + uint32_t phyWidth_ { 0 }; + uint32_t phyHeight_ { 0 }; + int32_t userId_ {INVALID_USERID}; + int32_t screenId_ {-1}; + VirtualScreenCaller caller_ {VirtualScreenCaller::UNKNOWN}; +}; class Screen : public RefBase { friend class ScreenManager; diff --git a/interfaces/innerkits/wm/window.h b/interfaces/innerkits/wm/window.h index 2d1162a2ae..e322c9ca6b 100644 --- a/interfaces/innerkits/wm/window.h +++ b/interfaces/innerkits/wm/window.h @@ -1691,7 +1691,7 @@ public: /** * @brief Pause window */ - virtual void Pause() {} + virtual void Pause(bool isGamePreLaunch = false) {} /** * @brief Hide window @@ -5444,6 +5444,20 @@ public: return WMError::WM_ERROR_INVALID_WINDOW_TYPE; } + /** + * @brief Get Window PersistentId. + * + * @return Window PersistentId. + */ + virtual int32_t GetWindowPersistentId() const { return INVALID_WINDOW_ID; }; + + /** + * @brief Get whether this window is AtomicService. + * + * @return True means the window is AtomicService, false means the window is not AtomicService. + */ + virtual bool GetIsAtomicService() const { return false; }; + /** * @brief notify split ratio changed * diff --git a/interfaces/innerkits/wm/window_scene.h b/interfaces/innerkits/wm/window_scene.h index d97d2ca57b..5b7d55277d 100644 --- a/interfaces/innerkits/wm/window_scene.h +++ b/interfaces/innerkits/wm/window_scene.h @@ -142,7 +142,7 @@ public: * * @return the error code of window */ - WMError GoPause(); + WMError GoPause(bool isGamePreLaunch = false); /** * Window handle new want. diff --git a/interfaces/innerkits/wm/wm_common.h b/interfaces/innerkits/wm/wm_common.h index a6be0b91b3..5d8d2ba23e 100644 --- a/interfaces/innerkits/wm/wm_common.h +++ b/interfaces/innerkits/wm/wm_common.h @@ -1523,11 +1523,21 @@ struct WindowAnchorInfo : public Parcelable { struct AttachOptions : public Parcelable { std::string currentLayoutMode = ""; + bool isIntersectedHeightLimit = false; + bool isIntersectedWidthLimit = false; + AttachOptions() = default; AttachOptions(std::string currentLayoutMode) : currentLayoutMode(currentLayoutMode) {} + AttachOptions(std::string currentLayoutMode, bool isIntersectedHeightLimit, + bool isIntersectedWidthLimit) : currentLayoutMode(currentLayoutMode), + isIntersectedHeightLimit(isIntersectedHeightLimit), + isIntersectedWidthLimit(isIntersectedWidthLimit) {} + bool operator==(const AttachOptions& other) const { - return currentLayoutMode == other.currentLayoutMode; + return currentLayoutMode == other.currentLayoutMode && + isIntersectedHeightLimit == other.isIntersectedHeightLimit && + isIntersectedWidthLimit == other.isIntersectedWidthLimit; } bool operator!=(const AttachOptions& other) const @@ -1537,7 +1547,12 @@ struct WindowAnchorInfo : public Parcelable { bool Marshalling(Parcel& parcel) const override { - return parcel.WriteString(currentLayoutMode); + if (!parcel.WriteString(currentLayoutMode) || + !parcel.WriteBool(isIntersectedHeightLimit) || + !parcel.WriteBool(isIntersectedWidthLimit)) { + return false; + } + return true; } static AttachOptions* Unmarshalling(Parcel& parcel) @@ -1551,6 +1566,11 @@ struct WindowAnchorInfo : public Parcelable { return nullptr; } attachOptions->currentLayoutMode = layoutMode; + + if (!parcel.ReadBool(attachOptions->isIntersectedHeightLimit) || + !parcel.ReadBool(attachOptions->isIntersectedWidthLimit)) { + return nullptr; + } return attachOptions.release(); } }; @@ -1603,6 +1623,8 @@ struct WindowAnchorInfo : public Parcelable { } windowAnchorInfo->windowAnchor_ = static_cast(windowAnchorMode); windowAnchorInfo->attachOptions.currentLayoutMode = attachOptions->currentLayoutMode; + windowAnchorInfo->attachOptions.isIntersectedHeightLimit = attachOptions->isIntersectedHeightLimit; + windowAnchorInfo->attachOptions.isIntersectedWidthLimit = attachOptions->isIntersectedWidthLimit; return windowAnchorInfo; } }; @@ -2318,6 +2340,66 @@ struct WindowLimits { << " " << vpRatio_ << " " << static_cast(pixelUnit_) << "]"; return oss.str(); } + + bool Marshalling(Parcel& parcel) const + { + return parcel.WriteUint32(maxWidth_) && parcel.WriteUint32(maxHeight_) && + parcel.WriteUint32(minWidth_) && parcel.WriteUint32(minHeight_) && + parcel.WriteFloat(maxRatio_) && parcel.WriteFloat(minRatio_) && + parcel.WriteFloat(vpRatio_) && parcel.WriteUint32(static_cast(pixelUnit_)); + } + + static WindowLimits* Unmarshalling(Parcel& parcel) + { + auto windowLimits = std::make_unique(); + if (!windowLimits) { + return nullptr; + } + uint32_t pixelUnit = 0; + if (!parcel.ReadUint32(windowLimits->maxWidth_) || + !parcel.ReadUint32(windowLimits->maxHeight_) || + !parcel.ReadUint32(windowLimits->minWidth_) || + !parcel.ReadUint32(windowLimits->minHeight_) || + !parcel.ReadFloat(windowLimits->maxRatio_) || + !parcel.ReadFloat(windowLimits->minRatio_) || + !parcel.ReadFloat(windowLimits->vpRatio_) || + !parcel.ReadUint32(pixelUnit)) { + return nullptr; + } + // Validate pixelUnit: valid values are PX=0 and VP=1 + if (pixelUnit > static_cast(PixelUnit::VP)) { + return nullptr; + } + windowLimits->pixelUnit_ = static_cast(pixelUnit); + return windowLimits.release(); + } +}; + +/** + * @struct AttachLimitOptions + * + * @brief Options for intersecting limits with attached windows. + * Used to specify whether to intersect height/width limits. + */ +struct AttachLimitOptions { + bool isIntersectedHeightLimit = false; + bool isIntersectedWidthLimit = false; + + AttachLimitOptions() = default; + AttachLimitOptions(bool isIntersectedHeightLimit, bool isIntersectedWidthLimit) + : isIntersectedHeightLimit(isIntersectedHeightLimit), + isIntersectedWidthLimit(isIntersectedWidthLimit) {} + + bool operator==(const AttachLimitOptions& other) const + { + return isIntersectedHeightLimit == other.isIntersectedHeightLimit && + isIntersectedWidthLimit == other.isIntersectedWidthLimit; + } + + bool operator!=(const AttachLimitOptions& other) const + { + return !this->operator==(other); + } }; /** diff --git a/interfaces/kits/ani/display_runtime/BUILD.gn b/interfaces/kits/ani/display_runtime/BUILD.gn index 9d13728b0a..94a11295f3 100644 --- a/interfaces/kits/ani/display_runtime/BUILD.gn +++ b/interfaces/kits/ani/display_runtime/BUILD.gn @@ -83,6 +83,7 @@ ohos_shared_library("displayani_kit") { "runtime_core:ani_helpers", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/ani/display_runtime/display_ani/ets/@ohos.display.ets b/interfaces/kits/ani/display_runtime/display_ani/ets/@ohos.display.ets index 219ee7305c..a96bfac48b 100644 --- a/interfaces/kits/ani/display_runtime/display_ani/ets/@ohos.display.ets +++ b/interfaces/kits/ani/display_runtime/display_ani/ets/@ohos.display.ets @@ -26,6 +26,7 @@ const CUTO_ARRAY_LENGTH = 5; const CREASE_RECTS_LENGTH = 10; const DISPLAY_PHYSICAL_RESOLUTION_LENGTH = 15; const ERROR_INVALID_PARAM = 401; +const ERROR_ILLEGAL_PARAM = 1400004; const ROUNDED_CORNER_LENGTH = 4; export interface Rect { @@ -569,7 +570,22 @@ function minusRoundedCornerArray(cornerArr: Array): void { export native function isFoldable(): boolean; -export native function isCaptured(): boolean; +export native function isCapturedWithoutParam(): boolean; + +export native function isCapturedByBundleNameList(bundleNameList: Array): boolean; + +export function isCaptured(): boolean { + return isCapturedWithoutParam(); +} + +export function isCaptured(bundleNameList: Array): boolean { + const MAX_BUNDLE_NAME_LIST_SIZE = 100; + if (bundleNameList.length > MAX_BUNDLE_NAME_LIST_SIZE) { + throw new BusinessError(ERROR_ILLEGAL_PARAM, + new Error(`[ANI] The size of bundleNameList is larger than 100, actual size: ${bundleNameList.length}`)); + } + return isCapturedByBundleNameList(bundleNameList); +} export function getFoldDisplayMode(): FoldDisplayMode { let res = getFoldDisplayModeNative(); diff --git a/interfaces/kits/ani/display_runtime/display_ani/include/display_ani_manager.h b/interfaces/kits/ani/display_runtime/display_ani/include/display_ani_manager.h index 5381d0aa1f..97660bbaa1 100644 --- a/interfaces/kits/ani/display_runtime/display_ani/include/display_ani_manager.h +++ b/interfaces/kits/ani/display_runtime/display_ani/include/display_ani_manager.h @@ -34,6 +34,7 @@ public: static ani_boolean IsFoldableAni(ani_env* env); static ani_int GetFoldStatus(ani_env* env); static ani_boolean IsCaptured(ani_env* env); + static ani_boolean IsCapturedByBundleNameList(ani_env* env, ani_object bundleNameListObj); static void GetCurrentFoldCreaseRegion(ani_env* env, ani_object obj, ani_long nativeObj); static void GetAllDisplaysAni(ani_env* env, ani_object arrayObj); diff --git a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani.cpp b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani.cpp index 0542020314..13c8b1b535 100644 --- a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani.cpp +++ b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani.cpp @@ -589,14 +589,16 @@ ani_status DisplayAni::NspBindNativeFunctions(ani_env* env, ani_namespace nsp) reinterpret_cast(DisplayManagerAni::AddVirtualScreenBlocklist)}, ani_native_function {"removeVirtualScreenBlocklistNative", nullptr, reinterpret_cast(DisplayManagerAni::RemoveVirtualScreenBlocklist)}, - ani_native_function {"isCaptured", nullptr, reinterpret_cast(DisplayManagerAni::IsCaptured)}, + ani_native_function {"isCapturedByBundleNameList", nullptr, + reinterpret_cast(DisplayManagerAni::IsCaptured)}, ani_native_function {"finalizerDisplayNative", nullptr, reinterpret_cast(DisplayManagerAni::FinalizerDisplay)}, ani_native_function {"onChangeWithAttributeNative", nullptr, reinterpret_cast(DisplayManagerAni::RegisterDisplayAttributeListener)}, ani_native_function {"displayInfoFinalizerCallback", nullptr, reinterpret_cast(DisplayAni::CleanDisplayInfoMap)}, - + ani_native_function {"isCapturedByBundleNameList", nullptr, + reinterpret_cast(DisplayManagerAni::IsCapturedByBundleNameList)}, }; auto ret = env->Namespace_BindNativeFunctions(nsp, funcs.data(), funcs.size()); if (ret != ANI_OK) { diff --git a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_listener.cpp b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_listener.cpp index 18baa19101..80738039f7 100644 --- a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_listener.cpp +++ b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_listener.cpp @@ -119,7 +119,7 @@ void DisplayAniListener::OnCreate(DisplayId id) return; } std::vector vec = it->second; - TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}d", vec.size()); + TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}zu", vec.size()); // find callbacks in vector for (ani_ref oneAniCallback : vec) { if (vm_ == nullptr) { @@ -176,7 +176,7 @@ void DisplayAniListener::OnDestroy(DisplayId id) return; } std::vector vec = it->second; - TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}d", vec.size()); + TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}zu", vec.size()); // find callbacks in vector for (ani_ref oneAniCallback : vec) { if (vm_ == nullptr) { diff --git a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_manager.cpp b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_manager.cpp index 2088b8a3d0..97e5e72238 100644 --- a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_manager.cpp +++ b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_manager.cpp @@ -154,6 +154,28 @@ ani_boolean DisplayManagerAni::IsCaptured(ani_env* env) return static_cast(isCapture); } +ani_boolean DisplayManagerAni::IsCapturedByBundleNameList(ani_env* env, ani_object bundleNameListObj) +{ + TLOGI(WmsLogTag::DMS, "[ANI] IsCapturedByBundleNameList begin"); + if (env == nullptr) { + TLOGE(WmsLogTag::DMS, "[ANI] env is nullptr"); + return false; + } + + std::vector bundleNameList; + ani_status ret = DisplayAniUtils::GetStdStringVector(env, bundleNameListObj, bundleNameList); + if (ret != ANI_OK) { + TLOGE(WmsLogTag::DMS, "[ANI] GetStdStringVector fail"); + AniErrUtils::ThrowBusinessError(env, DmErrorCode::DM_ERROR_INVALID_PARAM, "Failed to convert attributes"); + return false; + } + + bool isCapture = SingletonContainer::Get().IsCapturedByBundleNameList(bundleNameList); + TLOGI(WmsLogTag::DMS, "[ANI] BundleNameList size: %{public}zu, isCapturedByBundleNameList: %{public}u.", + bundleNameList.size(), isCapture); + return static_cast(isCapture); +} + ani_int DisplayManagerAni::GetFoldStatus(ani_env* env) { auto status = SingletonContainer::Get().GetFoldStatus(); @@ -896,6 +918,7 @@ ani_long DisplayManagerAni::OnCreateVirtualScreen(ani_env* env, ani_object virtu return static_cast(screenId); } VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::ANI_DISPLAY_MANAGER; DmErrorCode errCode = DisplayAniUtils::GetVirtualScreenOptionFromAni(env, virtualScreenConfig, option); if (errCode == DmErrorCode::DM_ERROR_INVALID_PARAM) { TLOGE(WmsLogTag::DMS, "[ANI] Get virtual screen option from ani failed"); diff --git a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_utils.cpp b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_utils.cpp index e3e9474790..be038b7229 100644 --- a/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_utils.cpp +++ b/interfaces/kits/ani/display_runtime/display_ani/src/display_ani_utils.cpp @@ -451,7 +451,7 @@ ani_object DisplayAniUtils::CreateAniUndefined(ani_env* env) static DmErrorCode GetFocusFromAni(ani_env* env, ani_object virtualScreenObj, VirtualScreenOption& option) { ani_ref supportsFocus = nullptr; - if (env->Object_GetPropertyByName_Ref(virtualScreenObj, "%%property-supportsFocus", &supportsFocus) != ANI_OK) { + if (env->Object_GetPropertyByName_Ref(virtualScreenObj, "supportsFocus", &supportsFocus) != ANI_OK) { TLOGE(WmsLogTag::DMS, "Failed to get supportsFocus."); return DmErrorCode::DM_ERROR_INVALID_PARAM; } diff --git a/interfaces/kits/ani/embeddable_window_stage/BUILD.gn b/interfaces/kits/ani/embeddable_window_stage/BUILD.gn index c01b99bd78..1866c49efc 100644 --- a/interfaces/kits/ani/embeddable_window_stage/BUILD.gn +++ b/interfaces/kits/ani/embeddable_window_stage/BUILD.gn @@ -87,6 +87,7 @@ ohos_shared_library("embeddablewindowstageani_kit") { "runtime_core:ani_helpers", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/ani/floating_ball/BUILD.gn b/interfaces/kits/ani/floating_ball/BUILD.gn index c2d8a977b6..cf8f17afe9 100644 --- a/interfaces/kits/ani/floating_ball/BUILD.gn +++ b/interfaces/kits/ani/floating_ball/BUILD.gn @@ -10,13 +10,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - + import("//build/ohos.gni") import("../../../../windowmanager_aafwk.gni") - + config("fbwindow_common_config") { visibility = [ ":*" ] - + include_dirs = [ "../common", "../../../../wm/include", @@ -24,10 +24,10 @@ config("fbwindow_common_config") { "../../arkui/ace_engine/interfaces/inner_api/xcomponent_controller", ] } - + config("fbwindow_kit_public_config") { visibility = [ ":*" ] - + include_dirs = [ "floating_ball_ani/include" ] } @@ -41,21 +41,21 @@ ohos_shared_library("fbwindowani_kit") { debug = false cfi_policy = "adaptive" } - sources = [ + sources = [ "floating_ball_ani/src/ani_fb_window.cpp", "floating_ball_ani/src/ani_fb_window_controller.cpp", "floating_ball_ani/src/ani_fb_window_utils.cpp", "floating_ball_ani/src/ani_fb_window_listener.cpp" ] - + configs = [ ":fbwindow_common_config", ":fbwindow_kit_public_config", "../../../../resources/config/build:coverage_flags", ] - + public_configs = [ ":fbwindow_kit_public_config" ] - + deps = [ "${window_base_path}/wm:libwm", "${window_base_path}/window_scene/interfaces/innerkits:libwsutils", @@ -92,6 +92,7 @@ ohos_shared_library("fbwindowani_kit") { "runtime_core:ani_helpers", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/ani/picture_in_picture_runtime/BUILD.gn b/interfaces/kits/ani/picture_in_picture_runtime/BUILD.gn index 766c523ee7..7768565c90 100644 --- a/interfaces/kits/ani/picture_in_picture_runtime/BUILD.gn +++ b/interfaces/kits/ani/picture_in_picture_runtime/BUILD.gn @@ -42,7 +42,7 @@ ohos_shared_library("pipwindowani_kit") { debug = false cfi_policy = "adaptive" } - sources = [ + sources = [ "pipwindow_ani/src/ani_pip_window.cpp", "pipwindow_ani/src/ani_pip_controller.cpp", "pipwindow_ani/src/ani_pip_utils.cpp", @@ -90,6 +90,7 @@ ohos_shared_library("pipwindowani_kit") { "runtime_core:ani_helpers", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_listener.cpp b/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_listener.cpp index 37d9ef9a5a..9f6bad45f9 100644 --- a/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_listener.cpp +++ b/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_listener.cpp @@ -96,7 +96,7 @@ void ScreenAniListener::OnConnect(ScreenId id) return; } std::vector vec = it->second; - TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}d", vec.size()); + TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}zu", vec.size()); // find callbacks in vector for (auto oneAniCallback : vec) { if (vm_ == nullptr) { @@ -155,7 +155,7 @@ void ScreenAniListener::OnDisconnect(ScreenId id) return; } std::vector vec = it->second; - TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}d", vec.size()); + TLOGI(WmsLogTag::DMS, "vec_callback size: %{public}zu", vec.size()); // find callbacks in vector for (auto oneAniCallback : vec) { if (vm_ == nullptr) { diff --git a/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_manager.cpp b/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_manager.cpp index a78f26023a..6c47dc2c9f 100644 --- a/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_manager.cpp +++ b/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_manager.cpp @@ -319,6 +319,7 @@ void ScreenManagerAni::CreateVirtualScreen(ani_env* env, ani_object options, ani } VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::ANI_SCREEN_MANAGER; auto ret = ScreenAniUtils::GetVirtualScreenOption(env, options, option); if (ret != DmErrorCode::DM_OK) { TLOGE(WmsLogTag::DMS, "[ANI] Get virtual screen options failed"); diff --git a/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_utils.cpp b/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_utils.cpp index b631ff7412..a97adce35c 100644 --- a/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_utils.cpp +++ b/interfaces/kits/ani/screen_runtime/screen_ani/src/screen_ani_utils.cpp @@ -253,7 +253,7 @@ ani_enum_item ScreenAniUtils::CreateAniEnum(ani_env* env, const char* enum_descr static DmErrorCode GetScreenFocusFromAni(ani_env* env, ani_object virtualScreenObj, VirtualScreenOption& option) { ani_ref focus = nullptr; - if (env->Object_GetPropertyByName_Ref(virtualScreenObj, "%%property-supportsFocus", &focus) != ANI_OK) { + if (env->Object_GetPropertyByName_Ref(virtualScreenObj, "supportsFocus", &focus) != ANI_OK) { TLOGE(WmsLogTag::DMS, "Failed to get supportsFocus."); return DmErrorCode::DM_ERROR_INVALID_PARAM; } diff --git a/interfaces/kits/ani/screenshot_runtime/BUILD.gn b/interfaces/kits/ani/screenshot_runtime/BUILD.gn index 005ddef049..1dcdc8ad5d 100644 --- a/interfaces/kits/ani/screenshot_runtime/BUILD.gn +++ b/interfaces/kits/ani/screenshot_runtime/BUILD.gn @@ -78,6 +78,7 @@ ohos_shared_library("screenshotani_kit") { "runtime_core:ani_helpers", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/ani/window_animation/BUILD.gn b/interfaces/kits/ani/window_animation/BUILD.gn index 9f6f72ce04..ab104180cf 100644 --- a/interfaces/kits/ani/window_animation/BUILD.gn +++ b/interfaces/kits/ani/window_animation/BUILD.gn @@ -61,4 +61,5 @@ ohos_shared_library("ani_window_animation_utils") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/interfaces/kits/ani/window_runtime/BUILD.gn b/interfaces/kits/ani/window_runtime/BUILD.gn index 0621f46111..9f65ef82af 100644 --- a/interfaces/kits/ani/window_runtime/BUILD.gn +++ b/interfaces/kits/ani/window_runtime/BUILD.gn @@ -100,6 +100,7 @@ ohos_shared_library("windowstageani_kit") { "runtime_core:ani_helpers", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/ani/window_runtime/window_stage_ani/ets/@ohos.window.ets b/interfaces/kits/ani/window_runtime/window_stage_ani/ets/@ohos.window.ets index 521932db9d..0783eae587 100644 --- a/interfaces/kits/ani/window_runtime/window_stage_ani/ets/@ohos.window.ets +++ b/interfaces/kits/ani/window_runtime/window_stage_ani/ets/@ohos.window.ets @@ -568,6 +568,8 @@ namespace window { currentLayoutMode?: string; parentWindowSizeChangeCallback? :Callback; parentWindowStatusChangeCallback? :Callback; + isIntersectedHeightLimit?: boolean; + isIntersectedWidthLimit?: boolean; } export interface Rect { @@ -982,6 +984,7 @@ export interface WindowInfo { globalRect?: Rect; displayId?: long; bundleName: string; + moduleName: string; abilityName: string; windowId: int; windowStatusType: WindowStatusType; @@ -994,6 +997,7 @@ export class WindowInfoInternal implements WindowInfo { globalRect?: Rect; displayId?: long; bundleName: string; + moduleName: string; abilityName: string; windowId: int; windowStatusType: WindowStatusType; diff --git a/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window.cpp b/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window.cpp index cdc7c6cc57..33928d45fe 100644 --- a/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window.cpp +++ b/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window.cpp @@ -5416,6 +5416,49 @@ void AniWindow::OnSetRelativePositionToParentWindowEnabled(ani_env* env, ani_boo } } +static void RegisterAttachOptionCallbacks(sptr windowToken, ani_env* env, ani_object attachOptions, + std::unique_ptr& registerManager) +{ + auto getPropertyAndCheckUndefined = [env](ani_object obj, const char* propName, + ani_ref& outRef, std::string_view errorPrefix) -> bool { + if (env->Object_GetPropertyByName_Ref(obj, propName, &outRef) != ANI_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "%s: Failed to get %s.", errorPrefix.data(), propName); + AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM, + std::string("Failed to get") + propName + "."); + return false; + } + ani_boolean isUndefined; + if (env->Reference_IsUndefined(outRef, &isUndefined) != ANI_OK || isUndefined) { + TLOGE(WmsLogTag::WMS_LAYOUT, "[ANI] Check %s isUndefined fail", propName); + AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); + return false; + } + return true; + }; + + ani_ref parentWindowSizeChangeCallback; + if (!getPropertyAndCheckUndefined(attachOptions, "parentWindowSizeChangeCallback", + parentWindowSizeChangeCallback, "parentWindowSizeChangeCallback")) { + return; + } + if (parentWindowSizeChangeCallback && registerManager->RegisterListener(windowToken, "parentWindowSizeChange", + CaseType::CASE_WINDOW, env, parentWindowSizeChangeCallback, 0) != WmErrorCode::WM_OK) { + AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); + return; + } + + ani_ref parentWindowStatusChangeCallback; + if (!getPropertyAndCheckUndefined(attachOptions, "parentWindowStatusChangeCallback", + parentWindowStatusChangeCallback, "parentWindowStatusChangeCallback")) { + return; + } + if (parentWindowStatusChangeCallback && registerManager->RegisterListener(windowToken, "parentWindowStatusChange", + CaseType::CASE_WINDOW, env, parentWindowStatusChangeCallback, 0) != WmErrorCode::WM_OK) { + AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); + return; + } +} + void AniWindow::AttachLayoutToParentWindow(ani_env* env, ani_object Obj, ani_long nativeObj, ani_object anchorInfo, ani_object attachOptions) { @@ -5432,22 +5475,35 @@ void AniWindow::AttachLayoutToParentWindow(ani_env* env, ani_object Obj, ani_lon static void ParseAttachOptions(sptr windowToken, ani_env* env, ani_object attachOptions, std::unique_ptr& registerManager, WindowAnchorInfo::AttachOptions& options) { - auto getPropertyAndCheckUndefined = [env](ani_object obj, const char* propName, - ani_ref& outRef, std::string_view errorPrefix) -> bool { - if (env->Object_GetPropertyByName_Ref(obj, propName, &outRef) != ANI_OK) { - TLOGE(WmsLogTag::WMS_LAYOUT, "%s: Failed to get %s.", errorPrefix.data(), propName); - AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM, - std::string("Failed to get") + propName + "."); - return false; - } - ani_boolean isUndefined; - if (env->Reference_IsUndefined(outRef, &isUndefined) != ANI_OK || isUndefined) { - TLOGE(WmsLogTag::WMS_LAYOUT, "[ANI] Check %s isUndefined fail", propName); + // Helper lambda to parse boolean property from attachOptions + auto parseBooleanProperty = [env, attachOptions](const char* propName, bool& outValue) -> bool { + ani_ref valueRet; + if (env->Object_GetPropertyByName_Ref(attachOptions, propName, &valueRet) != ANI_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Failed to get %s.", propName); + AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM, + std::string("Failed to get ") + propName + "."); + return false; + } + ani_boolean isUndefined; + if (env->Reference_IsUndefined(valueRet, &isUndefined) != ANI_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "[ANI] Check %s isUndefined fail", propName); + AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); + return false; + } + if (!isUndefined) { + ani_boolean boolValue; + if (env->Object_CallMethodByName_Boolean(static_cast(valueRet), + "toBoolean", ":z", &boolValue) != ANI_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Failed to get boolean value for %s.", propName); AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); return false; } - return true; - }; + outValue = static_cast(boolValue); + } + return true; + }; + + // Parse currentLayoutMode ani_ref nameValueRet; if (env->Object_GetPropertyByName_Ref(attachOptions, "currentLayoutMode", &nameValueRet) != ANI_OK) { TLOGE(WmsLogTag::WMS_LAYOUT, "Failed to get currentLayoutMode."); @@ -5466,26 +5522,18 @@ static void ParseAttachOptions(sptr windowToken, ani_env* env, ani_objec static_cast(nameValueRet)); options.currentLayoutMode = currentLayoutMode; } - ani_ref parentWindowSizeChangeCallback; - if (!getPropertyAndCheckUndefined(attachOptions, "parentWindowSizeChangeCallback", - parentWindowSizeChangeCallback, "parentWindowSizeChangeCallback")) { + + // Parse isIntersectedHeightLimit + if (!parseBooleanProperty("isIntersectedHeightLimit", options.isIntersectedHeightLimit)) { return; } - if (parentWindowSizeChangeCallback && registerManager->RegisterListener(windowToken, "parentWindowSizeChange", - CaseType::CASE_WINDOW, env, parentWindowSizeChangeCallback, 0) != WmErrorCode::WM_OK) { - AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); - return; - } - ani_ref parentWindowStatusChangeCallback; - if (!getPropertyAndCheckUndefined(attachOptions, "parentWindowStatusChangeCallback", - parentWindowStatusChangeCallback, "parentWindowStatusChangeCallback")) { - return; - } - if (parentWindowStatusChangeCallback && registerManager->RegisterListener(windowToken, "parentWindowStatusChange", - CaseType::CASE_WINDOW, env, parentWindowStatusChangeCallback, 0) != WmErrorCode::WM_OK) { - AniWindowUtils::AniThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM); + + // Parse isIntersectedWidthLimit + if (!parseBooleanProperty("isIntersectedWidthLimit", options.isIntersectedWidthLimit)) { return; } + + RegisterAttachOptionCallbacks(windowToken, env, attachOptions, registerManager); } void AniWindow::OnAttachToParentWindow(ani_env* env, ani_object anchorInfo, @@ -5531,10 +5579,17 @@ void AniWindow::OnAttachToParentWindow(ani_env* env, ani_object anchorInfo, WindowAnchorInfo windowAnchorInfo = { true, true, acceptWindowAnchorInfo.windowAnchor_, acceptWindowAnchorInfo.offsetX_, acceptWindowAnchorInfo.offsetY_}; windowAnchorInfo.attachOptions.currentLayoutMode = acceptWindowAnchorInfo.attachOptions.currentLayoutMode; + windowAnchorInfo.attachOptions.isIntersectedHeightLimit = + acceptWindowAnchorInfo.attachOptions.isIntersectedHeightLimit; + windowAnchorInfo.attachOptions.isIntersectedWidthLimit = + acceptWindowAnchorInfo.attachOptions.isIntersectedWidthLimit; windowAnchorInfo.isFromAttachOrDetach_ = true; - TLOGI(WmsLogTag::WMS_LAYOUT, "windowAnchorInfo %{public}d, offsetX:%{public}d, offset:%{public}d currentLayoutMode:" - "%{public}s", windowAnchorInfo.windowAnchor_, windowAnchorInfo.offsetX_, windowAnchorInfo.offsetY_, - windowAnchorInfo.attachOptions.currentLayoutMode.c_str()); + TLOGI(WmsLogTag::WMS_LAYOUT, "windowAnchorInfo %{public}d, offsetX:%{public}d, offset:%{public}d " + "currentLayoutMode:%{public}s, isIntersectedHeightLimit:%{public}d, isIntersectedWidthLimit:%{public}d", + windowAnchorInfo.windowAnchor_, windowAnchorInfo.offsetX_, windowAnchorInfo.offsetY_, + windowAnchorInfo.attachOptions.currentLayoutMode.c_str(), + windowAnchorInfo.attachOptions.isIntersectedHeightLimit, + windowAnchorInfo.attachOptions.isIntersectedWidthLimit); auto setWindowAnchorInfoRet = windowToken_->SetWindowAnchorInfo(windowAnchorInfo); auto it = WM_JS_TO_ERROR_CODE_MAP.find(setWindowAnchorInfoRet); WmErrorCode errorCode = (it != WM_JS_TO_ERROR_CODE_MAP.end()) ? it->second : WmErrorCode::WM_ERROR_STATE_ABNORMALLY; diff --git a/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window_utils.cpp b/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window_utils.cpp index 823f65031e..858ccd1f21 100644 --- a/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window_utils.cpp +++ b/interfaces/kits/ani/window_runtime/window_stage_ani/src/ani_window_utils.cpp @@ -873,6 +873,12 @@ ani_object AniWindowUtils::CreateAniWindowInfo(ani_env* env, const WindowVisibil return AniWindowUtils::CreateAniUndefined(env); } CallAniMethodVoid(env, windowInfo, cls, Builder::BuildSetterName("bundleName").c_str(), nullptr, bundleName); + ani_string moduleName; + if (GetAniString(env, info.GetModuleName(), &moduleName) != ANI_OK) { + TLOGE(WmsLogTag::WMS_ATTRIBUTE, "[ANI] create string failed"); + return AniWindowUtils::CreateAniUndefined(env); + } + CallAniMethodVoid(env, windowInfo, cls, Builder::BuildSetterName("moduleName").c_str(), nullptr, moduleName); ani_string abilityName; if (GetAniString(env, info.GetAbilityName(), &abilityName) != ANI_OK) { TLOGE(WmsLogTag::WMS_ATTRIBUTE, "[ANI] create string failed"); diff --git a/interfaces/kits/cj/display_runtime/BUILD.gn b/interfaces/kits/cj/display_runtime/BUILD.gn index 865d251f5f..48f9b1b306 100644 --- a/interfaces/kits/cj/display_runtime/BUILD.gn +++ b/interfaces/kits/cj/display_runtime/BUILD.gn @@ -65,6 +65,7 @@ ohos_shared_library("cj_display_ffi") { sources = [ "display_runtime_mock.cpp" ] external_deps = [ "napi:cj_bind_ffi" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/cj/screenshot/BUILD.gn b/interfaces/kits/cj/screenshot/BUILD.gn index 731ba6fe9b..0956de8e85 100644 --- a/interfaces/kits/cj/screenshot/BUILD.gn +++ b/interfaces/kits/cj/screenshot/BUILD.gn @@ -50,6 +50,7 @@ ohos_shared_library("cj_screenshot_ffi") { sources = [ "display_runtime_mock.cpp" ] external_deps = [ "napi:cj_bind_ffi" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/cj/window_runtime/BUILD.gn b/interfaces/kits/cj/window_runtime/BUILD.gn index 0729503034..88d811cb40 100644 --- a/interfaces/kits/cj/window_runtime/BUILD.gn +++ b/interfaces/kits/cj/window_runtime/BUILD.gn @@ -79,6 +79,7 @@ ohos_shared_library("cj_window_ffi") { sources = [ "window_mock.cpp" ] external_deps = [ "napi:cj_bind_ffi" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/napi/BUILD.gn b/interfaces/kits/napi/BUILD.gn index acfd18535b..489012474d 100644 --- a/interfaces/kits/napi/BUILD.gn +++ b/interfaces/kits/napi/BUILD.gn @@ -18,6 +18,7 @@ group("napi_packages") { "display_runtime:display_napi", "embeddable_window_stage:embeddablewindowstage_kit", "environmental:windowenv_packages", + "environmental/inner:windowenv_napi", "extension:uiextension_napi", "extension_window:extensionwindow_napi", "picture_in_picture_napi:pipwindow_napi", diff --git a/interfaces/kits/napi/display_runtime/BUILD.gn b/interfaces/kits/napi/display_runtime/BUILD.gn index e29a3c796e..079204bef4 100644 --- a/interfaces/kits/napi/display_runtime/BUILD.gn +++ b/interfaces/kits/napi/display_runtime/BUILD.gn @@ -61,7 +61,7 @@ ohos_shared_library("display_napi") { "graphic_surface:surface", "hilog:libhilog", "hitrace:hitrace_meter", - "image_framework:image_native", + "image_framework:image_native", "napi:ace_napi", ] @@ -111,6 +111,7 @@ ohos_shared_library("display_kit") { "napi:ace_napi", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/napi/display_runtime/js_display_manager.cpp b/interfaces/kits/napi/display_runtime/js_display_manager.cpp index c0be5cd3bd..f835ac6de3 100644 --- a/interfaces/kits/napi/display_runtime/js_display_manager.cpp +++ b/interfaces/kits/napi/display_runtime/js_display_manager.cpp @@ -46,6 +46,7 @@ constexpr size_t ARGC_TWO = 2; constexpr size_t ARGC_THREE = 3; constexpr size_t ARGS_MAX = 4; constexpr int32_t INDEX_ONE = 1; +constexpr size_t BUNDLE_NAME_LIST_MAX_SIZE = 100; class JsDisplayManager { public: explicit JsDisplayManager(napi_env env) { @@ -1026,16 +1027,51 @@ napi_value OnIsFoldable(napi_env env, napi_callback_info info) napi_value OnIsCaptured(napi_env env, napi_callback_info info) { std::string functionName = "isCaptured"; - size_t argc = 4; // default arg length - napi_value argv[4] = { nullptr }; // default arg length + size_t argc = ARGC_ONE; + napi_value argv[ARGC_ONE] = { nullptr }; napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); - if (argc >= ARGC_ONE) { - napi_throw(env, JsErrUtils::CreateJsError(env, DmErrorCode::DM_ERROR_INVALID_PARAM, - GetFormatMsg(functionName, "Input parameter invalid"))); - return NapiGetUndefined(env); + + if (argc > ARGC_ONE) { + return NapiThrowError(env, DmErrorCode::DM_ERROR_ILLEGAL_PARAM, + "Input parameter invalid", functionName); } - bool isCapture = SingletonContainer::Get().IsCaptured(); - TLOGD(WmsLogTag::DMS, "[NAPI]IsCaptured = %{public}u", isCapture); + + if (argc == 0) { + bool isCapture = SingletonContainer::Get().IsCaptured(); + TLOGD(WmsLogTag::DMS, "[NAPI]IsCaptured = %{public}u", isCapture); + napi_value result; + napi_get_boolean(env, isCapture, &result); + return result; + } + + napi_value nativeArray = argv[0]; + uint32_t size = 0; + if (GetType(env, nativeArray) != napi_object || + napi_get_array_length(env, nativeArray, &size) != napi_ok) { + return NapiThrowError(env, DmErrorCode::DM_ERROR_ILLEGAL_PARAM, + "Failed to convert parameter to bundleNameList array", functionName); + } + + if (size > BUNDLE_NAME_LIST_MAX_SIZE) { + return NapiThrowError(env, DmErrorCode::DM_ERROR_ILLEGAL_PARAM, + "The size of bundleNameList is larger than 100", functionName); + } + + std::vector bundleNameList; + for (uint32_t i = 0; i < size; i++) { + std::string bundleName; + napi_value element = nullptr; + napi_get_element(env, nativeArray, i, &element); + if (!ConvertFromJsValue(env, element, bundleName)) { + return NapiThrowError(env, DmErrorCode::DM_ERROR_ILLEGAL_PARAM, + "Failed to convert parameter to bundle name", functionName); + } + bundleNameList.push_back(bundleName); + } + + bool isCapture = SingletonContainer::Get().IsCapturedByBundleNameList(bundleNameList); + TLOGI(WmsLogTag::DMS, "[NAPI] BundleNameList size: %{public}zu, isCapturedByBundleNameList: %{public}u.", + bundleNameList.size(), isCapture); napi_value result; napi_get_boolean(env, isCapture, &result); return result; @@ -1194,6 +1230,7 @@ napi_value OnCreateVirtualScreen(napi_env env, napi_callback_info info) TLOGI(WmsLogTag::DMS, "called"); DmErrorCode errCode = DmErrorCode::DM_OK; VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::JS_DISPLAY_MANAGER; size_t argc = 4; std::string errMsg = ""; napi_value argv[4] = {nullptr}; diff --git a/interfaces/kits/napi/embeddable_window_stage/BUILD.gn b/interfaces/kits/napi/embeddable_window_stage/BUILD.gn index 58b8909370..882ce7966b 100644 --- a/interfaces/kits/napi/embeddable_window_stage/BUILD.gn +++ b/interfaces/kits/napi/embeddable_window_stage/BUILD.gn @@ -127,4 +127,5 @@ ohos_shared_library("embeddablewindowstage_kit") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/interfaces/kits/napi/environmental/BUILD.gn b/interfaces/kits/napi/environmental/BUILD.gn index 226dcd5138..96fda07067 100644 --- a/interfaces/kits/napi/environmental/BUILD.gn +++ b/interfaces/kits/napi/environmental/BUILD.gn @@ -25,6 +25,14 @@ name_mapping = [ { js_file = "$target_out_dir/engine/WindowFocusEnv.js" abc_name = "windowfocusenv" + }, + { + js_file = "$target_out_dir/engine/SystemDensityEnv.js" + abc_name = "systemdensityenv" + }, + { + js_file = "$target_out_dir/engine/DisplayIdEnv.js" + abc_name = "displayidenv" } ] diff --git a/interfaces/kits/napi/environmental/inner/BUILD.gn b/interfaces/kits/napi/environmental/inner/BUILD.gn new file mode 100644 index 0000000000..a2ff68ccb7 --- /dev/null +++ b/interfaces/kits/napi/environmental/inner/BUILD.gn @@ -0,0 +1,62 @@ +# Copyright (c) 2026 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("../../../../../windowmanager_aafwk.gni") + +config("windowenv_manager_config") { + visibility = [ ":*" ] + include_dirs = [ "../../../../../wm/include" ] +} + +ohos_shared_library("windowenv_napi") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + sources = [ + "js_windowenv_manager.cpp", + "js_windowenv_module.cpp", + ] + + deps = [ + "../../../../../utils:libwmutil", + "../../../../../utils:libwmutil_base", + "../../../../../wm:libwm", + "../../extension_window:extensionwindow_napi", + "../../window_runtime:window_native_kit", + ] + + external_deps = [ + "ability_runtime:ability_context_native", + "ability_runtime:abilitykit_native", + "ability_runtime:runtime", + "ace_engine:ace_uicontent", + "ace_engine:ace_xcomponent_controller", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_single", + "napi:ace_napi", + ] + relative_install_dir = "module" + part_name = "window_manager" + subsystem_name = "window" + + defines = [] + if (build_variant == "user") { + defines += [ "IS_RELEASE_VERSION" ] + } +} diff --git a/interfaces/kits/napi/environmental/inner/js_windowenv_manager.cpp b/interfaces/kits/napi/environmental/inner/js_windowenv_manager.cpp new file mode 100644 index 0000000000..5ab10d10f7 --- /dev/null +++ b/interfaces/kits/napi/environmental/inner/js_windowenv_manager.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_windowenv_manager.h" + +#include "dm_common.h" +#include "js_extension_window.h" +#include "js_window.h" +#include "ui_content.h" +#include "wm_common.h" +#include "window_manager_hilog.h" + +namespace OHOS { +namespace Rosen { +using namespace AbilityRuntime; +namespace { +const int32_t INVALID_INSTANCEID = -1; +constexpr size_t ARGC_ONE = 1; +constexpr size_t ARGC_FOUR = 4; +} +napi_value FindJsExtensionWindowById(napi_env env, int32_t id); + +static napi_value CreateJsNumber(napi_env env, uint64_t value) +{ + napi_value result = nullptr; + napi_create_int64(env, static_cast(value), &result); + return result; +} + +napi_value NapiGetUndefined(napi_env env) +{ + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; +} + +sptr GetWindowByInstanceId(int32_t instanceId) +{ + int32_t windowId = Ace::UIContent::GetUIContentWindowID(instanceId); + auto uicontent = Ace::UIContent::GetUIContent(instanceId); + if (!uicontent) { + TLOGE(WmsLogTag::DEFAULT, "uicontent nullptr instanceId: %{public}d, windowId: %{public}d", + instanceId, windowId); + return nullptr; + } + auto window = sptr(uicontent->GetUIContentWindow()); + if (window == nullptr) { + TLOGE(WmsLogTag::DEFAULT, "window nullptr %{public}d", windowId); + return nullptr; + } + return window; +} + +JsWindowEnvManager::JsWindowEnvManager() +{ +} + +JsWindowEnvManager::~JsWindowEnvManager() +{ +} + +void JsWindowEnvManager::Finalizer(napi_env env, void* data, void* hint) +{ + TLOGD(WmsLogTag::DEFAULT, "Finalizer"); + std::unique_ptr(static_cast(data)); +} + +napi_value JsWindowEnvManager::FindWindowById(napi_env env, napi_callback_info info) +{ + JsWindowEnvManager* me = CheckParamsAndGetThis(env, info); + return (me != nullptr) ? me->OnFindWindowById(env, info) : nullptr; +} + +napi_value JsWindowEnvManager::OnFindWindowById(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_FOUR; + napi_value argv[ARGC_FOUR] = { nullptr }; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + if (argc < ARGC_ONE) { + TLOGE(WmsLogTag::DEFAULT, "Invalid param size"); + return NapiGetUndefined(env); + } + int32_t instanceId = INVALID_INSTANCEID; + if (!ConvertFromJsValue(env, argv[0], instanceId) || instanceId == INVALID_INSTANCEID) { + TLOGE(WmsLogTag::DEFAULT, "invalid instanceId value: %{public}d", instanceId); + return NapiGetUndefined(env); + } + auto window = GetWindowByInstanceId(instanceId); + if (window == nullptr) { + TLOGE(WmsLogTag::DEFAULT, "window nullptr"); + return nullptr; + } + if (window->GetType() == WindowType::WINDOW_TYPE_UI_EXTENSION) { + return FindJsExtensionWindowById(env, window->GetWindowPersistentId()); + } + return CreateJsWindowObject(env, window); +} + + +napi_value JsWindowEnvManager::GetDisplayId(napi_env env, napi_callback_info info) +{ + JsWindowEnvManager* me = CheckParamsAndGetThis(env, info); + return (me != nullptr) ? me->OnGetDisplayId(env, info) : nullptr; +} + +napi_value JsWindowEnvManager::OnGetDisplayId(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_FOUR; + napi_value argv[ARGC_FOUR] = { nullptr }; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + if (argc < ARGC_ONE) { + TLOGE(WmsLogTag::DEFAULT, "Invalid param size"); + return NapiGetUndefined(env); + } + int32_t instanceId = INVALID_INSTANCEID; + if (!ConvertFromJsValue(env, argv[0], instanceId) || instanceId == INVALID_INSTANCEID) { + TLOGE(WmsLogTag::DEFAULT, "invalid instanceId value: %{public}d", instanceId); + return NapiGetUndefined(env); + } + auto window = GetWindowByInstanceId(instanceId); + if (window == nullptr) { + TLOGE(WmsLogTag::DEFAULT, "window nullptr"); + return nullptr; + } + if (window->GetType() == WindowType::WINDOW_TYPE_UI_EXTENSION && !window->GetIsAtomicService()) { + return CreateJsNumber(env, static_cast(DISPLAY_ID_INVALID)); + } + return CreateJsNumber(env, window->GetDisplayId()); +} + +napi_value JsWindowEnvManagerInit(napi_env env, napi_value exportObj) +{ + TLOGD(WmsLogTag::DEFAULT, "JsWindowEnvManagerInit"); + + if (env == nullptr || exportObj == nullptr) { + TLOGE(WmsLogTag::DEFAULT, "JsWindowEnvManagerInit env or exportObj is nullptr"); + return nullptr; + } + + std::unique_ptr jsWinEnvManager = std::make_unique(); + napi_wrap(env, exportObj, jsWinEnvManager.release(), JsWindowEnvManager::Finalizer, nullptr, nullptr); + + const char *moduleName = "JsWindowEnvManager"; + BindNativeFunction(env, exportObj, "findWindowById", moduleName, JsWindowEnvManager::FindWindowById); + BindNativeFunction(env, exportObj, "getDisplayId", moduleName, JsWindowEnvManager::GetDisplayId); + return NapiGetUndefined(env); +} +} // namespace Rosen +} // namespace OHOS diff --git a/interfaces/kits/napi/environmental/inner/js_windowenv_manager.h b/interfaces/kits/napi/environmental/inner/js_windowenv_manager.h new file mode 100644 index 0000000000..464db6622e --- /dev/null +++ b/interfaces/kits/napi/environmental/inner/js_windowenv_manager.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_JS_WINDOW_ENV_NAPI_H +#define OHOS_JS_WINDOW_ENV_NAPI_H + +#include "js_runtime_utils.h" + +namespace OHOS { +namespace Rosen { +napi_value JsWindowEnvManagerInit(napi_env env, napi_value exportObj); + +class JsWindowEnvManager { +public: + JsWindowEnvManager(); + ~JsWindowEnvManager(); + static void Finalizer(napi_env env, void* data, void* hint); + static napi_value FindWindowById(napi_env env, napi_callback_info info); + static napi_value GetDisplayId(napi_env env, napi_callback_info info); + +private: + static napi_value OnFindWindowById(napi_env env, napi_callback_info info); + static napi_value OnGetDisplayId(napi_env env, napi_callback_info info); +}; + +} // namespace Rosen +} // namespace OHOS + +#endif // OHOS_JS_WINDOW_ENV_NAPI_H diff --git a/interfaces/kits/napi/environmental/inner/js_windowenv_module.cpp b/interfaces/kits/napi/environmental/inner/js_windowenv_module.cpp new file mode 100644 index 0000000000..3f30c097b4 --- /dev/null +++ b/interfaces/kits/napi/environmental/inner/js_windowenv_module.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_windowenv_manager.h" + +static napi_module g_windowenvManagerModule = { + .nm_filename = "module/libwindowenv_napi.so/windowenv.js", + .nm_register_func = OHOS::Rosen::JsWindowEnvManagerInit, + .nm_modname = "windowenv", +}; + +extern "C" __attribute__((constructor)) void NAPI_application_windowenvmanager_AutoRegister() +{ + napi_module_register(&g_windowenvManagerModule); +} diff --git a/interfaces/kits/napi/environmental/src/DisplayIdEnv.ts b/interfaces/kits/napi/environmental/src/DisplayIdEnv.ts new file mode 100644 index 0000000000..16e4f9ab5e --- /dev/null +++ b/interfaces/kits/napi/environmental/src/DisplayIdEnv.ts @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const windowenv = requireInternal('windowenv'); +const hilog = requireInternal('hilog'); +const HILOG_DOMAIN = 0x04217; +const HILOG_TAG = 'WMSAttribute'; + +@ObservedV2 +class DisplayIdEnv implements IEnvironmentValue { + @Trace public displayId: number = -1; + #win: window.Window | uiExtension.WindowProxy; + + get value(): window.DisplayId { + return this; + } + + constructor(context: UIContext) { + try { + this.#win = windowenv.findWindowById(context.getId()); + this.displayId = windowenv.getDisplayId(context.getId()); + this.#win.on('displayIdChange', this.#displayIdChangeCallback); + } catch (error) { + hilog.error(HILOG_DOMAIN, HILOG_TAG, `[env] displayId env constructor failed, ${error.message}`); + } + } + + #displayIdChangeCallback = (displayId: number): void => { + this.displayId = displayId; + }; + + update(): void {} + + destroy(): void { + try { + this.#win.off('displayIdChange', this.#displayIdChangeCallback); + } catch (error) { + hilog.error(HILOG_DOMAIN, HILOG_TAG, `[env] px env destroy failed, ${error.message}`); + } + } +} + +export default { + DisplayIdEnv +} diff --git a/interfaces/kits/napi/environmental/src/SystemDensityEnv.ts b/interfaces/kits/napi/environmental/src/SystemDensityEnv.ts new file mode 100644 index 0000000000..19798c93d2 --- /dev/null +++ b/interfaces/kits/napi/environmental/src/SystemDensityEnv.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const windowenv = requireInternal('windowenv'); +const hilog = requireInternal('hilog'); +const HILOG_DOMAIN = 0x04217; +const HILOG_TAG = 'WMSAttribute'; + +@ObservedV2 +class SystemDensityEnv implements IEnvironmentValue { + @Trace public systemDensity: number = -1.0; + #win: window.Window | uiExtension.WindowProxy; + + get value(): window.SystemDensity { + return this; + } + + constructor(context: UIContext) { + try { + this.#win = windowenv.findWindowById(context.getId()); + const windowDensityInfo: window.WindowDensityInfo = this.#win.getWindowDensityInfo(); + this.systemDensity = windowDensityInfo.systemDensity; + this.#win.on('systemDensityChange', this.#systemDensityChangeCallback); + } catch (error) { + hilog.error(HILOG_DOMAIN, HILOG_TAG, `[env] density env constructor failed, ${error.message}`); + } + } + + #systemDensityChangeCallback = (systemDensity: number): void => { + this.systemDensity = systemDensity; + }; + + update(): void {} + + destroy(): void { + try { + this.#win.off('systemDensityChange', this.#systemDensityChangeCallback); + } catch (error) { + hilog.error(HILOG_DOMAIN, HILOG_TAG, `[env] px env destroy failed, ${error.message}`); + } + } +} + +export default { + SystemDensityEnv +} diff --git a/interfaces/kits/napi/environmental/src/common.d.ts b/interfaces/kits/napi/environmental/src/common.d.ts index 917a5dd588..29a8db0963 100644 --- a/interfaces/kits/napi/environmental/src/common.d.ts +++ b/interfaces/kits/napi/environmental/src/common.d.ts @@ -72,9 +72,24 @@ declare namespace window { WINDOW_DESTROYED = 7 } + interface WindowDensityInfo { + systemDensity: number; + defaultDensity: number; + customDensity: number; + } + + interface SystemDensity { + systemDensity: number; + } + + interface DisplayId { + displayId: number; + } + interface Window { getWindowProperties(): { windowRect: Size }; getWindowAvoidArea(type: number): AvoidArea; + getWindowDensityInfo(): window.WindowDensityInfo; on(type: 'windowSizeChange', callback: Callback): void; off(type: 'windowSizeChange', callback?: Callback): void; on(type: 'avoidAreaChange', callback: Callback): void; @@ -83,13 +98,39 @@ declare namespace window { off(type: 'windowEvent', callback?: Callback): void; on(type: 'windowHighlightChange', callback: Callback): void; off(type: 'windowHighlightChange', callback?: Callback): void; + on(type: 'systemDensityChange', callback: Callback): void; + off(type: 'systemDensityChange', callback?: Callback): void; + on(type: 'displayIdChange', callback: Callback): void; + off(type: 'displayIdChange', callback?: Callback): void; } } +declare namespace uiExtension { + interface WindowProxy { + getWindowProperties(): { windowRect: window.Size }; + getWindowAvoidArea(type: number): window.AvoidArea; + getWindowDensityInfo(): window.WindowDensityInfo; + on(type: 'windowSizeChange', callback: Callback): void; + off(type: 'windowSizeChange', callback?: Callback): void; + on(type: 'avoidAreaChange', callback: Callback): void; + off(type: 'avoidAreaChange', callback?: Callback): void; + on(type: 'systemDensityChange', callback: Callback): void; + off(type: 'systemDensityChange', callback?: Callback): void; + on(type: 'displayIdChange', callback: Callback): void; + off(type: 'displayIdChange', callback?: Callback): void; + } +} + +interface WindowEnv { + findWindowById(value: number): window.Window | uiExtension.WindowProxy; + getDisplayId(value: number): number; +} + declare class UIContext { getWindowName(): string; getUIObserver(): UIObserver; px2vp(value: number): number; + getId(): number; } declare namespace uiObserver { diff --git a/interfaces/kits/napi/environmental/tsconfig.json b/interfaces/kits/napi/environmental/tsconfig.json index 6207c00079..7f931bfa7a 100644 --- a/interfaces/kits/napi/environmental/tsconfig.json +++ b/interfaces/kits/napi/environmental/tsconfig.json @@ -3,7 +3,9 @@ "./src/common.d.ts", "./src/WindowSizeEnv.ts", "./src/WindowAvoidAreaEnv.ts", - "./src/WindowFocusEnv.ts" + "./src/WindowFocusEnv.ts", + "./src/SystemDensityEnv.ts", + "./src/DisplayIdEnv.ts" ], "compilerOptions": { "module": "ESNext", diff --git a/interfaces/kits/napi/extension_window/js_extension_window.cpp b/interfaces/kits/napi/extension_window/js_extension_window.cpp index f56109e5a1..abc52520eb 100644 --- a/interfaces/kits/napi/extension_window/js_extension_window.cpp +++ b/interfaces/kits/napi/extension_window/js_extension_window.cpp @@ -59,6 +59,17 @@ const std::unordered_set g_unsupportListener = { const std::unordered_set g_invalidListener = { "subWindowClose", }; +const std::unordered_set g_emptyProxyListener = { + "displayIdChange", + "systemDensityChange", +}; +static thread_local std::map> g_jsExtensionWindowMap; +static std::mutex g_extensionMutex; + +bool IsEmptyProxyListener(const std::string& type) +{ + return g_emptyProxyListener.find(type) != g_emptyProxyListener.end(); +} bool IsEmptyListener(const std::string& type) { @@ -76,6 +87,33 @@ bool IsInvalidListener(const std::string& type) } } // namespace +void addJsExtensionWindow(napi_env env, napi_value objValue, int32_t id) +{ + std::shared_ptr jsExtensionWindowRef; + napi_ref result = nullptr; + napi_create_reference(env, objValue, 1, &result); + jsExtensionWindowRef.reset(reinterpret_cast(result)); + std::lock_guard lock(g_extensionMutex); + g_jsExtensionWindowMap[id] = jsExtensionWindowRef; +} + +napi_value FindJsExtensionWindowById(napi_env env, int32_t id) +{ + std::lock_guard lock(g_extensionMutex); + if (g_jsExtensionWindowMap.find(id) == g_jsExtensionWindowMap.end()) { + napi_throw(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_STATE_ABNORMALLY, + "The extensionWindow is destroyed.")); + return NapiGetUndefined(env); + } + napi_value extensionWindow = g_jsExtensionWindowMap[id]->GetNapiValue(); + if (!extensionWindow) { + napi_throw(env, JsErrUtils::CreateJsError(env, WmErrorCode::WM_ERROR_STATE_ABNORMALLY, + "The extensionWindow is destroyed.")); + return NapiGetUndefined(env); + } + return extensionWindow; +} + const std::string& JsExtensionWindow::GetWindowName() const { return windowName_; @@ -134,7 +172,9 @@ napi_value JsExtensionWindow::CreateJsExtensionWindow(napi_env env, sptrGetWindowPersistentId()); return objValue; } @@ -206,6 +246,7 @@ napi_value JsExtensionWindow::CreateJsExtensionWindowObject(napi_env env, sptrGetWindowPersistentId()); return objValue; } @@ -1114,6 +1155,10 @@ napi_value JsExtensionWindow::OnRegisterExtensionWindowCallback(napi_env env, na return NapiThrowError(env, WmErrorCode::WM_ERROR_DEVICE_NOT_SUPPORT, "[window][on]msg: The device not support"); } + } else { + if (IsEmptyProxyListener(cbType)) { + return NapiGetUndefined(env); + } } napi_value value = argv[INDEX_ONE]; if (!NapiIsCallable(env, value)) { @@ -1180,6 +1225,10 @@ napi_value JsExtensionWindow::OnUnRegisterExtensionWindowCallback(napi_env env, return NapiThrowError(env, WmErrorCode::WM_ERROR_DEVICE_NOT_SUPPORT, "[window][off]msg: The device not support"); } + } else { + if (IsEmptyProxyListener(cbType)) { + return NapiGetUndefined(env); + } } napi_value value = nullptr; diff --git a/interfaces/kits/napi/screen_runtime/BUILD.gn b/interfaces/kits/napi/screen_runtime/BUILD.gn index 12870d86d4..ebff43115c 100644 --- a/interfaces/kits/napi/screen_runtime/BUILD.gn +++ b/interfaces/kits/napi/screen_runtime/BUILD.gn @@ -105,6 +105,7 @@ ohos_shared_library("screen_kit") { "napi:ace_napi", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/interfaces/kits/napi/screen_runtime/napi/js_screen_manager.cpp b/interfaces/kits/napi/screen_runtime/napi/js_screen_manager.cpp index 90cb99da57..c6431652ef 100644 --- a/interfaces/kits/napi/screen_runtime/napi/js_screen_manager.cpp +++ b/interfaces/kits/napi/screen_runtime/napi/js_screen_manager.cpp @@ -979,6 +979,7 @@ napi_value OnCreateVirtualScreen(napi_env env, napi_callback_info info) TLOGI(WmsLogTag::DMS, "called"); DmErrorCode errCode = DmErrorCode::DM_OK; VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::JS_SCREEN_MANAGER; size_t argc = 4; std::string errMsg = ""; napi_value argv[4] = {nullptr}; diff --git a/interfaces/kits/napi/window_animation/BUILD.gn b/interfaces/kits/napi/window_animation/BUILD.gn index 401a9b27c3..9e419be1fc 100644 --- a/interfaces/kits/napi/window_animation/BUILD.gn +++ b/interfaces/kits/napi/window_animation/BUILD.gn @@ -61,4 +61,5 @@ ohos_shared_library("window_animation_utils") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/interfaces/kits/napi/window_runtime/BUILD.gn b/interfaces/kits/napi/window_runtime/BUILD.gn index 2c4004ddf0..f6970d2b2a 100644 --- a/interfaces/kits/napi/window_runtime/BUILD.gn +++ b/interfaces/kits/napi/window_runtime/BUILD.gn @@ -149,6 +149,7 @@ ohos_shared_library("window_native_kit") { } version_script = "libwindow_native_kit.map" + ldflags = [ "-Wl,-Bsymbolic-functions" ] } ohos_shared_library("window_napi") { @@ -257,4 +258,5 @@ ohos_shared_library("windowstage_kit") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/interfaces/kits/napi/window_runtime/window_napi/js_window.cpp b/interfaces/kits/napi/window_runtime/window_napi/js_window.cpp index 81c8272c94..8b2941e537 100644 --- a/interfaces/kits/napi/window_runtime/window_napi/js_window.cpp +++ b/interfaces/kits/napi/window_runtime/window_napi/js_window.cpp @@ -7769,12 +7769,10 @@ bool JsWindow::ParseWindowAnchorInfo(napi_env env, napi_value jsObject, WindowAn bool JsWindow::ParseWindowAttachOptions(napi_env env, napi_value jsObject, WindowAnchorInfo::AttachOptions& subWindowAttachOptions) { - std::string data = ""; - std::string defaultValue = ""; if (GetType(env, jsObject) != napi_object) { return false; } - auto parseField = [](napi_env& env, const char* fieldName, std::string& data, auto& field, auto& defValue, + auto parseField = [](napi_env& env, const char* fieldName, auto& data, auto& field, const auto& defValue, napi_value& jsObject) -> bool { if (!ParseJsValueOrGetDefault(jsObject, env, fieldName, data, defValue)) { TLOGE(WmsLogTag::WMS_LAYOUT, "Failed to convert object to %{public}s", fieldName); @@ -7784,9 +7782,25 @@ bool JsWindow::ParseWindowAttachOptions(napi_env env, napi_value jsObject, return true; }; + std::string data = ""; + std::string defaultValue = ""; if (!parseField(env, "currentLayoutMode", data, subWindowAttachOptions.currentLayoutMode, defaultValue, jsObject)) { return false; } + + const bool defaultBoolValue = false; + bool boolData = false; + if (!parseField(env, "isIntersectedHeightLimit", boolData, subWindowAttachOptions.isIntersectedHeightLimit, + defaultBoolValue, jsObject)) { + return false; + } + + boolData = defaultBoolValue; + if (!parseField(env, "isIntersectedWidthLimit", boolData, subWindowAttachOptions.isIntersectedWidthLimit, + defaultBoolValue, jsObject)) { + return false; + } + return true; } @@ -8074,10 +8088,15 @@ napi_value JsWindow::OnAttachToParentWindow(napi_env env, napi_callback_info inf napi_value result = nullptr; std::shared_ptr napiAsyncTask = CreateEmptyAsyncTask(env, nullptr, &result); acceptAnchorInfo.attachOptions.currentLayoutMode = windowAttachOptions.currentLayoutMode; + acceptAnchorInfo.attachOptions.isIntersectedHeightLimit = windowAttachOptions.isIntersectedHeightLimit; + acceptAnchorInfo.attachOptions.isIntersectedWidthLimit = windowAttachOptions.isIntersectedWidthLimit; acceptAnchorInfo.isFromAttachOrDetach_ = true; - TLOGI(WmsLogTag::WMS_LAYOUT, "windowAnchorInfo %{public}d, offsetX:%{public}d, offsetY:%{public}d" - "currentLayoutMode:%{public}s", acceptAnchorInfo.windowAnchor_, acceptAnchorInfo.offsetX_, - acceptAnchorInfo.offsetY_, acceptAnchorInfo.attachOptions.currentLayoutMode.c_str()); + TLOGI(WmsLogTag::WMS_LAYOUT, "windowAnchorInfo %{public}d, offsetX:%{public}d, offsetY:%{public}d, " + "currentLayoutMode:%{public}s, isIntersectedHeightLimit:%{public}d, isIntersectedWidthLimit:%{public}d", + acceptAnchorInfo.windowAnchor_, acceptAnchorInfo.offsetX_, acceptAnchorInfo.offsetY_, + acceptAnchorInfo.attachOptions.currentLayoutMode.c_str(), + acceptAnchorInfo.attachOptions.isIntersectedHeightLimit, + acceptAnchorInfo.attachOptions.isIntersectedWidthLimit); napi_ref sizeChangeCallbackRef = nullptr; if (sizeChangeCallback != nullptr) { napi_valuetype valueType; diff --git a/interfaces/kits/napi/window_runtime/window_napi/js_window_utils.cpp b/interfaces/kits/napi/window_runtime/window_napi/js_window_utils.cpp index 5417a24657..4615e020dd 100644 --- a/interfaces/kits/napi/window_runtime/window_napi/js_window_utils.cpp +++ b/interfaces/kits/napi/window_runtime/window_napi/js_window_utils.cpp @@ -1072,11 +1072,12 @@ napi_value CreateJsWindowInfoObject(napi_env env, const sptrGetGlobalDisplayRect())); napi_set_named_property(env, objValue, "globalRect", GetRectAndConvertToJsValue(env, info->GetGlobalRect())); - napi_set_named_property(env, objValue, "displayId", - CreateJsNumber(env, static_cast(info->GetDisplayId()))); - napi_set_named_property(env, objValue, "bundleName", CreateJsValue(env, info->GetBundleName())); - napi_set_named_property(env, objValue, "abilityName", CreateJsValue(env, info->GetAbilityName())); - napi_set_named_property(env, objValue, "windowId", CreateJsValue(env, info->GetWindowId())); + napi_set_named_property(env, objValue, "displayId", + CreateJsNumber(env, static_cast(info->GetDisplayId()))); + napi_set_named_property(env, objValue, "bundleName", CreateJsValue(env, info->GetBundleName())); + napi_set_named_property(env, objValue, "moduleName", CreateJsValue(env, info->GetModuleName())); + napi_set_named_property(env, objValue, "abilityName", CreateJsValue(env, info->GetAbilityName())); + napi_set_named_property(env, objValue, "windowId", CreateJsValue(env, info->GetWindowId())); napi_set_named_property(env, objValue, "windowStatusType", CreateJsValue(env, static_cast(info->GetWindowStatus()))); napi_set_named_property(env, objValue, "isFocused", CreateJsValue(env, info->IsFocused())); @@ -2338,4 +2339,4 @@ std::unique_ptr CreateEmptyWsNapiAsyncTask(napi_env env, } } } // namespace Rosen -} // namespace OHOS \ No newline at end of file +} // namespace OHOS diff --git a/previewer/include/wm_common.h b/previewer/include/wm_common.h index 0c23c6065f..41b7f9bc7b 100644 --- a/previewer/include/wm_common.h +++ b/previewer/include/wm_common.h @@ -1083,11 +1083,21 @@ struct WindowAnchorInfo : public Parcelable { int32_t offsetY_ = 0; struct AttachOptions : public Parcelable { std::string currentLayoutMode = ""; + bool isIntersectedHeightLimit = false; + bool isIntersectedWidthLimit = false; + AttachOptions() = default; AttachOptions(const std::string&& currentLayoutMode) : currentLayoutMode(currentLayoutMode) {} + AttachOptions(std::string currentLayoutMode, bool isIntersectedHeightLimit, + bool isIntersectedWidthLimit) : currentLayoutMode(currentLayoutMode), + isIntersectedHeightLimit(isIntersectedHeightLimit), + isIntersectedWidthLimit(isIntersectedWidthLimit) {} + bool operator==(const AttachOptions& other) const { - return currentLayoutMode == other.currentLayoutMode; + return currentLayoutMode == other.currentLayoutMode && + isIntersectedHeightLimit == other.isIntersectedHeightLimit && + isIntersectedWidthLimit == other.isIntersectedWidthLimit; } bool operator!=(const AttachOptions& other) const @@ -1097,7 +1107,12 @@ struct WindowAnchorInfo : public Parcelable { bool Marshalling(Parcel& parcel) const override { - return parcel.WriteString(currentLayoutMode); + if (!parcel.WriteString(currentLayoutMode) || + !parcel.WriteBool(isIntersectedHeightLimit) || + !parcel.WriteBool(isIntersectedWidthLimit)) { + return false; + } + return true; } static AttachOptions* Unmarshalling(Parcel& parcel) @@ -1111,6 +1126,11 @@ struct WindowAnchorInfo : public Parcelable { return nullptr; } attachOptions->currentLayoutMode = layoutMode; + + if (!parcel.ReadBool(attachOptions->isIntersectedHeightLimit) || + !parcel.ReadBool(attachOptions->isIntersectedWidthLimit)) { + return nullptr; + } return attachOptions.release(); } }; @@ -1162,6 +1182,8 @@ struct WindowAnchorInfo : public Parcelable { } windowAnchorInfo->windowAnchor_ = static_cast(windowAnchorMode); windowAnchorInfo->attachOptions.currentLayoutMode = attachOptions->currentLayoutMode; + windowAnchorInfo->attachOptions.isIntersectedHeightLimit = attachOptions->isIntersectedHeightLimit; + windowAnchorInfo->attachOptions.isIntersectedWidthLimit = attachOptions->isIntersectedWidthLimit; return windowAnchorInfo; } }; @@ -1377,6 +1399,66 @@ struct WindowLimits { << " " << vpRatio_ << " " << static_cast(pixelUnit_) << "]"; return oss.str(); } + + bool Marshalling(Parcel& parcel) const + { + return parcel.WriteUint32(maxWidth_) && parcel.WriteUint32(maxHeight_) && + parcel.WriteUint32(minWidth_) && parcel.WriteUint32(minHeight_) && + parcel.WriteFloat(maxRatio_) && parcel.WriteFloat(minRatio_) && + parcel.WriteFloat(vpRatio_) && parcel.WriteUint32(static_cast(pixelUnit_)); + } + + static WindowLimits* Unmarshalling(Parcel& parcel) + { + auto windowLimits = std::make_unique(); + if (!windowLimits) { + return nullptr; + } + uint32_t pixelUnit = 0; + if (!parcel.ReadUint32(windowLimits->maxWidth_) || + !parcel.ReadUint32(windowLimits->maxHeight_) || + !parcel.ReadUint32(windowLimits->minWidth_) || + !parcel.ReadUint32(windowLimits->minHeight_) || + !parcel.ReadFloat(windowLimits->maxRatio_) || + !parcel.ReadFloat(windowLimits->minRatio_) || + !parcel.ReadFloat(windowLimits->vpRatio_) || + !parcel.ReadUint32(pixelUnit)) { + return nullptr; + } + // Validate pixelUnit: valid values are PX=0 and VP=1 + if (pixelUnit > static_cast(PixelUnit::VP)) { + return nullptr; + } + windowLimits->pixelUnit_ = static_cast(pixelUnit); + return windowLimits.release(); + } +}; + +/** + * @struct AttachLimitOptions + * + * @brief Options for intersecting limits with attached windows. + * Used to specify whether to intersect height/width limits. + */ +struct AttachLimitOptions { + bool isIntersectedHeightLimit = false; + bool isIntersectedWidthLimit = false; + + AttachLimitOptions() = default; + AttachLimitOptions(bool isIntersectedHeightLimit, bool isIntersectedWidthLimit) + : isIntersectedHeightLimit(isIntersectedHeightLimit), + isIntersectedWidthLimit(isIntersectedWidthLimit) {} + + bool operator==(const AttachLimitOptions& other) const + { + return isIntersectedHeightLimit == other.isIntersectedHeightLimit && + isIntersectedWidthLimit == other.isIntersectedWidthLimit; + } + + bool operator!=(const AttachLimitOptions& other) const + { + return !this->operator==(other); + } }; /** diff --git a/setresolution/BUILD.gn b/setresolution/BUILD.gn index f4479c78a7..d5fe9465d8 100644 --- a/setresolution/BUILD.gn +++ b/setresolution/BUILD.gn @@ -64,6 +64,7 @@ ohos_shared_library("libsetresolution_util") { "hitrace:hitrace_meter", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] part_name = "window_manager" subsystem_name = "window" } diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 20d8777d41..4a7f7be8ad 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -78,6 +78,7 @@ ohos_shared_library("libsnapshot_util") { "libjpeg-turbo:turbojpeg", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] part_name = "window_manager" subsystem_name = "window" } diff --git a/test/fuzztest/wms/window01_fuzzer/BUILD.gn b/test/fuzztest/wms/window01_fuzzer/BUILD.gn index eb5ba009be..d22741a52d 100644 --- a/test/fuzztest/wms/window01_fuzzer/BUILD.gn +++ b/test/fuzztest/wms/window01_fuzzer/BUILD.gn @@ -43,6 +43,7 @@ ohos_fuzztest("Window01FuzzTest") { "${window_base_path}/utils:libwmutil", "${window_base_path}/utils:libwmutil_base", "${window_base_path}/wm:libwm", + "${window_base_path}/extension/extension_connection:libwindow_extension_client", ] external_deps = [ @@ -68,7 +69,6 @@ ohos_fuzztest("Window01FuzzTest") { "ipc:ipc_single", "napi:ace_napi", "resource_management:global_resmgr", - "window_manager:libwindow_extension_client", ] } diff --git a/test/fuzztest/wms/window_fuzzer/BUILD.gn b/test/fuzztest/wms/window_fuzzer/BUILD.gn index a8a8e019e4..7366aa54d5 100644 --- a/test/fuzztest/wms/window_fuzzer/BUILD.gn +++ b/test/fuzztest/wms/window_fuzzer/BUILD.gn @@ -43,6 +43,7 @@ ohos_fuzztest("WindowFuzzTest") { "${window_base_path}/utils:libwmutil", "${window_base_path}/utils:libwmutil_base", "${window_base_path}/wm:libwm", + "${window_base_path}/extension/extension_connection:libwindow_extension_client", ] external_deps = [ @@ -68,7 +69,6 @@ ohos_fuzztest("WindowFuzzTest") { "ipc:ipc_single", "napi:ace_napi", "resource_management:global_resmgr", - "window_manager:libwindow_extension_client", ] } diff --git a/test/systemtest/extension/BUILD.gn b/test/systemtest/extension/BUILD.gn index 6414c9a497..c07c04d8c6 100644 --- a/test/systemtest/extension/BUILD.gn +++ b/test/systemtest/extension/BUILD.gn @@ -12,6 +12,7 @@ # limitations under the License. import("//build/test.gni") +import("../../../windowmanager_aafwk.gni") module_out_path = "window_manager/window_manager/extension" @@ -52,10 +53,10 @@ ohos_systemtest("modal_system_ui_extension_test") { "../../../resources/config/build:testcase_flags", ] - public_deps = [ - "../../../dm:libdm", - "../../../extension/modal_system_ui_extension:libmodal_system_ui_extension_client", - "../../../window_scene/interfaces/innerkits:libwsutils", + deps = [ + "${window_base_path}/dm:libdm", + "${window_base_path}/extension/modal_system_ui_extension:libmodal_system_ui_extension_client", + "${window_base_path}/window_scene/interfaces/innerkits:libwsutils", ] external_deps = [ @@ -84,9 +85,9 @@ ohos_systemtest("window_extension_connection_test") { "../../../resources/config/build:testcase_flags", ] - public_deps = [ - "../../../extension/extension_connection:libwindow_extension_client", - "../../../window_scene/session:scene_session", + deps = [ + "${window_base_path}/extension/extension_connection:libwindow_extension_client", + "${window_base_path}/window_scene/session:scene_session", ] external_deps = [ diff --git a/utils/BUILD.gn b/utils/BUILD.gn index 32d310b8eb..50f2f3de26 100644 --- a/utils/BUILD.gn +++ b/utils/BUILD.gn @@ -202,6 +202,7 @@ ohos_shared_library("libwmutil_base") { "init:libbegetutil", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" @@ -275,12 +276,13 @@ ohos_shared_library("libwmutil") { if (!(host_os == "linux" && host_cpu == "arm64")) { external_deps += [ "preferences:native_preferences" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" if (is_ohos && is_clang && target_cpu == "arm64") { - ldflags = [ + ldflags += [ "-Wl,--emit-relocs", "-Wl,--no-relax", "-mno-fix-cortex-a53-843419" diff --git a/utils/include/window_visibility_info.h b/utils/include/window_visibility_info.h index e8a543bca5..a958fa33d2 100644 --- a/utils/include/window_visibility_info.h +++ b/utils/include/window_visibility_info.h @@ -89,11 +89,15 @@ public: const Rect& GetRect() const { return rect_; } - const std::string& GetBundleName() const { return bundleName_; } - - void SetBundleName(const std::string& bundleName) { bundleName_ = bundleName; } - - const std::string& GetAbilityName() const { return abilityName_; } + const std::string& GetBundleName() const { return bundleName_; } + + void SetBundleName(const std::string& bundleName) { bundleName_ = bundleName; } + + const std::string& GetModuleName() const { return moduleName_; } + + void SetModuleName(const std::string& moduleName) { moduleName_ = moduleName; } + + const std::string& GetAbilityName() const { return abilityName_; } void SetAbilityName(const std::string& abilityName) { abilityName_ = abilityName; } @@ -152,10 +156,11 @@ public: WindowType windowType_ { WindowType::WINDOW_TYPE_APP_MAIN_WINDOW }; WindowStatus windowStatus_ = WindowStatus::WINDOW_STATUS_UNDEFINED; Rect rect_ = {0, 0, 0, 0}; - Rect globalDisplayRect_ { 0, 0, 0, 0 }; - Rect globalRect_ { 0, 0, 0, 0 }; - std::string bundleName_; - std::string abilityName_; + Rect globalDisplayRect_ { 0, 0, 0, 0 }; + Rect globalRect_ { 0, 0, 0, 0 }; + std::string bundleName_; + std::string moduleName_; + std::string abilityName_; bool isFocused_ = false; int32_t appIndex_ { 0 }; bool isSystem_ = false; @@ -167,4 +172,4 @@ public: ControlAppType controlAppType_ = ControlAppType::CONTROL_APP_TYPE_BEGIN; }; } // namespace OHOS::Rosen -#endif // OHOS_ROSEN_WINDOW_VISIBILITY_INFO_H \ No newline at end of file +#endif // OHOS_ROSEN_WINDOW_VISIBILITY_INFO_H diff --git a/utils/src/window_visibility_info.cpp b/utils/src/window_visibility_info.cpp index b3dce43d54..7fe6a3aa62 100644 --- a/utils/src/window_visibility_info.cpp +++ b/utils/src/window_visibility_info.cpp @@ -26,12 +26,13 @@ bool WindowVisibilityInfo::Marshalling(Parcel& parcel) const return parcel.WriteUint32(windowId_) && parcel.WriteInt32(pid_) && parcel.WriteInt32(uid_) && parcel.WriteUint32(static_cast(visibilityState_)) && parcel.WriteUint32(static_cast(windowType_)) && - parcel.WriteUint32(static_cast(windowStatus_)) && parcel.WriteInt32(rect_.posX_) && - parcel.WriteInt32(rect_.posY_) && parcel.WriteUint32(rect_.width_) && parcel.WriteUint32(rect_.height_) && - parcel.WriteString(bundleName_) && parcel.WriteString(abilityName_) && parcel.WriteBool(isFocused_) && - parcel.WriteInt32(appIndex_) && parcel.WriteBool(isSystem_) && parcel.WriteUint32(zOrder_) && - parcel.WriteInt32(callingPid_) && parcel.WriteInt32(globalDisplayRect_.posX_) && - parcel.WriteInt32(globalDisplayRect_.posY_) && parcel.WriteUint32(globalDisplayRect_.width_) && + parcel.WriteUint32(static_cast(windowStatus_)) && parcel.WriteInt32(rect_.posX_) && + parcel.WriteInt32(rect_.posY_) && parcel.WriteUint32(rect_.width_) && parcel.WriteUint32(rect_.height_) && + parcel.WriteString(bundleName_) && parcel.WriteString(moduleName_) && + parcel.WriteString(abilityName_) && parcel.WriteBool(isFocused_) && + parcel.WriteInt32(appIndex_) && parcel.WriteBool(isSystem_) && parcel.WriteUint32(zOrder_) && + parcel.WriteInt32(callingPid_) && parcel.WriteInt32(globalDisplayRect_.posX_) && + parcel.WriteInt32(globalDisplayRect_.posY_) && parcel.WriteUint32(globalDisplayRect_.width_) && parcel.WriteUint32(globalDisplayRect_.height_) && parcel.WriteInt32(collaboratorType_) && parcel.WriteUint64(displayId_) && parcel.WriteInt32(globalRect_.posX_) && parcel.WriteInt32(globalRect_.posY_) && parcel.WriteUint32(globalRect_.width_) && @@ -55,12 +56,13 @@ WindowVisibilityInfo* WindowVisibilityInfo::Unmarshalling(Parcel& parcel) return nullptr; } windowVisibilityInfo->visibilityState_ = static_cast(visibilityState); - windowVisibilityInfo->windowType_ = static_cast(parcel.ReadUint32()); - windowVisibilityInfo->windowStatus_ = static_cast(parcel.ReadUint32()); - windowVisibilityInfo->rect_ = { parcel.ReadInt32(), parcel.ReadInt32(), parcel.ReadUint32(), parcel.ReadUint32() }; - windowVisibilityInfo->bundleName_ = parcel.ReadString(); - windowVisibilityInfo->abilityName_ = parcel.ReadString(); - windowVisibilityInfo->isFocused_ = parcel.ReadBool(); + windowVisibilityInfo->windowType_ = static_cast(parcel.ReadUint32()); + windowVisibilityInfo->windowStatus_ = static_cast(parcel.ReadUint32()); + windowVisibilityInfo->rect_ = { parcel.ReadInt32(), parcel.ReadInt32(), parcel.ReadUint32(), parcel.ReadUint32() }; + windowVisibilityInfo->bundleName_ = parcel.ReadString(); + windowVisibilityInfo->moduleName_ = parcel.ReadString(); + windowVisibilityInfo->abilityName_ = parcel.ReadString(); + windowVisibilityInfo->isFocused_ = parcel.ReadBool(); windowVisibilityInfo->appIndex_ = parcel.ReadInt32(); windowVisibilityInfo->isSystem_ = parcel.ReadBool(); windowVisibilityInfo->zOrder_ = parcel.ReadUint32(); diff --git a/window_scene/common/BUILD.gn b/window_scene/common/BUILD.gn index d299c68e7a..602bcba32c 100644 --- a/window_scene/common/BUILD.gn +++ b/window_scene/common/BUILD.gn @@ -88,4 +88,5 @@ ohos_shared_library("window_scene_common") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/window_scene/common/include/window_session_property.h b/window_scene/common/include/window_session_property.h index 62e4b839a4..dcba3ff435 100755 --- a/window_scene/common/include/window_session_property.h +++ b/window_scene/common/include/window_session_property.h @@ -87,6 +87,7 @@ public: void SetWindowLimitsVP(const WindowLimits& windowLimits); void SetUserWindowLimits(const WindowLimits& windowLimits); void SetConfigWindowLimitsVP(const WindowLimits& windowLimitsVP); + void SetLimitsForAttachedWindows(const WindowLimits& windowLimits); void SetLastLimitsVpr(float vpr); void SetSystemBarProperty(WindowType type, const SystemBarProperty& property); void SetKeyboardLayoutParams(const KeyboardLayoutParams& params); @@ -166,6 +167,7 @@ public: WindowLimits GetWindowLimitsVP() const; WindowLimits GetUserWindowLimits() const; WindowLimits GetConfigWindowLimitsVP() const; + WindowLimits GetLimitsForAttachedWindows() const; float GetLastLimitsVpr() const; uint32_t GetWindowModeSupportType() const; std::unordered_map GetSystemBarProperty() const; @@ -213,6 +215,8 @@ public: static void UnmarshallingShadowsInfo(Parcel& parcel, WindowSessionProperty* property); bool MarshallingWindowAnchorInfo(Parcel& parcel) const; static void UnmarshallingWindowAnchorInfo(Parcel& parcel, WindowSessionProperty* property); + bool MarshallingHookWindowInfo(Parcel& parcel) const; + static void UnmarshallingHookWindowInfo(Parcel& parcel, WindowSessionProperty* property); void SetTextFieldPositionY(double textFieldPositionY); void SetTextFieldHeight(double textFieldHeight); @@ -256,6 +260,22 @@ public: bool IsSubWindowZLevelAboveParentLoosened() const; void SetWindowAnchorInfo(const WindowAnchorInfo& windowAnchorInfo); WindowAnchorInfo GetWindowAnchorInfo() const; + // Set attached window limits from a specific source window (by persistentId) + void SetAttachedWindowLimits(int32_t sourcePersistentId, const WindowLimits& attachedWindowLimits); + // Remove attached window limits from a specific source window + void RemoveAttachedWindowLimits(int32_t sourcePersistentId); + // Get all attached window limits (preserving insertion order) + std::vector> GetAttachedWindowLimitsList() const; + void ClearAttachedWindowLimitsList(); + // Set limit options for a specific attached window + void SetAttachedLimitOptions(int32_t sourcePersistentId, const AttachLimitOptions& options); + // Get limit options for a specific attached window + AttachLimitOptions GetAttachedLimitOptions(int32_t sourcePersistentId) const; + // Remove limit options for a specific attached window + void RemoveAttachedLimitOptions(int32_t sourcePersistentId); + // Get all attached limit options (preserving insertion order) + std::vector> GetAttachedLimitOptionsList() const; + void ClearAttachedLimitOptionsList(); /* * Window Hierarchy @@ -282,6 +302,10 @@ public: bool GetPcAppInpadOrientationLandscape() const; void SetMobileAppInPadLayoutFullScreen(bool isMobileAppInPadLayoutFullScreen); bool GetMobileAppInPadLayoutFullScreen() const; + void SetForceSplitEnable(bool isForceSplitEnabled); + bool GetForceSplitEnable() const; + void SetHookWindowInfo(const HookWindowInfo& hookWindowInfo); + HookWindowInfo GetHookWindowInfo() const; void SetRotationLocked(bool locked); bool GetRotationLocked() const; @@ -528,6 +552,7 @@ private: WindowLimits limitsVP_ = WindowLimits::DEFAULT_VP_LIMITS(); WindowLimits userLimits_ = WindowLimits::DEFAULT_VP_LIMITS(); WindowLimits configLimitsVP_ = WindowLimits::DEFAULT_VP_LIMITS(); + WindowLimits limitsForAttachedWindows_ = WindowLimits::DEFAULT_VP_LIMITS(); float lastVpr_ = 0.0f; PiPTemplateInfo pipTemplateInfo_ = {}; FloatingBallTemplateInfo fbTemplateInfo_ = {}; @@ -593,6 +618,14 @@ private: bool subWindowOutlineEnabled_ = false; bool zLevelAboveParentLoosened_ = false; WindowAnchorInfo windowAnchorInfo_; + // Store window limits from attached windows (preserving insertion order for priority) + // Parent window stores limits from its sub windows, sub window stores limits from its parent + // Earlier attached windows have higher priority in intersection calculation + std::vector> attachedWindowLimitsList_; + // Store limit options for each attached window (preserving insertion order to match limits list) + // Parent window stores options from its sub windows (which limits to intersect) + // Each pair contains: + std::vector> attachedLimitOptionsList_; /* * Window Hierarchy @@ -668,10 +701,14 @@ private: mutable std::mutex shadowsInfoMutex_; mutable std::mutex globalDisplayRectMutex_; Rect globalDisplayRect_ { 0, 0, 0, 0 }; + mutable std::mutex hookWindowInfoMutex_; + HookWindowInfo hookWindowInfo_; bool isPcAppInpadCompatibleMode_ = false; bool isPcAppInpadSpecificSystemBarInvisible_ = false; bool isPcAppInpadOrientationLandscape_ = false; bool isMobileAppInPadLayoutFullScreen_ = false; + mutable std::mutex isForceSplitEnabledMutex_; + bool isForceSplitEnabled_ = false; bool isRotationLock_ = false; /* @@ -844,9 +881,6 @@ struct FreeMultiWindowConfig : public Parcelable { }; struct AppForceLandscapeConfig : public Parcelable { - int32_t mode_ = 0; - int32_t supportSplit_ = -1; - bool ignoreOrientation_ = false; std::string sysConfigJsonStr_ = ""; std::string appConfigJsonStr_ = ""; std::string sysHomePage_ = ""; @@ -857,20 +891,15 @@ struct AppForceLandscapeConfig : public Parcelable { bool hasChanged_ = true; bool configEnable_ = false; AppForceLandscapeConfig() {} - AppForceLandscapeConfig(int32_t mode, int32_t supportSplit, bool ignoreOrientation, - const std::string& sysConfigJsonStr, const std::string& appConfigJsonStr, + AppForceLandscapeConfig(const std::string& sysConfigJsonStr, const std::string& appConfigJsonStr, const std::string& sysHomePage, bool isSysRouter, bool isAppRouter, - bool containsSysConfig, bool containsAppConfig) : mode_(mode), supportSplit_(supportSplit), - ignoreOrientation_(ignoreOrientation), sysConfigJsonStr_(sysConfigJsonStr), + bool containsSysConfig, bool containsAppConfig) : sysConfigJsonStr_(sysConfigJsonStr), appConfigJsonStr_(appConfigJsonStr), sysHomePage_(sysHomePage), isSysRouter_(isSysRouter), isAppRouter_(isAppRouter), containsSysConfig_(containsSysConfig), containsAppConfig_(containsAppConfig) {} virtual bool Marshalling(Parcel& parcel) const override { - if (!parcel.WriteInt32(mode_) || - !parcel.WriteInt32(supportSplit_) || - !parcel.WriteBool(ignoreOrientation_) || - !parcel.WriteString(sysConfigJsonStr_) || + if (!parcel.WriteString(sysConfigJsonStr_) || !parcel.WriteString(appConfigJsonStr_) || !parcel.WriteString(sysHomePage_) || !parcel.WriteBool(isSysRouter_) || @@ -888,10 +917,7 @@ struct AppForceLandscapeConfig : public Parcelable { if (config == nullptr) { return nullptr; } - if (!parcel.ReadInt32(config->mode_) || - !parcel.ReadInt32(config->supportSplit_) || - !parcel.ReadBool(config->ignoreOrientation_) || - !parcel.ReadString(config->sysConfigJsonStr_) || + if (!parcel.ReadString(config->sysConfigJsonStr_) || !parcel.ReadString(config->appConfigJsonStr_) || !parcel.ReadString(config->sysHomePage_) || !parcel.ReadBool(config->isSysRouter_) || @@ -905,10 +931,7 @@ struct AppForceLandscapeConfig : public Parcelable { static bool IsSameForceSplitConfig(const AppForceLandscapeConfig& preconfig, const AppForceLandscapeConfig& config) { - if (preconfig.mode_ != config.mode_ || - preconfig.supportSplit_ != config.supportSplit_ || - preconfig.ignoreOrientation_ != config.ignoreOrientation_ || - preconfig.containsSysConfig_ != config.containsSysConfig_ || + if (preconfig.containsSysConfig_ != config.containsSysConfig_ || preconfig.containsAppConfig_ != config.containsAppConfig_) { return false; } diff --git a/window_scene/common/src/window_session_property.cpp b/window_scene/common/src/window_session_property.cpp index 416bf6ab4c..73ce8ed4af 100755 --- a/window_scene/common/src/window_session_property.cpp +++ b/window_scene/common/src/window_session_property.cpp @@ -1007,7 +1007,8 @@ bool WindowSessionProperty::MarshallingWindowLimits(Parcel& parcel) const return writeWindowLimits(limits_) && writeWindowLimits(limitsVP_) && - writeWindowLimits(userLimits_); + writeWindowLimits(userLimits_) && + writeWindowLimits(limitsForAttachedWindows_); } void WindowSessionProperty::UnmarshallingWindowLimits(Parcel& parcel, WindowSessionProperty* property) @@ -1028,6 +1029,7 @@ void WindowSessionProperty::UnmarshallingWindowLimits(Parcel& parcel, WindowSess property->SetWindowLimits(readWindowLimits()); property->SetWindowLimitsVP(readWindowLimits()); property->SetUserWindowLimits(readWindowLimits()); + property->SetLimitsForAttachedWindows(readWindowLimits()); } bool WindowSessionProperty::MarshallingSystemBarMap(Parcel& parcel) const @@ -1419,6 +1421,90 @@ WindowAnchorInfo WindowSessionProperty::GetWindowAnchorInfo() const return windowAnchorInfo_; } +/** @note @window.layout */ +void WindowSessionProperty::SetAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits) +{ + // Check if this sourceId already exists, if so, update it; otherwise, append + for (auto& [id, limits] : attachedWindowLimitsList_) { + if (id == sourcePersistentId) { + limits = attachedWindowLimits; + return; + } + } + // Not found, append to the end (later attached, lower priority) + attachedWindowLimitsList_.push_back({sourcePersistentId, attachedWindowLimits}); +} + +/** @note @window.layout */ +void WindowSessionProperty::RemoveAttachedWindowLimits(int32_t sourcePersistentId) +{ + auto it = std::remove_if(attachedWindowLimitsList_.begin(), attachedWindowLimitsList_.end(), + [sourcePersistentId](const auto& item) { + return item.first == sourcePersistentId; + }); + attachedWindowLimitsList_.erase(it, attachedWindowLimitsList_.end()); +} + +/** @note @window.layout */ +std::vector> WindowSessionProperty::GetAttachedWindowLimitsList() const +{ + return attachedWindowLimitsList_; +} + +/** @note @window.layout */ +void WindowSessionProperty::ClearAttachedWindowLimitsList() +{ + attachedWindowLimitsList_.clear(); +} + +/** @note @window.layout */ +void WindowSessionProperty::SetAttachedLimitOptions(int32_t sourcePersistentId, const AttachLimitOptions& options) +{ + // Find existing entry and update in-place to preserve order + for (auto& entry : attachedLimitOptionsList_) { + if (entry.first == sourcePersistentId) { + entry.second = options; // In-place update, preserves position in vector + return; + } + } + // Not found, add new entry at the end + attachedLimitOptionsList_.emplace_back(sourcePersistentId, options); +} + +/** @note @window.layout */ +AttachLimitOptions WindowSessionProperty::GetAttachedLimitOptions(int32_t sourcePersistentId) const +{ + for (const auto& [id, options] : attachedLimitOptionsList_) { + if (id == sourcePersistentId) { + return options; + } + } + return AttachLimitOptions{}; // Return default options if not found +} + +/** @note @window.layout */ +void WindowSessionProperty::RemoveAttachedLimitOptions(int32_t sourcePersistentId) +{ + auto it = std::remove_if(attachedLimitOptionsList_.begin(), attachedLimitOptionsList_.end(), + [sourcePersistentId](const auto& entry) { + return entry.first == sourcePersistentId; + }); + attachedLimitOptionsList_.erase(it, attachedLimitOptionsList_.end()); +} + +/** @note @window.layout */ +std::vector> WindowSessionProperty::GetAttachedLimitOptionsList() const +{ + return attachedLimitOptionsList_; +} + +/** @note @window.layout */ +void WindowSessionProperty::ClearAttachedLimitOptionsList() +{ + attachedLimitOptionsList_.clear(); +} + void WindowSessionProperty::SetZIndex(int32_t zIndex) { zIndex_ = zIndex; @@ -1532,6 +1618,8 @@ bool WindowSessionProperty::Marshalling(Parcel& parcel) const parcel.WriteString(ancoRealBundleName_) && parcel.WriteBool(isShowDecorInFreeMultiWindow_) && parcel.WriteBool(isMobileAppInPadLayoutFullScreen_) && + parcel.WriteBool(isForceSplitEnabled_) && + MarshallingHookWindowInfo(parcel) && parcel.WriteBool(isFullScreenInForceSplitMode_) && parcel.WriteInt32(static_cast(pageCompatibleMode_)) && parcel.WriteFloat(aspectRatio_) && @@ -1659,6 +1747,8 @@ WindowSessionProperty* WindowSessionProperty::Unmarshalling(Parcel& parcel) property->SetAncoRealBundleName(parcel.ReadString()); property->SetIsShowDecorInFreeMultiWindow(parcel.ReadBool()); property->SetMobileAppInPadLayoutFullScreen(parcel.ReadBool()); + property->SetForceSplitEnable(parcel.ReadBool()); + UnmarshallingHookWindowInfo(parcel, property); property->SetIsFullScreenInForceSplitMode(parcel.ReadBool()); property->SetPageCompatibleMode(static_cast(parcel.ReadInt32())); property->SetAspectRatio(parcel.ReadFloat()); @@ -2315,6 +2405,18 @@ WindowLimits WindowSessionProperty::GetConfigWindowLimitsVP() const return configLimitsVP_; } +/** @note @window.layout */ +void WindowSessionProperty::SetLimitsForAttachedWindows(const WindowLimits& windowLimits) +{ + limitsForAttachedWindows_ = windowLimits; +} + +/** @note @window.layout */ +WindowLimits WindowSessionProperty::GetLimitsForAttachedWindows() const +{ + return limitsForAttachedWindows_; +} + void WindowSessionProperty::SetLastLimitsVpr(float vpr) { lastVpr_ = vpr; @@ -2602,6 +2704,30 @@ void WindowSessionProperty::SetMobileAppInPadLayoutFullScreen(bool isMobileAppIn isMobileAppInPadLayoutFullScreen_ = isMobileAppInPadLayoutFullScreen; } +void WindowSessionProperty::SetForceSplitEnable(bool isForceSplitEnabled) +{ + std::lock_guard lock(isForceSplitEnabledMutex_); + isForceSplitEnabled_ = isForceSplitEnabled; +} + +bool WindowSessionProperty::GetForceSplitEnable() const +{ + std::lock_guard lock(isForceSplitEnabledMutex_); + return isForceSplitEnabled_; +} + +void WindowSessionProperty::SetHookWindowInfo(const HookWindowInfo& hookWindowInfo) +{ + std::lock_guard lock(hookWindowInfoMutex_); + hookWindowInfo_ = hookWindowInfo; +} + +HookWindowInfo WindowSessionProperty::GetHookWindowInfo() const +{ + std::lock_guard lock(hookWindowInfoMutex_); + return hookWindowInfo_; +} + bool WindowSessionProperty::GetPcAppInpadCompatibleMode() const { return isPcAppInpadCompatibleMode_; @@ -2868,6 +2994,21 @@ void WindowSessionProperty::UnmarshallingWindowAnchorInfo(Parcel& parcel, Window property->SetWindowAnchorInfo(*windowAnchorInfo); } +bool WindowSessionProperty::MarshallingHookWindowInfo(Parcel& parcel) const +{ + return parcel.WriteParcelable(&hookWindowInfo_); +} + +void WindowSessionProperty::UnmarshallingHookWindowInfo(Parcel& parcel, WindowSessionProperty* property) +{ + sptr hookWindowInfo = parcel.ReadParcelable(); + if (hookWindowInfo == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "hookWindowInfo is nullptr!"); + return; + } + property->SetHookWindowInfo(*hookWindowInfo); +} + void WindowSessionProperty::SetMissionInfo(const MissionInfo& missionInfo) { std::lock_guard lock(missionInfoMutex_); diff --git a/window_scene/intention_event/BUILD.gn b/window_scene/intention_event/BUILD.gn index b604f06ba5..cfb3703698 100644 --- a/window_scene/intention_event/BUILD.gn +++ b/window_scene/intention_event/BUILD.gn @@ -86,4 +86,5 @@ ohos_shared_library("libintention_event") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/window_scene/interfaces/innerkits/BUILD.gn b/window_scene/interfaces/innerkits/BUILD.gn index 0ac2e8e7ad..f4091c7339 100644 --- a/window_scene/interfaces/innerkits/BUILD.gn +++ b/window_scene/interfaces/innerkits/BUILD.gn @@ -31,7 +31,7 @@ ohos_shared_library("libwsutils") { sources = [ "src/scene_board_judgement.cpp" ] public_configs = [ ":libwsutils_public_config" ] - + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/window_scene/interfaces/kits/ani/scene_session_manager/BUILD.gn b/window_scene/interfaces/kits/ani/scene_session_manager/BUILD.gn index b00ea3ee95..790f4410aa 100644 --- a/window_scene/interfaces/kits/ani/scene_session_manager/BUILD.gn +++ b/window_scene/interfaces/kits/ani/scene_session_manager/BUILD.gn @@ -78,6 +78,7 @@ ohos_shared_library("scenesessionmanagerani_kit") { "runtime_core:ani", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.cpp b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.cpp index 29e3192fc1..c1f00373ae 100644 --- a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.cpp +++ b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.cpp @@ -714,6 +714,8 @@ void JsSceneSession::BindNativeMethod(napi_env env, napi_value objValue, const c JsSceneSession::UpdateSceneAnimationConfig); BindNativeFunction(env, objValue, "setMobileAppInPadLayoutFullScreen", moduleName, JsSceneSession::SetMobileAppInPadLayoutFullScreen); + BindNativeFunction(env, objValue, "setForceSplitEnable", moduleName, JsSceneSession::SetForceSplitEnable); + BindNativeFunction(env, objValue, "updateHookWindowInfo", moduleName, JsSceneSession::UpdateHookWindowInfo); BindNativeFunction(env, objValue, "notifyOrientationExecutionResult", moduleName, JsSceneSession::NotifyOrientationExecutionResult); BindNativeFunction(env, objValue, "getSceneNodeCount", moduleName, @@ -3239,6 +3241,20 @@ napi_value JsSceneSession::SetMobileAppInPadLayoutFullScreen(napi_env env, napi_ return (me != nullptr) ? me->OnSetMobileAppInPadLayoutFullScreen(env, info) : nullptr; } +napi_value JsSceneSession::SetForceSplitEnable(napi_env env, napi_callback_info info) +{ + TLOGD(WmsLogTag::WMS_COMPAT, "[NAPI]"); + JsSceneSession* me = CheckParamsAndGetThis(env, info); + return (me != nullptr) ? me->OnSetForceSplitEnable(env, info) : nullptr; +} + +napi_value JsSceneSession::UpdateHookWindowInfo(napi_env env, napi_callback_info info) +{ + TLOGD(WmsLogTag::WMS_COMPAT, "[NAPI]"); + JsSceneSession* me = CheckParamsAndGetThis(env, info); + return (me != nullptr) ? me->OnUpdateHookWindowInfo(env, info) : nullptr; +} + napi_value JsSceneSession::SetPcAppInpadSpecificSystemBarInvisible(napi_env env, napi_callback_info info) { TLOGD(WmsLogTag::WMS_PC, "[NAPI]"); @@ -7595,6 +7611,77 @@ napi_value JsSceneSession::OnSetMobileAppInPadLayoutFullScreen(napi_env env, nap return NapiGetUndefined(env); } +napi_value JsSceneSession::OnSetForceSplitEnable(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_THREE; + napi_value argv[ARG_INDEX_3] = { nullptr }; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + if (argc != ARGC_THREE) { + TLOGE(WmsLogTag::WMS_COMPAT, "Argc is invalid: %{public}zu", argc); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), + "Input parameter is missing or invalid")); + return NapiGetUndefined(env); + } + + bool isForceSplitEnabled = false; + if (!ConvertFromJsValue(env, argv[ARG_INDEX_0], isForceSplitEnabled)) { + TLOGE(WmsLogTag::WMS_COMPAT, "Failed to convert parameter to isForceSplitEnabled"); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), + "Input parameter is missing or invalid")); + return NapiGetUndefined(env); + } + bool needUpdateViewport = false; + if (!ConvertFromJsValue(env, argv[ARG_INDEX_1], needUpdateViewport)) { + TLOGE(WmsLogTag::WMS_COMPAT, "Failed to convert parameter to needUpdateViewport"); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), + "Input parameter is missing or invalid")); + return NapiGetUndefined(env); + } + SelectMode selectMode = SelectMode::WIDE_MODE; + if (!ConvertFromJsValue(env, argv[ARG_INDEX_2], selectMode)) { + TLOGE(WmsLogTag::WMS_COMPAT, "Failed to convert parameter to selectMode"); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), + "Input parameter is missing or invalid")); + return NapiGetUndefined(env); + } + + auto session = weakSession_.promote(); + if (session == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "session is nullptr, id:%{public}d", persistentId_); + return NapiGetUndefined(env); + } + session->SetForceSplitEnable(isForceSplitEnabled, needUpdateViewport, selectMode); + return NapiGetUndefined(env); +} + +napi_value JsSceneSession::OnUpdateHookWindowInfo(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_TWO; + napi_value argv[ARG_INDEX_2] = { nullptr }; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + if (argc != ARGC_ONE) { + TLOGE(WmsLogTag::WMS_COMPAT, "Argc is invalid: %{public}zu", argc); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), + "Input parameter is missing or invalid")); + return NapiGetUndefined(env); + } + + HookWindowInfo hookWindowInfo{}; + if (!ConvertHookWindowInfoFromJs(env, argv[ARG_INDEX_0], hookWindowInfo)) { + TLOGE(WmsLogTag::WMS_COMPAT, "Failed to convert parameter to hookWindowInfo"); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), + "Input parameter is missing or invalid")); + return NapiGetUndefined(env); + } + auto session = weakSession_.promote(); + if (session == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "session is nullptr, id:%{public}d", persistentId_); + return NapiGetUndefined(env); + } + session->UpdateHookWindowInfo(hookWindowInfo); + return NapiGetUndefined(env); +} + napi_value JsSceneSession::OnSetPcAppInpadSpecificSystemBarInvisible(napi_env env, napi_callback_info info) { size_t argc = ARGC_FOUR; diff --git a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.h b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.h index ef43900e58..ac441d8c69 100644 --- a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.h +++ b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session.h @@ -288,6 +288,8 @@ private: static napi_value SetPcAppInpadOrientationLandscape(napi_env env, napi_callback_info info); static napi_value UpdateSceneAnimationConfig(napi_env env, napi_callback_info info); static napi_value SetMobileAppInPadLayoutFullScreen(napi_env env, napi_callback_info info); + static napi_value SetForceSplitEnable(napi_env env, napi_callback_info info); + static napi_value UpdateHookWindowInfo(napi_env env, napi_callback_info info); static napi_value NotifyOrientationExecutionResult(napi_env env, napi_callback_info info); static napi_value GetSceneNodeCount(napi_env env, napi_callback_info info); static napi_value NotifyPreCalcWindowProperty(napi_env env, napi_callback_info info); @@ -400,6 +402,8 @@ private: napi_value OnUpdateSceneAnimationConfig(napi_env env, napi_callback_info info); napi_value OnGetUid(napi_env env, napi_callback_info info); napi_value OnSetMobileAppInPadLayoutFullScreen(napi_env env, napi_callback_info info); + napi_value OnSetForceSplitEnable(napi_env env, napi_callback_info info); + napi_value OnUpdateHookWindowInfo(napi_env env, napi_callback_info info); napi_value OnNotifyOrientationExecutionResult(napi_env env, napi_callback_info info); napi_value OnGetSceneNodeCount(napi_env env, napi_callback_info info); napi_value OnNotifyPreCalcWindowProperty(napi_env env, napi_callback_info info); diff --git a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.cpp b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.cpp index 63056d7187..e483ce9f13 100644 --- a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.cpp +++ b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.cpp @@ -294,16 +294,12 @@ napi_value JsSceneSessionManager::Init(napi_env env, napi_value exportObj) JsSceneSessionManager::InitScheduleUtils); BindNativeFunction(env, exportObj, "setAppForceLandscapeConfig", moduleName, JsSceneSessionManager::SetAppForceLandscapeConfig); - BindNativeFunction(env, exportObj, "setAppForceLandscapeConfigEnable", moduleName, - JsSceneSessionManager::SetAppForceLandscapeConfigEnable); BindNativeFunction(env, exportObj, "setSelectMode", moduleName, JsSceneSessionManager::SetSelectMode); BindNativeFunction(env, exportObj, "isScbCoreEnabled", moduleName, JsSceneSessionManager::IsScbCoreEnabled); BindNativeFunction(env, exportObj, "updateAppHookDisplayInfo", moduleName, JsSceneSessionManager::UpdateAppHookDisplayInfo); - BindNativeFunction(env, exportObj, "updateAppHookWindowInfo", moduleName, - JsSceneSessionManager::UpdateAppHookWindowInfo); BindNativeFunction(env, exportObj, "notifyHookOrientationChange", moduleName, JsSceneSessionManager::NotifyHookOrientationChange); BindNativeFunction(env, exportObj, "refreshPcZOrder", moduleName, @@ -1499,14 +1495,6 @@ napi_value JsSceneSessionManager::UpdateAppHookDisplayInfo(napi_env env, napi_ca return (me != nullptr) ? me->OnUpdateAppHookDisplayInfo(env, info) : nullptr; } -napi_value JsSceneSessionManager::UpdateAppHookWindowInfo(napi_env env, napi_callback_info info) -{ - TLOGI(WmsLogTag::WMS_COMPAT, "[NAPI]"); - JsSceneSessionManager* me = CheckParamsAndGetThis(env, info); - return (me != nullptr) ? me->OnUpdateAppHookWindowInfo(env, info) : nullptr; -} - - napi_value JsSceneSessionManager::NotifyHookOrientationChange(napi_env env, napi_callback_info info) { TLOGD(WmsLogTag::WMS_COMPAT, "[NAPI]"); @@ -4796,38 +4784,6 @@ napi_value JsSceneSessionManager::OnUpdateAppHookDisplayInfo(napi_env env, napi_ return NapiGetUndefined(env); } -napi_value JsSceneSessionManager::OnUpdateAppHookWindowInfo(napi_env env, napi_callback_info info) -{ - size_t argc = ARGC_TWO; - napi_value argv[ARGC_TWO] = { nullptr }; - napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); - - if (argc < ARGC_TWO) { - TLOGE(WmsLogTag::WMS_COMPAT, "Argc is invalid: %{public}zu", argc); - napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), - "Input parameter is missing or invalid")); - return NapiGetUndefined(env); - } - - std::string bundleName; - if (!ConvertFromJsValue(env, argv[ARG_INDEX_ZERO], bundleName)) { - TLOGE(WmsLogTag::WMS_COMPAT, "Failed to convert parameter to bundleName"); - napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), - "Input parameter is missing or invalid")); - return NapiGetUndefined(env); - } - - HookWindowInfo hookWindowInfo{}; - if (!ConvertHookWindowInfoFromJs(env, argv[ARG_INDEX_ONE], hookWindowInfo)) { - TLOGE(WmsLogTag::WMS_COMPAT, "Failed to convert parameter to hookWindowInfo"); - napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), - "Input parameter is missing or invalid")); - return NapiGetUndefined(env); - } - SceneSessionManager::GetInstance().UpdateAppHookWindowInfo(bundleName, hookWindowInfo); - return NapiGetUndefined(env); -} - napi_value JsSceneSessionManager::OnNotifyHookOrientationChange(napi_env env, napi_callback_info info) { size_t argc = ARGC_ONE; @@ -4879,16 +4835,6 @@ napi_value JsSceneSessionManager::OnSetAppForceLandscapeConfig(napi_env env, nap } AppForceLandscapeConfig config; - napi_value jsMode = nullptr; - napi_get_named_property(env, argv[ARG_INDEX_ONE], "mode", &jsMode); - RETURN_IF_CONVERT_FAIL(env, jsMode, config.mode_, "mode", WmsLogTag::DEFAULT); - napi_value jsSupportSplit = nullptr; - napi_get_named_property(env, argv[ARG_INDEX_ONE], "supportSplit", &jsSupportSplit); - RETURN_IF_CONVERT_FAIL(env, jsSupportSplit, config.supportSplit_, "supportSplit", WmsLogTag::DEFAULT); - napi_value jsIgnoreOrient = nullptr; - napi_get_named_property(env, argv[ARG_INDEX_ONE], "ignoreOrientation", &jsIgnoreOrient); - RETURN_IF_CONVERT_FAIL(env, jsIgnoreOrient, config.ignoreOrientation_, "ignoreOrientation", - WmsLogTag::DEFAULT); napi_value jsContainsSysConfig = nullptr; napi_get_named_property(env, argv[ARG_INDEX_ONE], "containsSysConfig", &jsContainsSysConfig); RETURN_IF_CONVERT_FAIL(env, jsContainsSysConfig, config.containsSysConfig_, "containsSysConfig", @@ -4913,66 +4859,16 @@ napi_value JsSceneSessionManager::OnSetAppForceLandscapeConfig(napi_env env, nap napi_get_named_property(env, argv[ARG_INDEX_THREE], "configJsonStr", &jsAppConfigJsonStr); ConvertFromJsValue(env, jsAppConfigJsonStr, config.appConfigJsonStr_); - TLOGI(WmsLogTag::DEFAULT, "SetAppForceLandscapeConfig bundleName: %{public}s, mode: %{public}d, " - "supportSplit: %{public}d, ignoreOrientation: %{public}d, containsSysConfig: %{public}d, " - "containsAppConfig: %{public}d, isSysRouter: %{public}d, sysConfigJsonStr: %{public}s," + TLOGI(WmsLogTag::DEFAULT, "SetAppForceLandscapeConfig bundleName: %{public}s, containsSysConfig: %{public}d, " + "containsAppConfig: %{public}d, isSysRouter: %{public}d, sysConfigJsonStr: %{public}s, " "sysHomePage: %{public}s, isAppRouter: %{public}d, appConfigJsonStr: %{public}s", - bundleName.c_str(), config.mode_, config.supportSplit_, config.ignoreOrientation_, - config.containsSysConfig_, config.containsAppConfig_, config.isSysRouter_, + bundleName.c_str(), config.containsSysConfig_, config.containsAppConfig_, config.isSysRouter_, config.sysConfigJsonStr_.c_str(), config.sysHomePage_.c_str(), config.isAppRouter_, config.appConfigJsonStr_.c_str()); SceneSessionManager::GetInstance().SetAppForceLandscapeConfig(bundleName, config); return NapiGetUndefined(env); } -napi_value JsSceneSessionManager::SetAppForceLandscapeConfigEnable(napi_env env, napi_callback_info info) -{ - JsSceneSessionManager* me = CheckParamsAndGetThis(env, info); - return (me != nullptr) ? me->OnSetAppForceLandscapeConfigEnable(env, info) : nullptr; -} - -napi_value JsSceneSessionManager::OnSetAppForceLandscapeConfigEnable(napi_env env, napi_callback_info info) -{ - size_t argc = ARGC_FOUR; - napi_value argv[ARGC_FOUR] = { nullptr }; - napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); - if (argc != OHOS::Rosen::ARGC_FOUR) { - TLOGE(WmsLogTag::DEFAULT, "Argc is invalid: %{public}zu", argc); - napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), - "Input parameter is missing or invalid")); - return NapiGetUndefined(env); - } - - std::string bundleName; - if (!ConvertFromJsValue(env, argv[ARG_INDEX_ZERO], bundleName)) { - TLOGE(WmsLogTag::DEFAULT, "Failed to convert parameter to bundleName"); - napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), - "Input parameter is missing or invalid")); - return NapiGetUndefined(env); - } - - bool enableForceSplit = false; - if (GetType(env, argv[ARG_INDEX_ONE]) == napi_boolean) { - RETURN_IF_CONVERT_FAIL(env, argv[ARG_INDEX_ONE], enableForceSplit, "enableForceSplit", WmsLogTag::DEFAULT); - } - bool needUpdateViewport = false; - if (GetType(env, argv[ARG_INDEX_TWO]) == napi_boolean) { - RETURN_IF_CONVERT_FAIL(env, argv[ARG_INDEX_TWO], needUpdateViewport, "needUpdateViewport", WmsLogTag::DEFAULT); - } - SelectMode selectMode = SelectMode::WIDE_MODE; - if (GetType(env, argv[ARG_INDEX_THREE]) == napi_number) { - RETURN_IF_CONVERT_FAIL(env, argv[ARG_INDEX_THREE], selectMode, "selectMode", WmsLogTag::DEFAULT); - } - - TLOGI(WmsLogTag::DEFAULT, "SetAppForceLandscapeConfigEnable bundleName: %{public}s, enable: %{public}d, " - "needUpdateViewport: %{public}d, selectMode: %{public}u", bundleName.c_str(), - enableForceSplit, needUpdateViewport, static_cast(selectMode)); - - SceneSessionManager::GetInstance().SetAppForceLandscapeConfigEnable(bundleName, enableForceSplit, - needUpdateViewport, selectMode); - return NapiGetUndefined(env); -} - napi_value JsSceneSessionManager::SetSelectMode(napi_env env, napi_callback_info info) { JsSceneSessionManager* me = CheckParamsAndGetThis(env, info); diff --git a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.h b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.h index 899ee1aa5f..1b2098ceed 100644 --- a/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.h +++ b/window_scene/interfaces/kits/napi/scene_session_manager/js_scene_session_manager.h @@ -118,7 +118,6 @@ public: static napi_value NotifySCBRecentStateChange(napi_env env, napi_callback_info info); static napi_value UpdateDisplayHookInfo(napi_env env, napi_callback_info info); static napi_value UpdateAppHookDisplayInfo(napi_env env, napi_callback_info info); - static napi_value UpdateAppHookWindowInfo(napi_env env, napi_callback_info info); static napi_value NotifyHookOrientationChange(napi_env env, napi_callback_info info); static napi_value InitScheduleUtils(napi_env env, napi_callback_info info); static napi_value SetAppForceLandscapeConfig(napi_env env, napi_callback_info info); @@ -148,7 +147,6 @@ public: static napi_value NotifySupportRotationChange(napi_env env, napi_callback_info info); static napi_value GetAllJsonProfile(napi_env env, napi_callback_info info); static napi_value GetJsonProfile(napi_env env, napi_callback_info info); - static napi_value SetAppForceLandscapeConfigEnable(napi_env env, napi_callback_info info); static napi_value SetSelectMode(napi_env env, napi_callback_info info); /* @@ -274,7 +272,6 @@ private: napi_value OnNotifySCBRecentStateChange(napi_env env, napi_callback_info info); napi_value OnUpdateDisplayHookInfo(napi_env env, napi_callback_info info); napi_value OnUpdateAppHookDisplayInfo(napi_env env, napi_callback_info info); - napi_value OnUpdateAppHookWindowInfo(napi_env env, napi_callback_info info); napi_value OnNotifyHookOrientationChange(napi_env env, napi_callback_info info); napi_value OnInitScheduleUtils(napi_env env, napi_callback_info info); napi_value OnSetAppForceLandscapeConfig(napi_env env, napi_callback_info info); @@ -300,7 +297,6 @@ private: napi_value OnNotifySupportRotationChange(napi_env env, napi_callback_info info); napi_value OnGetAllJsonProfile(napi_env env, napi_callback_info info); napi_value OnGetJsonProfile(napi_env env, napi_callback_info info); - napi_value OnSetAppForceLandscapeConfigEnable(napi_env env, napi_callback_info info); napi_value OnSetSelectMode(napi_env env, napi_callback_info info); /* diff --git a/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.cpp b/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.cpp index 8d63c16053..ad9ac8f547 100644 --- a/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.cpp +++ b/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.cpp @@ -42,6 +42,7 @@ constexpr int32_t INVALID_ID = -1; namespace { const std::string ON_SCREEN_CONNECTION_CHANGE_CALLBACK = "screenConnectChange"; const std::string ON_TENT_MODE_CHANGE_CALLBACK = "tentModeChange"; +const std::string ON_EXT_SCREEN_UNSUPPORT_CALLBACK = "extScreenUnsupport"; const std::map POWER_STATE_MAP { { ScbScreenPowerState::POWER_OFF, ScreenPowerState::POWER_OFF }, { ScbScreenPowerState::POWER_DOZE, ScreenPowerState::POWER_DOZE }, @@ -90,6 +91,7 @@ napi_value JsScreenSessionManager::Init(napi_env env, napi_value exportObj) const char* moduleName = "JsScreenSessionManager"; BindNativeFunction(env, exportObj, "on", moduleName, JsScreenSessionManager::RegisterCallback); + BindNativeFunction(env, exportObj, "off", moduleName, JsScreenSessionManager::UnRegisterCallback); BindNativeFunction(env, exportObj, "updateScreenRotationProperty", moduleName, JsScreenSessionManager::UpdateScreenRotationProperty); BindNativeFunction(env, exportObj, "updateServerScreenProperty", moduleName, @@ -190,6 +192,13 @@ napi_value JsScreenSessionManager::RegisterCallback(napi_env env, napi_callback_ return (me != nullptr) ? me->OnRegisterCallback(env, info) : nullptr; } +napi_value JsScreenSessionManager::UnRegisterCallback(napi_env env, napi_callback_info info) +{ + TLOGD(WmsLogTag::DMS, "[NAPI]UnRegisterCallback"); + JsScreenSessionManager* me = CheckParamsAndGetThis(env, info); + return (me != nullptr) ? me->OnUnRegisterCallback(env, info) : nullptr; +} + napi_value JsScreenSessionManager::UpdateScreenRotationProperty(napi_env env, napi_callback_info info) { TLOGD(WmsLogTag::DMS, "[NAPI]UpdateScreenRotationProperty"); @@ -534,6 +543,145 @@ void JsScreenSessionManager::OnTentModeChange(const TentMode tentMode) } } +void JsScreenSessionManager::RegisterTransRSEventCallback(napi_env env, napi_ref& callback, RSExposedEventType type) +{ + TLOGI(WmsLogTag::DMS, "[NAPI]Register, type:%{public}u", static_cast(type)); + { + std::shared_lock lock(rsEventCallbacksMutex_); + auto it = rsEventCallbacks_.find(type); + if (it != rsEventCallbacks_.end()) { + napi_value callbackNapi; + napi_get_reference_value(env, callback, &callbackNapi); + + for (auto* callbackItem : it->second) { + if (!callbackItem) { + continue; + } + bool isEquals = false; + napi_strict_equals(env, callbackItem->GetNapiValue(), callbackNapi, &isEquals); + if (isEquals) { + TLOGW(WmsLogTag::DMS, "[NAPI]Callback already registered for type:%{public}u", + static_cast(type)); + return; + } + } + } + } + NativeReference* callbackRef = reinterpret_cast(callback); + { + std::unique_lock lock(rsEventCallbacksMutex_); + rsEventCallbacks_[type].emplace_back(callbackRef); + } + + bool isFirstCallback = false; + { + std::shared_lock lock(rsEventCallbacksMutex_); + isFirstCallback = (rsEventCallbacks_[type].size() == 1); + } + if (isFirstCallback) { + ScreenSessionManagerClient::GetInstance().RegisterTransRSEventListener(type, this); + } + + TLOGI(WmsLogTag::DMS, "[NAPI]Success to register type:%{public}u", static_cast(type)); +} + +void JsScreenSessionManager::UnRegisterTransRSEventCallback(napi_env env, napi_ref& callback, RSExposedEventType type) +{ + std::unique_lock lock(rsEventCallbacksMutex_); + auto it = rsEventCallbacks_.find(type); + if (it == rsEventCallbacks_.end()) { + TLOGE(WmsLogTag::DMS, "[NAPI] No callbacks registered for type:%{public}u", static_cast(type)); + return; + } + + auto& callbacks = it->second; + auto iter = std::find_if(callbacks.begin(), callbacks.end(), + [&](NativeReference* callbackItem) { + if (!callbackItem) return false; + napi_value callbackNapi; + napi_get_reference_value(env, callback, &callbackNapi); + bool isEquals = false; + napi_strict_equals(env, callbackItem->GetNapiValue(), callbackNapi, &isEquals); + return isEquals; + }); + if (iter != callbacks.end()) { + napi_delete_reference(env, reinterpret_cast(*iter)); + callbacks.erase(iter); + TLOGI(WmsLogTag::DMS, "[NAPI] Unregistered callback for type:%{public}u", static_cast(type)); + if (callbacks.empty()) { + rsEventCallbacks_.erase(it); + ScreenSessionManagerClient::GetInstance().UnRegisterTransRSEventListener(type, this); + } + } else { + TLOGE(WmsLogTag::DMS, "[NAPI]Callback not registered for type:%{public}u", static_cast(type)); + } +} + +napi_value JsScreenSessionManager::ConvertRsEventToNapiValue(napi_env env, const sptr& event) +{ + if (!event) { + return nullptr; + } + + napi_value obj; + napi_create_object(env, &obj); + + switch (event->GetEventType()) { + case RSExposedEventType::EXT_SCREEN_UNSUPPORT: { + break; + } + default: + break; + } + + return obj; +} + +void JsScreenSessionManager::OnTransRSEvent(const sptr& data) +{ + if (!data) { + TLOGE(WmsLogTag::DMS, "[NAPI] data is null"); + return; + } + + RSExposedEventType type = data->GetEventType(); + std::vector callbacks; + { + std::shared_lock lock(rsEventCallbacksMutex_); + auto it = rsEventCallbacks_.find(type); + if (it == rsEventCallbacks_.end() || it->second.empty()) { + TLOGW(WmsLogTag::DMS, "[NAPI] callbacks empty for type:%{public}u", static_cast(type)); + return; + } + callbacks = it->second; + } + + for (auto& callback : callbacks) { + TLOGI(WmsLogTag::DMS, "[NAPI] OnRSEvent begin, type:%{public}u", static_cast(type)); + auto asyncTask = [this, callback, data, env = env_]() { + HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsScreenSessionManager::OnRSEvent"); + + napi_value jsEvent = ConvertRsEventToNapiValue(env, data); + napi_value argv[] = { jsEvent }; + napi_value method = callback->GetNapiValue(); + if (method == nullptr) { + TLOGNE(WmsLogTag::DMS, "Failed to get method callback from object!"); + return; + } + napi_call_function(env, NapiGetUndefined(env), method, ArraySize(argv), argv, nullptr); + }; + + if (env_ != nullptr) { + napi_status ret = napi_send_event(env_, asyncTask, napi_eprio_vip, "OnRSEvent"); + if (ret != napi_status::napi_ok) { + TLOGE(WmsLogTag::DMS, "Failed to SendEvent."); + } + } else { + TLOGE(WmsLogTag::DMS, "env is nullptr"); + } + } +} + napi_value JsScreenSessionManager::SetCameraStatus(napi_env env, napi_callback_info info) { TLOGD(WmsLogTag::DMS, "[NAPI]SetCameraStatus"); @@ -684,6 +832,8 @@ napi_value JsScreenSessionManager::OnRegisterCallback(napi_env env, const napi_c RegisterScreenConnectionCallback(env, callbackRef); } else if (callbackType == ON_TENT_MODE_CHANGE_CALLBACK) { RegisterTentModeCallback(env, callbackRef); + } else if (callbackType == ON_EXT_SCREEN_UNSUPPORT_CALLBACK) { + RegisterTransRSEventCallback(env, callbackRef, RSExposedEventType::EXT_SCREEN_UNSUPPORT); } else { TLOGE(WmsLogTag::DMS, "Unsupported callback type: %{public}s.", callbackType.c_str()); napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM))); @@ -691,6 +841,25 @@ napi_value JsScreenSessionManager::OnRegisterCallback(napi_env env, const napi_c return NapiGetUndefined(env); } +napi_value JsScreenSessionManager::OnUnRegisterCallback(napi_env env, const napi_callback_info info) +{ + std::string callbackType; + napi_ref callbackRef; + if (!ObtainCallBackInfo(env, info, callbackType, callbackRef)) { + TLOGE(WmsLogTag::DMS, "[NAPI] param check fail"); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM))); + return NapiGetUndefined(env); + } + if (callbackType == ON_EXT_SCREEN_UNSUPPORT_CALLBACK) { + UnRegisterTransRSEventCallback(env, callbackRef, RSExposedEventType::EXT_SCREEN_UNSUPPORT); + } else { + TLOGE(WmsLogTag::DMS, "Unsupported callback type: %{public}s.", callbackType.c_str()); + napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM))); + } + napi_delete_reference(env, callbackRef); + return NapiGetUndefined(env); +} + void JsScreenSessionManager::RegisterScreenConnectionCallback(napi_env env, napi_ref& callback) { TLOGI(WmsLogTag::DMS, "[NAPI] begin"); @@ -1534,7 +1703,7 @@ napi_value JsScreenSessionManager::OnGetScreenSnapshotWithAllWindows(napi_env en std::array scaleParam; for (size_t i = 0; i < ARGC_TWO; i++) { if (!ConvertFromJsValue(env, argv[i + 1], scaleParam[i])) { - TLOGE(WmsLogTag::DMS, "[NAPI]Failed to convert parameter to scale[%d]", i + 1); + TLOGE(WmsLogTag::DMS, "[NAPI]Failed to convert parameter to scale[%zu]", i + 1); napi_throw(env, CreateJsError(env, static_cast(WSErrorCode::WS_ERROR_INVALID_PARAM), "Input parameter is missing or invalid")); return NapiGetUndefined(env); diff --git a/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.h b/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.h index 57ce630539..2a39b565b3 100644 --- a/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.h +++ b/window_scene/interfaces/kits/napi/screen_session_manager/js_screen_session_manager.h @@ -28,7 +28,7 @@ namespace OHOS::Rosen { class JsScreenSessionManager final : public IScreenConnectionListener, public ITentModeListener, - public PowerMgr::TakeOverShutdownCallbackStub { + public ITransRSEventListener, public PowerMgr::TakeOverShutdownCallbackStub { public: explicit JsScreenSessionManager(napi_env env); ~JsScreenSessionManager(); @@ -40,9 +40,11 @@ public: void OnScreenDisconnected(const sptr& screenSession) override; void OnTentModeChange(const TentMode tentMode) override; bool OnTakeOverShutdown(const PowerMgr::TakeOverInfo& info) override; + void OnTransRSEvent(const sptr& data) override; private: static napi_value RegisterCallback(napi_env env, napi_callback_info info); + static napi_value UnRegisterCallback(napi_env env, napi_callback_info info); static napi_value UpdateScreenRotationProperty(napi_env env, napi_callback_info info); static napi_value UpdateServerScreenProperty(napi_env env, napi_callback_info info); static napi_value GetCurvedCompressionArea(napi_env env, napi_callback_info info); @@ -83,8 +85,12 @@ private: static napi_value RegisterSwitchUserAnimationNotification(napi_env env, napi_callback_info info); napi_value OnRegisterCallback(napi_env env, const napi_callback_info info); + napi_value OnUnRegisterCallback(napi_env env, const napi_callback_info info); void RegisterScreenConnectionCallback(napi_env env, napi_ref& callbackRef); void RegisterTentModeCallback(napi_env env, napi_ref& callbackRef); + void RegisterTransRSEventCallback(napi_env env, napi_ref& callback, RSExposedEventType type); + void UnRegisterTransRSEventCallback(napi_env env, napi_ref& callback, RSExposedEventType type); + napi_value ConvertRsEventToNapiValue(napi_env env, const sptr& data); napi_value OnUpdateScreenRotationProperty(napi_env env, const napi_callback_info info); napi_value OnUpdateServerScreenProperty(napi_env env, const napi_callback_info info); napi_value OnGetCurvedCompressionArea(napi_env env, const napi_callback_info info); @@ -134,6 +140,8 @@ private: napi_env env_; std::map jsScreenSessionMap_; std::shared_mutex tentModeChangeCallbackMutex_; + std::shared_mutex rsEventCallbacksMutex_; + std::unordered_map> rsEventCallbacks_; }; } // namespace OHOS::Rosen diff --git a/window_scene/screen_session_manager/BUILD.gn b/window_scene/screen_session_manager/BUILD.gn index 1cf5dbb353..d5b7dd002d 100644 --- a/window_scene/screen_session_manager/BUILD.gn +++ b/window_scene/screen_session_manager/BUILD.gn @@ -90,7 +90,8 @@ ohos_shared_library("screen_session_manager") { "src/setting_observer.cpp", "src/setting_provider.cpp", "src/zidl/screen_session_manager_stub.cpp", - "src/screen_session_manager_adapter.cpp" + "src/screen_session_manager_adapter.cpp", + "src/rs_event_data_manager.cpp" ] cflags_cc = [ "-std=c++17" ] @@ -283,6 +284,7 @@ ohos_shared_library("screen_session_manager") { print("window manager screen multi usr ability is disabled.") } + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/window_scene/screen_session_manager/include/rs_event_data_manager.h b/window_scene/screen_session_manager/include/rs_event_data_manager.h new file mode 100644 index 0000000000..42fd53cb71 --- /dev/null +++ b/window_scene/screen_session_manager/include/rs_event_data_manager.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCREEN_RS_EVENT_DATA_MANAGER_H +#define SCREEN_RS_EVENT_DATA_MANAGER_H + +#include +#include +#include + +namespace OHOS { +namespace Rosen { + +class RSEventDataBase : public Parcelable { +public: + virtual ~RSEventDataBase() = default; + virtual RSExposedEventType GetEventType() const = 0; + virtual bool Unmarshalling(Parcel& parcel) = 0; +}; + +class RSExtScreenUnsupportEventData : public RSEventDataBase { +public: + ~RSExtScreenUnsupportEventData() override = default; + RSExposedEventType GetEventType() const override; + bool Marshalling(Parcel& parcel) const override; + bool Unmarshalling(Parcel& parcel) override; +}; + +} +} +#endif /* SCREEN_RS_EVENT_DATA_MANAGER_H */ \ No newline at end of file diff --git a/window_scene/screen_session_manager/include/screen_session_manager.h b/window_scene/screen_session_manager/include/screen_session_manager.h index b3a590fdcd..0952fad3e5 100644 --- a/window_scene/screen_session_manager/include/screen_session_manager.h +++ b/window_scene/screen_session_manager/include/screen_session_manager.h @@ -333,6 +333,7 @@ public: bool IsFoldable() override; bool IsCaptured() override; + bool IsCapturedByBundleNameList(const std::vector& bundleNameList) override; FoldStatus GetFoldStatus() override; SuperFoldStatus GetSuperFoldStatus() override; @@ -396,7 +397,7 @@ public: void OnDisconnect(ScreenId screenId) override {} void OnPropertyChange(const ScreenProperty& newProperty, ScreenPropertyChangeReason reason, ScreenId screenId) override; - void UpdateDisplayOrientationWhenBootAnimation(ScreenId screenId); + void UpdateDisplayOrientationWhenBootAnimation(ScreenId screenId, const ScreenProperty& screenProperty); void OnFoldPropertyChange(ScreenId screenId, const ScreenProperty& newProperty, ScreenPropertyChangeReason reason, FoldDisplayMode displayMode) override; void OnPowerStatusChange(DisplayPowerEvent event, EventStatus status, @@ -646,6 +647,7 @@ public: MultiScreenPositionOptions& dynamicScreenOptions, uint32_t adjacentPercentage, uint32_t staticHeight, uint32_t staticWidth, uint32_t dynamicHeight); void GetStaticAndDynamicSession(); + const std::map& GetScreenActiveModeRectMap(); static bool GetScreenSessionMngSystemAbility(); void RunFinishTask(); @@ -713,6 +715,11 @@ private: void ConfigureWaterfallDisplayCompressionParams(); void ConfigureScreenSnapshotParams(); void RegisterScreenChangeListener(); + void RegisterRSListeners(); + void DoRegisterRSListeners(); + void OnTransRSEvent(const std::shared_ptr& rsRawData); + sptr ConvertRSExposedEventDataBase( + const std::shared_ptr& rsRawData); void RegisterFoldNotSwitchingListener(); void RegisterBrightnessInfoChangeListener(); void UnregisterBrightnessInfoChangeListener(); @@ -1240,6 +1247,7 @@ private: void SetOptionConfig(ScreenId screenId, VirtualScreenOption option); void DoSetScreenPowerStatus(ScreenId rsScreenId, ScreenPowerStatus status); void ClearScreenPowerStatus(ScreenId rsScreenId); + void InitScreenActiveModeRectMap(); std::map screenPowerStatusMap_; std::mutex screenPowerStatusMapMutex_; @@ -1250,6 +1258,9 @@ private: std::atomic foldDisplayModeAfterRotation_ = FoldDisplayMode::UNKNOWN; std::atomic onBootAnimation_ = false; bool isBoot_ = false; + int32_t retryCount_ = 50; + std::mutex screenActiveModeRectMapMutex_; + std::map screenActiveModeRectMap_ = {}; private: class ScbClientListenerDeathRecipient : public IRemoteObject::DeathRecipient { diff --git a/window_scene/screen_session_manager/include/zidl/screen_session_manager_interface.h b/window_scene/screen_session_manager/include/zidl/screen_session_manager_interface.h index 9cfa35a514..40cde1d514 100644 --- a/window_scene/screen_session_manager/include/zidl/screen_session_manager_interface.h +++ b/window_scene/screen_session_manager/include/zidl/screen_session_manager_interface.h @@ -282,6 +282,7 @@ public: virtual bool IsFoldable() { return false; } virtual bool IsCaptured() { return false; } + virtual bool IsCapturedByBundleNameList(const std::vector& bundleNameList) { return false; } virtual FoldStatus GetFoldStatus() { return FoldStatus::UNKNOWN; } virtual SuperFoldStatus GetSuperFoldStatus() { return SuperFoldStatus::UNKNOWN; } diff --git a/window_scene/screen_session_manager/include/zidl/screen_session_manager_proxy.h b/window_scene/screen_session_manager/include/zidl/screen_session_manager_proxy.h index 2d3b6b7168..f29790e9f1 100644 --- a/window_scene/screen_session_manager/include/zidl/screen_session_manager_proxy.h +++ b/window_scene/screen_session_manager/include/zidl/screen_session_manager_proxy.h @@ -184,6 +184,7 @@ public: bool IsFoldable() override; bool IsCaptured() override; + bool IsCapturedByBundleNameList(const std::vector& bundleNameList) override; FoldStatus GetFoldStatus() override; SuperFoldStatus GetSuperFoldStatus() override; diff --git a/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_controller.h b/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_controller.h index b8aa024b47..fc29c01ad0 100644 --- a/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_controller.h +++ b/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_controller.h @@ -70,6 +70,7 @@ public: virtual void NotifyRunSensorFoldStateManager(); virtual float GetSpecialVirtualPixelRatio(); virtual void PowerkeySetScreenActiveRect(); + virtual const std::map& GetScreenActiveModeRectMap() const; private: std::vector foldCreaseRegionItems_; }; diff --git a/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_policy.h b/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_policy.h index 1bcbf12c8c..e438dc10a9 100644 --- a/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_policy.h +++ b/window_scene/screen_session_manager/infra/include/fold_screen/fold_screen_base_policy.h @@ -142,6 +142,7 @@ public: FoldStatus targetFoldStatus) const; virtual float GetSpecialVirtualPixelRatio(); virtual void PowerkeySetScreenActiveRect() {}; + const std::map& GetScreenActiveModeRectMap() const; protected: FoldScreenBasePolicy(); diff --git a/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_controller.cpp b/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_controller.cpp index d6fc04a809..da3b299631 100644 --- a/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_controller.cpp +++ b/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_controller.cpp @@ -262,4 +262,9 @@ void FoldScreenBaseController::PowerkeySetScreenActiveRect() { FoldScreenBasePolicy::GetInstance().PowerkeySetScreenActiveRect(); } + +const std::map& FoldScreenBaseController::GetScreenActiveModeRectMap() const +{ + return FoldScreenBasePolicy::GetInstance().GetScreenActiveModeRectMap(); +} } // namespace OHOS::Rosen \ No newline at end of file diff --git a/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_policy.cpp b/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_policy.cpp index c57392fbe4..7503175a93 100644 --- a/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_policy.cpp +++ b/window_scene/screen_session_manager/infra/src/fold_screen/fold_screen_base_policy.cpp @@ -1010,4 +1010,9 @@ FoldDisplayMode FoldScreenBasePolicy::GetCurrentDisplayMode() const std::lock_guard lock_mode(displayModeMutex_); return currentDisplayMode_; } + +const std::map& FoldScreenBasePolicy::GetScreenActiveModeRectMap() const +{ + return screenActiveModeRectMap_; +} } // namespace OHOS::Rosen \ No newline at end of file diff --git a/window_scene/screen_session_manager/src/fold_screen_controller/super_fold_state_manager.cpp b/window_scene/screen_session_manager/src/fold_screen_controller/super_fold_state_manager.cpp index cba5c4db3a..ab4709d95a 100644 --- a/window_scene/screen_session_manager/src/fold_screen_controller/super_fold_state_manager.cpp +++ b/window_scene/screen_session_manager/src/fold_screen_controller/super_fold_state_manager.cpp @@ -406,6 +406,10 @@ void SuperFoldStateManager::ModifyMirrorScreenVisibleRectInner(const OHOS::Rect& for (auto& [screenId, curRect]: mirrorScreenVisibleRectMap) { ScreenId rsId = SCREEN_ID_INVALID; ScreenSessionManager::GetInstance().ConvertScreenIdToRsScreenId(screenId, rsId); + auto screenSession = ScreenSessionManager::GetInstance().GetScreenSession(screenId); + if (screenSession == nullptr || screenSession->GetScreenProperty().GetScreenType() == ScreenType::VIRTUAL) { + continue; + } TLOGI(WmsLogTag::DMS, "handle mirror ScreenId: %{public}" PRIu64 ", rsId: %{public}" PRIu64, screenId, rsId); displayIds = CalculateReCordingDisplayIds(rsRect); RSInterfaces::GetInstance().SetMirrorScreenVisibleRect(rsId, rsRect); diff --git a/window_scene/screen_session_manager/src/multi_screen_change_utils.cpp b/window_scene/screen_session_manager/src/multi_screen_change_utils.cpp index 4d0e5cfd83..8cfb658ae6 100644 --- a/window_scene/screen_session_manager/src/multi_screen_change_utils.cpp +++ b/window_scene/screen_session_manager/src/multi_screen_change_utils.cpp @@ -178,6 +178,10 @@ void MultiScreenChangeUtils::ScreenCombinationChange(sptr& innerS return; } ssmClient->SetScreenCombination(innerScreen->GetScreenId(), externalScreen->GetScreenId(), externalCombination); + ScreenSessionManager::GetInstance().NotifyScreenChanged(innerScreen->ConvertToScreenInfo(), + ScreenChangeEvent::SCREEN_SOURCE_MODE_CHANGE); + ScreenSessionManager::GetInstance().NotifyScreenChanged(externalScreen->ConvertToScreenInfo(), + ScreenChangeEvent::SCREEN_SOURCE_MODE_CHANGE); } void MultiScreenChangeUtils::ScreenSerialNumberChange(sptr& innerScreen, diff --git a/window_scene/screen_session_manager/src/rs_event_data_manager.cpp b/window_scene/screen_session_manager/src/rs_event_data_manager.cpp new file mode 100644 index 0000000000..135930008d --- /dev/null +++ b/window_scene/screen_session_manager/src/rs_event_data_manager.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rs_event_data_manager.h" + +namespace OHOS::Rosen { +RSExposedEventType RSExtScreenUnsupportEventData::GetEventType() const +{ + return RSExposedEventType::EXT_SCREEN_UNSUPPORT; +} + +bool RSExtScreenUnsupportEventData::Marshalling(Parcel& parcel) const +{ + return true; +} + +bool RSExtScreenUnsupportEventData::Unmarshalling(Parcel& parcel) +{ + return true; +} +} // namespace OHOS::Rosen \ No newline at end of file diff --git a/window_scene/screen_session_manager/src/screen_session_manager.cpp b/window_scene/screen_session_manager/src/screen_session_manager.cpp index 1d5b669826..b4e5f1b3f6 100644 --- a/window_scene/screen_session_manager/src/screen_session_manager.cpp +++ b/window_scene/screen_session_manager/src/screen_session_manager.cpp @@ -474,6 +474,9 @@ void ScreenSessionManager::HandleFoldScreenPowerInit() TLOGNFE(WmsLogTag::DMS, "foldScreenController_ is nullptr"); return; } + if (FoldScreenStateInternel::IsLoadDmsExt()) { + InitScreenActiveModeRectMap(); + } foldScreenController_->SetOnBootAnimation(true); if (FoldScreenStateInternel::IsSingleDisplayPocketFoldDevice()) { SetFoldScreenPowerInit([&]() { @@ -489,6 +492,22 @@ void ScreenSessionManager::HandleFoldScreenPowerInit() #endif } +void ScreenSessionManager::InitScreenActiveModeRectMap() +{ + if (foldScreenController_ == nullptr) { + TLOGNFE(WmsLogTag::DMS, "foldScreenController is nullptr"); + return; + } + std::lock_guard lock(screenActiveModeRectMapMutex_); + screenActiveModeRectMap_ = foldScreenController_->GetScreenActiveModeRectMap(); +} + +const std::map& ScreenSessionManager::GetScreenActiveModeRectMap() +{ + std::lock_guard lock(screenActiveModeRectMapMutex_); + return screenActiveModeRectMap_; +} + bool ScreenSessionManager::IsSupportCoordination() { return !FoldScreenStateInternel::IsDualDisplayFoldDevice() || IS_COORDINATION_SUPPORT; @@ -596,6 +615,7 @@ void ScreenSessionManager::Init() } AodLibInit(); RegisterScreenChangeListener(); + RegisterRSListeners(); if(FoldScreenStateInternel::IsSecondaryDisplayFoldDevice() || FoldScreenStateInternel::IsLoadDmsExt() || FoldScreenStateInternel::IsSingleDisplayPocketFoldDevice() || @@ -988,6 +1008,70 @@ void ScreenSessionManager::RegisterScreenChangeListener() } } +void ScreenSessionManager::RegisterRSListeners() +{ + TLOGNFI(WmsLogTag::DMS, "RegisterRSListeners Start"); + DoRegisterRSListeners(); +} + +void ScreenSessionManager::DoRegisterRSListeners() +{ + auto res = rsInterface_.RegisterExposedEventCallback(RSExposedEventType::EXT_SCREEN_UNSUPPORT, + DmUtils::wrap_callback([this](const std::shared_ptr& param) { + OnTransRSEvent(param); + }) + ); + if (res != StatusCode::SUCCESS && retryCount_-- > 0) { + auto task = [this]() { DoRegisterRSListeners(); }; + taskScheduler_->PostAsyncTask(task, "RegisterRSListeners", 50); + screenEventTracker_.RecordEvent("Dms register rs events failed, will retry."); + } else if (res != StatusCode::SUCCESS) { + TLOGNFE(WmsLogTag::DMS, "Dms register rs events failed after max retries."); + screenEventTracker_.RecordEvent("Dms register rs events failed after max retries."); + } else { + screenEventTracker_.RecordEvent("Dms register rs events success."); + } +} + +sptr ScreenSessionManager::ConvertRSExposedEventDataBase( + const std::shared_ptr& rsRawData) +{ + if (!rsRawData) { + return nullptr; + } + + switch (rsRawData->type_) { + case RSExposedEventType::EXT_SCREEN_UNSUPPORT: { + sptr rsData = new (std::nothrow) RSExtScreenUnsupportEventData(); + return rsData; + } + default: { + TLOGNFW(WmsLogTag::DMS, "Unknown RSExposedEventType: %{public}u", rsRawData->type_); + break; + } + } + return nullptr; +} + +void ScreenSessionManager::OnTransRSEvent(const std::shared_ptr& rsRawData) +{ + if(!rsRawData) { + TLOGNFE(WmsLogTag::DMS, "Rsdata is null"); + return; + } + sptr rsData = ConvertRSExposedEventDataBase(rsRawData); + if (!rsData) { + return; + } + auto clientProxy = GetClientProxy(); + if (!clientProxy) { + TLOGNFE(WmsLogTag::DMS, "ClientProxy is null."); + return; + } + clientProxy->OnTransRSEvent(rsData); + return; +} + void ScreenSessionManager::RegisterFoldNotSwitchingListener() { TLOGNFI(WmsLogTag::DMS, "start"); @@ -2191,14 +2275,14 @@ void ScreenSessionManager::HandlePhysicalMirrorColorSpace(GraphicCM_ColorSpaceTy int32_t ret = RSInterfaces::GetInstance().SetScreenColorSpace(screenId, colorSpace); if (ret != StatusCode::SUCCESS) { TLOGE(WmsLogTag::DMS, - "SetMirrorColorSpace fail! ret:%{public}d, screenId:%{public}llu, colorSpace:%{public}d", + "SetMirrorColorSpace fail! ret:%{public}d, screenId:%{public}" PRIu64", colorSpace:%{public}d", ret, screenId, colorSpace); return; } TLOGI(WmsLogTag::DMS, - "SetMirrorColorSpace success, screenId:%{public}llu, colorSpace:%{public}d", + "SetMirrorColorSpace success, screenId:%{public}" PRIu64", colorSpace:%{public}d", screenId, colorSpace); } @@ -8070,7 +8154,7 @@ DMError ScreenSessionManager::MakeMirrorForRecord(const std::vector& m return DMError::DM_ERROR_NOT_SYSTEM_APP; } auto realScreenId = SuperFoldPolicy::GetInstance().GetRealScreenId(mainScreenIds); - TLOGNFI(WmsLogTag::DMS, "realScreenId: %{public}llu", static_cast(realScreenId)); + TLOGNFI(WmsLogTag::DMS, "realScreenId: %{public}" PRIu64, static_cast(realScreenId)); if (FoldScreenStateInternel::IsSuperFoldDisplayDevice() && realScreenId != SCREEN_ID_INVALID) { DMRect mainScreenRect = SuperFoldPolicy::GetInstance().GetRecordRect(mainScreenIds); std::ostringstream oss; @@ -10214,7 +10298,7 @@ DMError ScreenSessionManager::SetFoldDisplayModeInner(const FoldDisplayMode disp if (reason.compare("backSelfie") == 0) { UpdateCameraBackSelfie(true); } - if (reason.compare("exitCoordinationMode")) { + if (reason == "exitCoordinationMode" || reason == "exitBackSelfie") { ExitCoordinationAndRecoverDisplayMode(); return DMError::DM_OK; } @@ -10663,6 +10747,34 @@ bool ScreenSessionManager::IsCaptured() } } +bool ScreenSessionManager::IsCapturedByBundleNameList(const std::vector& bundleNameList) +{ + if (bundleNameList.empty()) { + return false; + } + + std::unordered_set bundleNameSet(bundleNameList.begin(), bundleNameList.end()); + + std::lock_guard lock(screenSessionMapMutex_); + for (auto& sessionItem : screenSessionMap_) { + auto screenSession = sessionItem.second; + if (screenSession == nullptr) { + continue; + } + if (screenSession->GetScreenProperty().GetScreenType() != ScreenType::VIRTUAL) { + continue; + } + + const std::string& bundleName = screenSession->GetBundleName(); + if (bundleNameSet.count(bundleName)) { + TLOGI(WmsLogTag::DMS, "Found capturing app: %{public}s", bundleName.c_str()); + return true; + } + } + + return false; +} + bool ScreenSessionManager::IsMultiScreenCollaboration() { return isMultiScreenCollaboration_; @@ -11261,7 +11373,7 @@ void ScreenSessionManager::OnPropertyChange(const ScreenProperty& newProperty, S TLOGNFI(WmsLogTag::DMS, "screenId: %{public}" PRIu64 " reason: %{public}d", screenId, static_cast(reason)); // Update display orientation when boot animation if (IsOnBootAnimation()) { - UpdateDisplayOrientationWhenBootAnimation(screenId); + UpdateDisplayOrientationWhenBootAnimation(screenId, newProperty); } auto clientProxy = GetClientProxy(); if (!clientProxy) { @@ -11276,18 +11388,25 @@ void ScreenSessionManager::OnPropertyChange(const ScreenProperty& newProperty, S clientProxy->OnPropertyChanged(screenId, newProperty, reason); } -void ScreenSessionManager::UpdateDisplayOrientationWhenBootAnimation(ScreenId screenId) +void ScreenSessionManager::UpdateDisplayOrientationWhenBootAnimation(ScreenId screenId, + const ScreenProperty& screenProperty) { sptr screenSession = GetScreenSession(screenId); if (!screenSession) { TLOGNFE(WmsLogTag::DMS, "Get screen session failed, screenId: %{public}" PRIu64, screenId); return; } + auto displayOrientation = screenSession->CalcDisplayOrientation(screenProperty.GetScreenRotation(), + screenProperty.GetDisplayMode()); + auto deviceOrientation = screenSession->CalcDeviceOrientationWithBounds(screenProperty.GetDeviceRotation(), + screenProperty.GetDisplayMode(), screenProperty.GetBounds()); auto currProperty = screenSession->GetScreenProperty(); - currProperty.CalcDefaultDisplayOrientation(); + currProperty.SetDisplayOrientation(displayOrientation); + currProperty.SetDeviceOrientation(deviceOrientation); screenSession->SetScreenProperty(currProperty); - TLOGNFI(WmsLogTag::DMS, "DisplayOrientation is: %{public}d", - screenSession->GetScreenProperty().GetDisplayOrientation()); + TLOGNFI(WmsLogTag::DMS, "DisplayOrientation is: %{public}d, deviceOrientation is: %{public}d", + screenSession->GetScreenProperty().GetDisplayOrientation(), + screenSession->GetScreenProperty().GetDeviceOrientation()); } void ScreenSessionManager::OnFoldPropertyChange(ScreenId screenId, const ScreenProperty& newProperty, @@ -13611,6 +13730,8 @@ void ScreenSessionManager::CreateExtendVirtualScreen(ScreenId screenId) screenId, rsScreenId); } NotifyDisplayCreate(screenSession->ConvertToDisplayInfo()); + OnPropertyChange(screenSession->GetScreenProperty(), ScreenPropertyChangeReason::CHANGE_MODE, + screenId); } DMError ScreenSessionManager::SetMultiScreenRelativePosition(MultiScreenPositionOptions mainScreenOptions, @@ -15000,7 +15121,9 @@ DMError ScreenSessionManager::GetScreenAreaOfDisplayArea(DisplayId displayId, co displayAreaFixed.posY_ += screenRegion.height_ - displayRegion.height_; } displayRegion.height_ = screenRegion.height_; - } else if (FoldScreenStateInternel::IsSecondaryDisplayFoldDevice() && GetFoldDisplayMode() == FoldDisplayMode::FULL) { + } else if ((FoldScreenStateInternel::IsSecondaryDisplayFoldDevice() || + FoldScreenStateInternel::IsSecondaryDisplaySuperFoldDevice()) && + GetFoldDisplayMode() == FoldDisplayMode::FULL) { SetDisplayRegionAndAreaFixed(displayInfo->GetRotation(), displayRegion, displayAreaFixed); } CalculateRotatedDisplay(displayInfo->GetRotation(), screenRegion, displayRegion, displayAreaFixed); @@ -15012,7 +15135,16 @@ DMError ScreenSessionManager::GetScreenAreaOfDisplayArea(DisplayId displayId, co void ScreenSessionManager::SetDisplayRegionAndAreaFixed(Rotation rotation, DMRect& displayRegion, DMRect& displayAreaFixed) { - int32_t offsetX = static_cast(screenParams_[FULL_STATUS_OFFSET_X]); + int32_t offsetX = 0; + auto screenActiveModeRectMap = GetScreenActiveModeRectMap(); + auto screenActiveModeRectIter = screenActiveModeRectMap.find(FoldDisplayMode::FULL); + if (FoldScreenStateInternel::IsSecondaryDisplaySuperFoldDevice() && + screenActiveModeRectIter != screenActiveModeRectMap.end()) { + RRect bounds = screenActiveModeRectIter->second; + offsetX = bounds.rect_.GetTop(); + } else if (screenParams_.size() > FULL_STATUS_OFFSET_X) { + offsetX = static_cast(screenParams_[FULL_STATUS_OFFSET_X]); + } switch (rotation) { case Rotation::ROTATION_0: displayRegion.posX_ = offsetX; @@ -15032,8 +15164,7 @@ void ScreenSessionManager::CalculateRotatedDisplay(Rotation rotation, const DMRe { std::vector phyOffsets = FoldScreenStateInternel::GetPhyRotationOffset(); int32_t phyOffset = 0; - if (phyOffsets.size() > 1 && - (FoldScreenStateInternel::IsSecondaryDisplayFoldDevice() || GetFoldStatus() != FoldStatus::FOLDED)) { + if (phyOffsets.size() > 1 && !FoldScreenStateInternel::IsOuterScreen(GetFoldDisplayMode())) { if (!ScreenSettingHelper::ConvertStrToInt32(phyOffsets[1], phyOffset)) { TLOGNFE(WmsLogTag::DMS, "transfer phyOffset1 failed."); return; @@ -15093,7 +15224,8 @@ void ScreenSessionManager::CalculateRotatedDisplay(Rotation rotation, const DMRe void ScreenSessionManager::CalculateScreenArea(const DMRect& displayRegion, const DMRect& displayArea, const DMRect& screenRegion, DMRect& screenArea) { - if (FoldScreenStateInternel::IsSecondaryDisplayFoldDevice()) { + if (FoldScreenStateInternel::IsSecondaryDisplayFoldDevice() || + FoldScreenStateInternel::IsSecondaryDisplaySuperFoldDevice()) { screenArea = displayArea; return; } @@ -15909,6 +16041,12 @@ void ScreenSessionManager::SetOptionConfig(ScreenId screenId, VirtualScreenOptio } else { screenSession->SetSupportsInput(false); } + + if (option.caller_ != VirtualScreenCaller::NATIVE_SCREEN_MANAGER) { + option.bundleName_ = SysCapUtil::GetBundleName(); + } + TLOGNFI(WmsLogTag::DMS, "The caller : %{public}u, the bundleName of option: %{public}s", + option.caller_, option.bundleName_.c_str()); screenSession->SetBundleName(option.bundleName_); } diff --git a/window_scene/screen_session_manager/src/zidl/screen_session_manager_proxy.cpp b/window_scene/screen_session_manager/src/zidl/screen_session_manager_proxy.cpp index 53039a6c66..30719a11b6 100644 --- a/window_scene/screen_session_manager/src/zidl/screen_session_manager_proxy.cpp +++ b/window_scene/screen_session_manager/src/zidl/screen_session_manager_proxy.cpp @@ -1193,7 +1193,7 @@ ScreenId ScreenSessionManagerProxy::CreateVirtualScreen(VirtualScreenOption virt data.WriteString(virtualOption.serialNumber_) && data.WriteString(virtualOption.bundleName_) && data.WriteInt32(virtualOption.userId_) && data.WriteUint32(virtualOption.phyWidth_) && data.WriteUint32(virtualOption.phyHeight_) && - data.WriteInt32(virtualOption.screenId_); + data.WriteInt32(virtualOption.screenId_) && data.WriteUint32(static_cast(virtualOption.caller_)); if (virtualOption.surface_ != nullptr && virtualOption.surface_->GetProducer() != nullptr) { res = res && data.WriteBool(true) && @@ -3219,6 +3219,33 @@ bool ScreenSessionManagerProxy::IsCaptured() return reply.ReadBool(); } +bool ScreenSessionManagerProxy::IsCapturedByBundleNameList(const std::vector& bundleNameList) +{ + sptr remote = Remote(); + if (remote == nullptr) { + TLOGW(WmsLogTag::DMS, "remote is null"); + return false; + } + + MessageParcel data; + MessageParcel reply; + MessageOption option; + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::DMS, "WriteInterfaceToken failed"); + return false; + } + if (!data.WriteStringVector(bundleNameList)) { + TLOGE(WmsLogTag::DMS, "Write bundleNameList failed"); + return false; + } + if (remote->SendRequest(static_cast(DisplayManagerMessage::TRANS_ID_DEVICE_IS_CAPTURE_BY_BUNDLE_LIST), + data, reply, option) != ERR_NONE) { + TLOGE(WmsLogTag::DMS, "SendRequest failed"); + return false; + } + return reply.ReadBool(); +} + FoldStatus ScreenSessionManagerProxy::GetFoldStatus() { sptr remote = Remote(); diff --git a/window_scene/screen_session_manager/src/zidl/screen_session_manager_stub.cpp b/window_scene/screen_session_manager/src/zidl/screen_session_manager_stub.cpp index 13df3d4142..0149e00a2a 100644 --- a/window_scene/screen_session_manager/src/zidl/screen_session_manager_stub.cpp +++ b/window_scene/screen_session_manager/src/zidl/screen_session_manager_stub.cpp @@ -337,6 +337,7 @@ int32_t ScreenSessionManagerStub::OnRemoteRequestInner(uint32_t code, MessagePar uint32_t phyWidth = data.ReadUint32(); uint32_t phyHeight = data.ReadUint32(); int32_t screenIdParam = data.ReadInt32(); + VirtualScreenCaller caller = static_cast(data.ReadUint32()); bool isSurfaceValid = data.ReadBool(); sptr surface = nullptr; if (isSurfaceValid) { @@ -364,7 +365,8 @@ int32_t ScreenSessionManagerStub::OnRemoteRequestInner(uint32_t code, MessagePar .phyWidth_ = phyWidth, .phyHeight_ = phyHeight, .userId_ = userId, - .screenId_ = screenIdParam + .screenId_ = screenIdParam, + .caller_ = caller }; ScreenId screenId = CreateVirtualScreen(virScrOption, virtualScreenAgent); static_cast(reply.WriteUint64(static_cast(screenId))); @@ -977,7 +979,16 @@ int32_t ScreenSessionManagerStub::OnRemoteRequestInner(uint32_t code, MessagePar reply.WriteBool(IsCaptured()); break; } - //Fold Screen + case DisplayManagerMessage::TRANS_ID_DEVICE_IS_CAPTURE_BY_BUNDLE_LIST: { + std::vector bundleNameList; + if (!data.ReadStringVector(&bundleNameList)) { + TLOGE(WmsLogTag::DMS, "Failed to read bundleNameList"); + return ERR_INVALID_DATA; + } + reply.WriteBool(IsCapturedByBundleNameList(bundleNameList)); + break; + } + // Fold Screen case DisplayManagerMessage::TRANS_ID_SCENE_BOARD_SET_FOLD_DISPLAY_MODE: { FoldDisplayMode displayMode = static_cast(data.ReadUint32()); SetFoldDisplayMode(displayMode); diff --git a/window_scene/screen_session_manager_client/BUILD.gn b/window_scene/screen_session_manager_client/BUILD.gn index 4deaf82c3c..324cc3d0d2 100644 --- a/window_scene/screen_session_manager_client/BUILD.gn +++ b/window_scene/screen_session_manager_client/BUILD.gn @@ -42,6 +42,7 @@ ohos_shared_library("screen_session_manager_client") { "../screen_session_manager/src/zidl/screen_session_manager_proxy.cpp", "src/screen_session_manager_client.cpp", "src/zidl/screen_session_manager_client_stub.cpp", + "../screen_session_manager/src/rs_event_data_manager.cpp", ] public_configs = [ ":screen_session_manager_client_public_config" ] @@ -77,4 +78,5 @@ ohos_shared_library("screen_session_manager_client") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } diff --git a/window_scene/screen_session_manager_client/include/screen_session_manager_client.h b/window_scene/screen_session_manager_client/include/screen_session_manager_client.h index 028867fd85..4052837668 100644 --- a/window_scene/screen_session_manager_client/include/screen_session_manager_client.h +++ b/window_scene/screen_session_manager_client/include/screen_session_manager_client.h @@ -53,6 +53,11 @@ public: virtual void OnTentModeChange(const TentMode tentMode) = 0; }; +class ITransRSEventListener : virtual public RefBase { +public: + virtual void OnTransRSEvent(const sptr& param) = 0; +}; + class ScreenSessionManagerClient : public ScreenSessionManagerClientStub { WM_DECLARE_SINGLE_INSTANCE_BASE(ScreenSessionManagerClient) @@ -145,6 +150,9 @@ public: bool OnFoldPropertyChange(ScreenId screenId, const ScreenProperty& property, ScreenPropertyChangeReason reason, FoldDisplayMode displayMode, ScreenProperty& midProperty) override; void RegisterTentModeChangeListener(ITentModeListener* listener); + void OnTransRSEvent(const sptr& param) override; + void RegisterTransRSEventListener(const RSExposedEventType& type, const sptr& listener); + void UnRegisterTransRSEventListener(const RSExposedEventType& type, const sptr& listener); /* * RS Client Multi Instance @@ -221,6 +229,8 @@ private: std::set animateFinishNotificationSet_; mutable std::shared_mutex animateFinishDescriptionSetMutex_; mutable std::mutex animateFinishNotificationSetMutex_; + std::mutex transToRSEventMutex_; + std::unordered_map>> transRSEventListener_; }; } // namespace OHOS::Rosen diff --git a/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_interface.h b/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_interface.h index 91de53ec31..50840eb825 100644 --- a/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_interface.h +++ b/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_interface.h @@ -22,6 +22,7 @@ #include "display_info.h" #include "session/screen/include/screen_property.h" #include "session_option.h" +#include "rs_event_data_manager.h" namespace OHOS::Rosen { class IScreenSessionManagerClient : public IRemoteBroker { @@ -64,6 +65,7 @@ public: TRANS_ID_ON_FOLD_PROPERTY_CHANGED, TRANS_ID_SET_INTERNAL_CLIPTOBOUNDS, TRANS_ID_ON_TENT_MODE_CHANGE, + TRANS_ID_ON_TRANS_RS_EVENT_TO_DESKTOP, }; virtual void SwitchUserCallback(std::vector oldScbPids, int32_t currentScbPid) = 0; @@ -111,6 +113,7 @@ public: virtual void OnAnimationFinish() = 0; virtual void SetInternalClipToBounds(ScreenId screenId, bool clipToBounds) = 0; virtual void OnTentModeChange(TentMode tentMode) = 0; + virtual void OnTransRSEvent(const sptr& param) = 0; }; } // namespace OHOS::Rosen diff --git a/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_proxy.h b/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_proxy.h index 8a928584f1..c9ad80401c 100644 --- a/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_proxy.h +++ b/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_proxy.h @@ -71,9 +71,11 @@ public: void OnAnimationFinish() override; void SetInternalClipToBounds(ScreenId screenId, bool clipToBounds) override; void OnTentModeChange(TentMode tentMode) override; + void OnTransRSEvent(const sptr& param) override; private: static inline BrokerDelegator delegator_; bool ScreenConnectWriteParam(const SessionOption& SessionOption, ScreenEvent screenEvent, MessageParcel& data); + bool WriteRSEventToParcel(MessageParcel& data, const RSEventDataBase& param); }; } // namespace OHOS::Rosen diff --git a/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_stub.h b/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_stub.h index 2992cea08f..34393996f9 100644 --- a/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_stub.h +++ b/window_scene/screen_session_manager_client/include/zidl/screen_session_manager_client_stub.h @@ -73,6 +73,9 @@ private: int HandleOnAnimationFinish(MessageParcel& data, MessageParcel& reply); int HandleSetInternalClipToBounds(MessageParcel& data, MessageParcel& reply); int HandleTentModeChange(MessageParcel& data, MessageParcel& reply); + int HandleTransRSEvent(MessageParcel& data, MessageParcel& reply); + sptr ReadRSEventFromParcel(MessageParcel& data); + sptr CreateEventByType(const RSExposedEventType& type); HandleScreenChangeMap HandleScreenChangeMap_ {}; }; diff --git a/window_scene/screen_session_manager_client/src/screen_session_manager_client.cpp b/window_scene/screen_session_manager_client/src/screen_session_manager_client.cpp index aeca019bab..7be0122777 100644 --- a/window_scene/screen_session_manager_client/src/screen_session_manager_client.cpp +++ b/window_scene/screen_session_manager_client/src/screen_session_manager_client.cpp @@ -1639,4 +1639,85 @@ bool ScreenSessionManagerClient::GetSupportsFocus(DisplayId displayId) TLOGD(WmsLogTag::DMS, "displayId:%{public}" PRIu64", supportsFocus:%{public}d", displayId, supportsFocus); return supportsFocus; } + +void ScreenSessionManagerClient::RegisterTransRSEventListener( + const RSExposedEventType& type, const sptr& listener) +{ + if (!listener) { + TLOGE(WmsLogTag::DMS, "Failed to register transRSEvent listener, listener is null"); + return; + } + + { + std::lock_guard lock(transToRSEventMutex_); + auto& listeners = transRSEventListener_[type]; + + if (std::find(listeners.begin(), listeners.end(), listener) != listeners.end()) { + TLOGI(WmsLogTag::DMS, "Listener already exists for type:%{public}u", static_cast(type)); + return; + } + + listeners.push_back(listener); + } + ConnectToServer(); + TLOGI(WmsLogTag::DMS, "Success to register transRSEvent listener."); +} + +void ScreenSessionManagerClient::UnRegisterTransRSEventListener( + const RSExposedEventType& type, const sptr& listener) +{ + if (!listener) { + TLOGE(WmsLogTag::DMS, "listener is null"); + return; + } + + std::lock_guard lock(transToRSEventMutex_); + auto it = transRSEventListener_.find(type); + if (it == transRSEventListener_.end()) { + TLOGE(WmsLogTag::DMS, "No listeners for type:%{public}u", static_cast(type)); + return; + } + + auto& vec = it->second; + auto iter = std::find(vec.begin(), vec.end(), listener); + if (iter != vec.end()) { + vec.erase(iter); + TLOGI(WmsLogTag::DMS, "Unregistered listener for type:%{public}u", static_cast(type)); + + if (vec.empty()) { + transRSEventListener_.erase(it); + TLOGI(WmsLogTag::DMS, "Remove empty listener list for type:%{public}u", static_cast(type)); + } + } else { + TLOGE(WmsLogTag::DMS, "Listener not found for type:%{public}u", static_cast(type)); + } +} + +void ScreenSessionManagerClient::OnTransRSEvent(const sptr& data) +{ + if (!data) { + TLOGE(WmsLogTag::DMS, "data is null"); + return; + } + + RSExposedEventType type = data->GetEventType(); + TLOGI(WmsLogTag::DMS, "OnTransRSEvent begin, type:%{public}u", static_cast(type)); + + std::vector> listeners; + { + std::lock_guard lock(transToRSEventMutex_); + auto it = transRSEventListener_.find(type); + if (it == transRSEventListener_.end() || it->second.empty()) { + TLOGW(WmsLogTag::DMS, "No listeners for type:%{public}u", static_cast(type)); + return; + } + listeners = it->second; + } + + for (auto& listener : listeners) { + if (listener) { + listener->OnTransRSEvent(data); + } + } +} } // namespace OHOS::Rosen \ No newline at end of file diff --git a/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_proxy.cpp b/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_proxy.cpp index 34fde45ef3..9a78e63cda 100644 --- a/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_proxy.cpp +++ b/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_proxy.cpp @@ -1176,4 +1176,48 @@ void ScreenSessionManagerClientProxy::SetInternalClipToBounds(ScreenId screenId, return; } } + +void ScreenSessionManagerClientProxy::OnTransRSEvent(const sptr& param) +{ + sptr remote = Remote(); + if (remote == nullptr) { + TLOGE(WmsLogTag::DMS, "remote is nullptr"); + return; + } + + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::DMS, "WriteInterfaceToken failed"); + return; + } + + if (!WriteRSEventToParcel(data, *param)) { + TLOGE(WmsLogTag::DMS, "WriteRSEventToParcel failed"); + return; + } + + if (remote->SendRequest( + static_cast(ScreenSessionManagerClientMessage::TRANS_ID_ON_TRANS_RS_EVENT_TO_DESKTOP), + data, reply, option) != ERR_NONE) { + TLOGE(WmsLogTag::DMS, "SendRequest failed"); + return; + } +} + +bool ScreenSessionManagerClientProxy::WriteRSEventToParcel(MessageParcel& data, const RSEventDataBase& param) +{ + if (!data.WriteUint32(static_cast(param.GetEventType()))) { + TLOGE(WmsLogTag::DMS, "Write event type failed"); + return false; + } + + if (!param.Marshalling(data)) { + TLOGE(WmsLogTag::DMS, "Marshalling failed, type:%{public}u", static_cast(param.GetEventType())); + return false; + } + + return true; +} } // namespace OHOS::Rosen diff --git a/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_stub.cpp b/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_stub.cpp index 34a87a487f..442104118d 100644 --- a/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_stub.cpp +++ b/window_scene/screen_session_manager_client/src/zidl/screen_session_manager_client_stub.cpp @@ -166,6 +166,10 @@ void ScreenSessionManagerClientStub::InitScreenChangeMap() [this](MessageParcel& data, MessageParcel& reply) { return HandleTentModeChange(data, reply); }; + HandleScreenChangeMap_[ScreenSessionManagerClientMessage::TRANS_ID_ON_TRANS_RS_EVENT_TO_DESKTOP] = + [this](MessageParcel& data, MessageParcel& reply) { + return HandleTransRSEvent(data, reply); + }; } ScreenSessionManagerClientStub::ScreenSessionManagerClientStub() @@ -663,4 +667,43 @@ int ScreenSessionManagerClientStub::HandleSetInternalClipToBounds(MessageParcel& SetInternalClipToBounds(mainScreenId, clipToBounds); return ERR_NONE; } + +int ScreenSessionManagerClientStub::HandleTransRSEvent(MessageParcel& data, MessageParcel& reply) +{ + auto eventData = ReadRSEventFromParcel(data); + if (eventData) { + OnTransRSEvent(eventData); + } + return ERR_NONE; +} + +sptr ScreenSessionManagerClientStub::CreateEventByType(const RSExposedEventType& type) +{ + switch (type) { + case RSExposedEventType::EXT_SCREEN_UNSUPPORT: + TLOGI(WmsLogTag::DMS, "Create RSExtScreenUnsupportEventData"); + return (new (std::nothrow) RSExtScreenUnsupportEventData()); + default: + return nullptr; + } +} + +sptr ScreenSessionManagerClientStub::ReadRSEventFromParcel(MessageParcel& data) +{ + uint32_t typeValue = data.ReadUint32(); + RSExposedEventType type = static_cast(typeValue); + + sptr event = CreateEventByType(type); + if (!event) { + TLOGE(WmsLogTag::DMS, "Unknown event type:%{public}u", typeValue); + return nullptr; + } + + if (!event->Unmarshalling(data)) { + TLOGE(WmsLogTag::DMS, "Unmarshalling failed, type:%{public}u", typeValue); + return nullptr; + } + + return event; +} } // namespace OHOS::Rosen diff --git a/window_scene/session/BUILD.gn b/window_scene/session/BUILD.gn index b96a616a98..476e9c5d78 100644 --- a/window_scene/session/BUILD.gn +++ b/window_scene/session/BUILD.gn @@ -98,7 +98,7 @@ ohos_source_set("ui_effect_controller") { deps = [ "${window_base_path}/utils:ui_effect_controller_common", "${window_base_path}/window_scene/common:window_scene_common", - + ] part_name = "window_manager" subsystem_name = "window" @@ -203,7 +203,7 @@ ohos_static_library("scene_session_static") { if (!(host_os == "linux" && host_cpu == "arm64")) { external_deps += [ "preferences:native_preferences" ] } - + defines = [] if (defined(global_parts_info) && @@ -348,6 +348,7 @@ ohos_shared_library("scene_session") { "samgr:samgr_proxy", "zlib:shared_libz", ] + if (!(host_os == "linux" && host_cpu == "arm64")) { external_deps += [ "preferences:native_preferences" ] } @@ -402,6 +403,7 @@ ohos_shared_library("scene_session") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } screen_session_sources = [ @@ -479,6 +481,7 @@ ohos_shared_library("screen_session") { sources = screen_session_sources deps = screen_session_deps external_deps = screen_session_external_deps + ldflags = [ "-Wl,-Bsymbolic-functions" ] defines = [] if (window_manager_feature_screen_color_gamut) { diff --git a/window_scene/session/container/include/zidl/session_stage_interface.h b/window_scene/session/container/include/zidl/session_stage_interface.h index 59e062c8e2..5799471ab1 100644 --- a/window_scene/session/container/include/zidl/session_stage_interface.h +++ b/window_scene/session/container/include/zidl/session_stage_interface.h @@ -177,6 +177,52 @@ public: */ virtual void NotifyGlobalScaledRectChange(const Rect& globalScaledRect) {} + /** + * @brief Update attached window limits for parent-child windows + * + * Update window limits when parent and child windows establish attach relationship. + * Each window receives the other window's limits and decides whether to apply them based on flags. + * + * @param sourcePersistentId the persistentId of the window providing the limits + * @param attachedWindowLimits the other window's limits (parent gets sub's, sub gets parent's) + * @param isIntersectedHeightLimit whether to limit height with attached window's limits + * @param isIntersectedWidthLimit whether to limit width with attached window's limits + * @return Returns WSError::WS_OK if called success, otherwise failed. + */ + virtual WSError UpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, bool isIntersectedWidthLimit) + { + return WSError::WS_OK; + } + + /** + * Remove attached window limits from a specific source window. + * Called when a window detaches or is destroyed. + * + * @param sourcePersistentId the persistentId of the source window whose limits should be removed + * @return Returns WSError::WS_OK if called success, otherwise failed. + */ + virtual WSError RemoveAttachedWindowLimits(int32_t sourcePersistentId) + { + return WSError::WS_OK; + } + + /** + * Sync parent's full limits list to attaching child window. + * Called when a sub-window first attaches, to deliver the main window's complete + * attached limits info (main window's own limits + all other attached windows' limits). + * + * @param limitsList vector of (sourcePersistentId, WindowLimits) pairs, main window first + * @param optionsList vector of (sourcePersistentId, AttachLimitOptions) pairs, main window first + * @return Returns WSError::WS_OK if called success, otherwise failed. + */ + virtual WSError SyncAllAttachedLimitsToChild( + const std::vector>& limitsList, + const std::vector>& optionsList) + { + return WSError::WS_OK; + } + /** * @brief Set pip event to client. * @@ -308,10 +354,8 @@ public: return { RectType::RELATIVE_TO_SCREEN, { 0, 0, 0, 0, } }; } virtual WSError NotifyAppForceLandscapeConfigUpdated() = 0; - virtual WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) = 0; - virtual WSError NotifyAppHookWindowInfoUpdated() = 0; virtual WSError UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) = 0; + virtual WSError SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) = 0; virtual WSError CloseSpecificScene() { return WSError::WS_DO_NOTHING; } virtual WSError UpdateBrightness(float brightness) = 0; diff --git a/window_scene/session/container/include/zidl/session_stage_ipc_interface_code.h b/window_scene/session/container/include/zidl/session_stage_ipc_interface_code.h index 3a83ba3b94..a532ffcb6f 100644 --- a/window_scene/session/container/include/zidl/session_stage_ipc_interface_code.h +++ b/window_scene/session/container/include/zidl/session_stage_ipc_interface_code.h @@ -99,15 +99,18 @@ enum class SessionStageInterfaceCode { TRANS_ID_NOTIFY_CROSS_AXIS, TRANS_ID_NOTIFY_WINDOW_ATTACH_STATE_CHANGE, TRANS_ID_NOTIFY_APP_FORCE_LANDSCAPE_CONFIG_UPDATED, - TRANS_ID_NOTIFY_APP_HOOK_WINDOW_INFO_UPDATED, TRANS_ID_CLOSE_SPECIFIC_SCENE, - TRANS_ID_NOTIFY_APP_FORCE_LANDSCAPE_ENABLE_UPDATED, TRANS_ID_GET_SCREEN_NODE_COUNT, TRANS_ID_GET_SCENE_NODE_COUNT_WITH_CALLBACK, TRANS_ID_NOTIFY_ORIENTATION_EXECUTION_RESULT, + // Layout TRANS_ID_UPDATE_WINDOW_MODE_FOR_UI_TEST, TRANS_ID_UPDATE_GLOBAL_DISPLAY_RECT, + TRANS_ID_UPDATE_ATTACHED_WINDOW_LIMITS, + TRANS_ID_REMOVE_ATTACHED_WINDOW_LIMITS, + TRANS_ID_SYNC_ALL_ATTACHED_LIMITS_TO_CHILD, + // Floating ball TRANS_ID_SEND_FB_ACTION_EVENT, TRANS_ID_NOTIFY_UPDATE_SHOW_DECOR_IN_FREE_MULTI_WINDOW, @@ -125,6 +128,7 @@ enum class SessionStageInterfaceCode { TRANS_ID_UPDATE_PROPERTY_WHEN_TRIGGER_MODE, TRANS_ID_NOTIFY_PARENT_LIFECYCLE_EVENT, TRANS_ID_UPDATE_APP_HOOK_WINDOW_INFO, + TRANS_ID_SET_FORCE_SPLIT_ENABLE, // Float view TRANS_ID_SEND_FV_ACTION_EVENT, TRANS_ID_SYNC_FV_WINDOW_INFO, diff --git a/window_scene/session/container/include/zidl/session_stage_proxy.h b/window_scene/session/container/include/zidl/session_stage_proxy.h index 30d1aa584f..9179a25c41 100644 --- a/window_scene/session/container/include/zidl/session_stage_proxy.h +++ b/window_scene/session/container/include/zidl/session_stage_proxy.h @@ -73,6 +73,13 @@ public: void NotifyTransformChange(const Transform& transform) override; void NotifySingleHandTransformChange(const SingleHandTransform& singleHandTransform) override; void NotifyGlobalScaledRectChange(const Rect& globalScaledRect) override; + WSError UpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, + bool isIntersectedWidthLimit) override; + WSError RemoveAttachedWindowLimits(int32_t sourcePersistentId) override; + WSError SyncAllAttachedLimitsToChild( + const std::vector>& limitsList, + const std::vector>& optionsList) override; WSError NotifyDialogStateChange(bool isForeground) override; WSError SetPipActionEvent(const std::string& action, int32_t status) override; WSError SetPiPControlEvent(WsPiPControlType controlType, WsPiPControlStatus status) override; @@ -121,10 +128,8 @@ public: WSError GetSceneNodeCount(const sptr& callback) override; WSError NotifyOrientationExecutionResult(uint32_t promiseId, OrientationExecutionResult result) override; WSError NotifyAppForceLandscapeConfigUpdated() override; - WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) override; - WSError NotifyAppHookWindowInfoUpdated() override; WSError UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) override; + WSError SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) override; WSError CloseSpecificScene() override; void NotifyLifecyclePausedStatus() override; void NotifyAppUseControlStatus(bool isUseControl) override; diff --git a/window_scene/session/container/include/zidl/session_stage_stub.h b/window_scene/session/container/include/zidl/session_stage_stub.h index 3bb691b11f..28a0ecb310 100644 --- a/window_scene/session/container/include/zidl/session_stage_stub.h +++ b/window_scene/session/container/include/zidl/session_stage_stub.h @@ -73,6 +73,9 @@ private: int HandleNotifyTransformChange(MessageParcel& data, MessageParcel& reply); int HandleNotifySingleHandTransformChange(MessageParcel& data, MessageParcel& reply); int HandleNotifyGlobalScaledRectChange(MessageParcel& data, MessageParcel& reply); + int HandleUpdateAttachedWindowLimits(MessageParcel& data, MessageParcel& reply); + int HandleRemoveAttachedWindowLimits(MessageParcel& data, MessageParcel& reply); + int HandleSyncAllAttachedLimitsToChild(MessageParcel& data, MessageParcel& reply); int HandleNotifyDialogStateChange(MessageParcel& data, MessageParcel& reply); int HandleSetPipActionEvent(MessageParcel& data, MessageParcel& reply); int HandleSetPiPControlEvent(MessageParcel& data, MessageParcel& reply); @@ -111,8 +114,8 @@ private: int HandleNotifyRotationChange(MessageParcel& data, MessageParcel& reply); int HandleNotifyAppForceLandscapeConfigUpdated(MessageParcel& data, MessageParcel& reply); int HandleNotifyAppForceLandscapeConfigEnableUpdated(MessageParcel& data, MessageParcel& reply); - int HandleNotifyAppHookWindowInfoUpdated(MessageParcel& data, MessageParcel& reply); int HandleUpdateAppHookWindowInfo(MessageParcel& data, MessageParcel& reply); + int HandleSetForceSplitEnable(MessageParcel& data, MessageParcel& reply); int HandleGetRouterStackInfo(MessageParcel& data, MessageParcel& reply); int HandleGetSceneNodeCount(MessageParcel& data, MessageParcel& reply); int HandleGetSceneNodeCountWithCallback(MessageParcel& data, MessageParcel& reply); diff --git a/window_scene/session/container/src/zidl/session_stage_proxy.cpp b/window_scene/session/container/src/zidl/session_stage_proxy.cpp index f567d7e645..2259007c80 100644 --- a/window_scene/session/container/src/zidl/session_stage_proxy.cpp +++ b/window_scene/session/container/src/zidl/session_stage_proxy.cpp @@ -2613,6 +2613,122 @@ RotationChangeResult SessionStageProxy::NotifyRotationChange(const RotationChang return rotationChangeResult; } +/** @note @window.layout */ +WSError SessionStageProxy::UpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, bool isIntersectedWidthLimit) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "Called, sourcePersistentId=%{public}d", sourcePersistentId); + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::WMS_LAYOUT, "WriteInterfaceToken failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteInt32(sourcePersistentId)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write sourcePersistentId failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!attachedWindowLimits.Marshalling(data)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write attachedWindowLimits failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteBool(isIntersectedHeightLimit)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write isIntersectedHeightLimit failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteBool(isIntersectedWidthLimit)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write isIntersectedWidthLimit failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + sptr remote = Remote(); + if (remote == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "remote is null"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (remote->SendRequest(static_cast(SessionStageInterfaceCode::TRANS_ID_UPDATE_ATTACHED_WINDOW_LIMITS), + data, reply, option) != ERR_NONE) { + TLOGE(WmsLogTag::WMS_LAYOUT, "SendRequest failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + return WSError::WS_OK; +} + +/** @note @window.layout */ +WSError SessionStageProxy::RemoveAttachedWindowLimits(int32_t sourcePersistentId) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "Called, sourcePersistentId=%{public}d", sourcePersistentId); + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::WMS_LAYOUT, "WriteInterfaceToken failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteInt32(sourcePersistentId)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write sourcePersistentId failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + sptr remote = Remote(); + if (remote == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "remote is null"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (remote->SendRequest(static_cast(SessionStageInterfaceCode::TRANS_ID_REMOVE_ATTACHED_WINDOW_LIMITS), + data, reply, option) != ERR_NONE) { + TLOGE(WmsLogTag::WMS_LAYOUT, "SendRequest failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + return WSError::WS_OK; +} + +/** @note @window.layout */ +WSError SessionStageProxy::SyncAllAttachedLimitsToChild( + const std::vector>& limitsList, + const std::vector>& optionsList) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "Called, limitsCount=%{public}zu", limitsList.size()); + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::WMS_LAYOUT, "WriteInterfaceToken failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteUint32(static_cast(limitsList.size()))) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write limitsList size failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + for (const auto& [sourceId, limits] : limitsList) { + if (!data.WriteInt32(sourceId) || !limits.Marshalling(data)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write limits entry failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + } + if (!data.WriteUint32(static_cast(optionsList.size()))) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write optionsList size failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + for (const auto& [sourceId, opts] : optionsList) { + if (!data.WriteInt32(sourceId) || !data.WriteBool(opts.isIntersectedHeightLimit) || + !data.WriteBool(opts.isIntersectedWidthLimit)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write options entry failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + } + sptr remote = Remote(); + if (remote == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "remote is null"); + return WSError::WS_ERROR_IPC_FAILED; + } + auto code = SessionStageInterfaceCode::TRANS_ID_SYNC_ALL_ATTACHED_LIMITS_TO_CHILD; + if (remote->SendRequest(static_cast(code), data, reply, option) != ERR_NONE) { + TLOGE(WmsLogTag::WMS_LAYOUT, "SendRequest failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + return WSError::WS_OK; +} + WSError SessionStageProxy::NotifyAppForceLandscapeConfigUpdated() { MessageParcel data; @@ -2638,64 +2754,6 @@ WSError SessionStageProxy::NotifyAppForceLandscapeConfigUpdated() return WSError::WS_OK; } -WSError SessionStageProxy::NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, SelectMode selectMode) -{ - MessageParcel data; - MessageParcel reply; - MessageOption option(MessageOption::TF_ASYNC); - if (!data.WriteInterfaceToken(GetDescriptor())) { - TLOGE(WmsLogTag::WMS_COMPAT, "WriteInterfaceToken failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - if (!data.WriteBool(needUpdateViewport)) { - TLOGE(WmsLogTag::WMS_COMPAT, "Write needUpdateViewport failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - if (!data.WriteUint32(static_cast(selectMode))) { - TLOGE(WmsLogTag::WMS_COMPAT, "Write selectMode failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - - sptr remote = Remote(); - if (remote == nullptr) { - TLOGE(WmsLogTag::WMS_COMPAT, "remote is null"); - return WSError::WS_ERROR_IPC_FAILED; - } - - if (remote->SendRequest( - static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_APP_FORCE_LANDSCAPE_ENABLE_UPDATED), - data, reply, option) != ERR_NONE) { - TLOGE(WmsLogTag::WMS_COMPAT, "SendRequest failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - return WSError::WS_OK; -} - -WSError SessionStageProxy::NotifyAppHookWindowInfoUpdated() -{ - MessageParcel data; - MessageParcel reply; - MessageOption option(MessageOption::TF_ASYNC); - if (!data.WriteInterfaceToken(GetDescriptor())) { - TLOGE(WmsLogTag::WMS_LAYOUT, "WriteInterfaceToken failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - - sptr remote = Remote(); - if (remote == nullptr) { - TLOGE(WmsLogTag::WMS_LAYOUT, "remote is null"); - return WSError::WS_ERROR_IPC_FAILED; - } - - if (remote->SendRequest( - static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_APP_HOOK_WINDOW_INFO_UPDATED), - data, reply, option) != ERR_NONE) { - TLOGE(WmsLogTag::WMS_LAYOUT, "SendRequest failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - return WSError::WS_OK; -} - WSError SessionStageProxy::UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) { MessageParcel data; @@ -2723,6 +2781,42 @@ WSError SessionStageProxy::UpdateAppHookWindowInfo(const HookWindowInfo& hookWin return WSError::WS_OK; } +WSError SessionStageProxy::SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::WMS_COMPAT, "WriteInterfaceToken failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteBool(isForceSplitEnabled)) { + TLOGE(WmsLogTag::WMS_COMPAT, "Write isForceSplitEnabled failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteBool(needUpdateViewport)) { + TLOGE(WmsLogTag::WMS_COMPAT, "Write needUpdateViewport failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!data.WriteUint32(static_cast(selectMode))) { + TLOGE(WmsLogTag::WMS_COMPAT, "Write selectMode failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + + sptr remote = Remote(); + if (remote == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "remote is null"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (remote->SendRequest( + static_cast(SessionStageInterfaceCode::TRANS_ID_SET_FORCE_SPLIT_ENABLE), + data, reply, option) != ERR_NONE) { + TLOGE(WmsLogTag::WMS_COMPAT, "SendRequest failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + return WSError::WS_OK; +} + void SessionStageProxy::NotifyKeyboardAnimationWillBegin(const KeyboardAnimationInfo& keyboardAnimationInfo, const std::shared_ptr& rsTransaction) { diff --git a/window_scene/session/container/src/zidl/session_stage_stub.cpp b/window_scene/session/container/src/zidl/session_stage_stub.cpp index 0f7d5f1c16..6249e7f2a9 100644 --- a/window_scene/session/container/src/zidl/session_stage_stub.cpp +++ b/window_scene/session/container/src/zidl/session_stage_stub.cpp @@ -166,6 +166,12 @@ int SessionStageStub::OnRemoteRequest(uint32_t code, MessageParcel& data, Messag return HandleNotifySingleHandTransformChange(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_GLOBAL_SCALED_RECT): return HandleNotifyGlobalScaledRectChange(data, reply); + case static_cast(SessionStageInterfaceCode::TRANS_ID_UPDATE_ATTACHED_WINDOW_LIMITS): + return HandleUpdateAttachedWindowLimits(data, reply); + case static_cast(SessionStageInterfaceCode::TRANS_ID_REMOVE_ATTACHED_WINDOW_LIMITS): + return HandleRemoveAttachedWindowLimits(data, reply); + case static_cast(SessionStageInterfaceCode::TRANS_ID_SYNC_ALL_ATTACHED_LIMITS_TO_CHILD): + return HandleSyncAllAttachedLimitsToChild(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_DIALOG_STATE_CHANGE): return HandleNotifyDialogStateChange(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_SET_PIP_ACTION_EVENT): @@ -238,10 +244,6 @@ int SessionStageStub::OnRemoteRequest(uint32_t code, MessageParcel& data, Messag return HandleNotifyRotationChange(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_APP_FORCE_LANDSCAPE_CONFIG_UPDATED): return HandleNotifyAppForceLandscapeConfigUpdated(data, reply); - case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_APP_FORCE_LANDSCAPE_ENABLE_UPDATED): - return HandleNotifyAppForceLandscapeConfigEnableUpdated(data, reply); - case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_APP_HOOK_WINDOW_INFO_UPDATED): - return HandleNotifyAppHookWindowInfoUpdated(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_PAUSED_STATUS): return HandleNotifyPausedStatus(); case static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_USE_CONTROL_STATUS): @@ -280,6 +282,8 @@ int SessionStageStub::OnRemoteRequest(uint32_t code, MessageParcel& data, Messag return HandleNotifyParentLifecycleEvent(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_UPDATE_APP_HOOK_WINDOW_INFO): return HandleUpdateAppHookWindowInfo(data, reply); + case static_cast(SessionStageInterfaceCode::TRANS_ID_SET_FORCE_SPLIT_ENABLE): + return HandleSetForceSplitEnable(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_SEND_FV_ACTION_EVENT): return HandleSendFvActionEvent(data, reply); case static_cast(SessionStageInterfaceCode::TRANS_ID_SYNC_FV_WINDOW_INFO): @@ -866,6 +870,105 @@ int SessionStageStub::HandleNotifyGlobalScaledRectChange(MessageParcel& data, Me return ERR_NONE; } +/** @note @window.layout */ +int SessionStageStub::HandleUpdateAttachedWindowLimits(MessageParcel& data, MessageParcel& reply) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "Called"); + int32_t sourcePersistentId; + if (!data.ReadInt32(sourcePersistentId)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read sourcePersistentId failed"); + return ERR_INVALID_DATA; + } + auto attachedWindowLimits = std::unique_ptr(WindowLimits::Unmarshalling(data)); + if (attachedWindowLimits == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read attachedWindowLimits failed"); + return ERR_INVALID_DATA; + } + bool isIntersectedHeightLimit = false; + if (!data.ReadBool(isIntersectedHeightLimit)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read isIntersectedHeightLimit failed"); + return ERR_INVALID_DATA; + } + bool isIntersectedWidthLimit = false; + if (!data.ReadBool(isIntersectedWidthLimit)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read isIntersectedWidthLimit failed"); + return ERR_INVALID_DATA; + } + WSError ret = UpdateAttachedWindowLimits(sourcePersistentId, *attachedWindowLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "UpdateAttachedWindowLimits failed, ret: %{public}d", ret); + return static_cast(ret); + } + return ERR_NONE; +} + +/** @note @window.layout */ +int SessionStageStub::HandleRemoveAttachedWindowLimits(MessageParcel& data, MessageParcel& reply) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "Called"); + int32_t sourcePersistentId; + if (!data.ReadInt32(sourcePersistentId)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read sourcePersistentId failed"); + return ERR_INVALID_DATA; + } + WSError ret = RemoveAttachedWindowLimits(sourcePersistentId); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "RemoveAttachedWindowLimits failed, ret: %{public}d", ret); + return static_cast(ret); + } + return ERR_NONE; +} + +/** @note @window.layout */ +int SessionStageStub::HandleSyncAllAttachedLimitsToChild(MessageParcel& data, MessageParcel& reply) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "Called"); + uint32_t limitsCount = 0; + if (!data.ReadUint32(limitsCount)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read limitsList size failed"); + return ERR_INVALID_DATA; + } + std::vector> limitsList; + limitsList.reserve(limitsCount); + for (uint32_t i = 0; i < limitsCount; ++i) { + int32_t sourceId = 0; + if (!data.ReadInt32(sourceId)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read sourceId failed at index %{public}u", i); + return ERR_INVALID_DATA; + } + auto limits = std::unique_ptr(WindowLimits::Unmarshalling(data)); + if (limits == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read WindowLimits failed at index %{public}u", i); + return ERR_INVALID_DATA; + } + limitsList.emplace_back(sourceId, *limits); + } + uint32_t optionsCount = 0; + if (!data.ReadUint32(optionsCount)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read optionsList size failed"); + return ERR_INVALID_DATA; + } + std::vector> optionsList; + optionsList.reserve(optionsCount); + for (uint32_t i = 0; i < optionsCount; ++i) { + int32_t sourceId = 0; + bool heightLimit = false; + bool widthLimit = false; + if (!data.ReadInt32(sourceId) || !data.ReadBool(heightLimit) || !data.ReadBool(widthLimit)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "read options entry failed at index %{public}u", i); + return ERR_INVALID_DATA; + } + optionsList.emplace_back(sourceId, AttachLimitOptions{heightLimit, widthLimit}); + } + WSError ret = SyncAllAttachedLimitsToChild(limitsList, optionsList); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "SyncAllAttachedLimitsToChild failed, ret: %{public}d", ret); + return static_cast(ret); + } + return ERR_NONE; +} + int SessionStageStub::HandleNotifyDensityFollowHost(MessageParcel& data, MessageParcel& reply) { TLOGD(WmsLogTag::WMS_UIEXT, "HandleNotifyDensityFollowHost"); @@ -1529,9 +1632,26 @@ int SessionStageStub::HandleNotifyAppForceLandscapeConfigUpdated(MessageParcel& return ERR_NONE; } -int SessionStageStub::HandleNotifyAppForceLandscapeConfigEnableUpdated(MessageParcel& data, MessageParcel& reply) +int SessionStageStub::HandleUpdateAppHookWindowInfo(MessageParcel& data, MessageParcel& reply) { TLOGD(WmsLogTag::WMS_COMPAT, "in"); + sptr hookInfo = data.ReadParcelable(); + if (hookInfo == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "hookInfo is nullptr!"); + return ERR_INVALID_DATA; + } + UpdateAppHookWindowInfo(*hookInfo); + return ERR_NONE; +} + +int SessionStageStub::HandleSetForceSplitEnable(MessageParcel& data, MessageParcel& reply) +{ + TLOGD(WmsLogTag::WMS_COMPAT, "in"); + bool isForceSplitEnabled = false; + if (!data.ReadBool(isForceSplitEnabled)) { + TLOGE(WmsLogTag::WMS_COMPAT, "read isForceSplitEnabled failed"); + return ERR_INVALID_DATA; + } bool needUpdateViewport = false; if (!data.ReadBool(needUpdateViewport)) { TLOGE(WmsLogTag::WMS_COMPAT, "read needUpdateViewport failed"); @@ -1542,25 +1662,7 @@ int SessionStageStub::HandleNotifyAppForceLandscapeConfigEnableUpdated(MessagePa TLOGE(WmsLogTag::WMS_COMPAT, "read selectMode failed"); return ERR_INVALID_DATA; } - NotifyAppForceLandscapeConfigEnableUpdated(needUpdateViewport, static_cast(selectModeValue)); - return ERR_NONE; -} -int SessionStageStub::HandleNotifyAppHookWindowInfoUpdated(MessageParcel& data, MessageParcel& reply) -{ - TLOGD(WmsLogTag::WMS_LAYOUT, "in"); - NotifyAppHookWindowInfoUpdated(); - return ERR_NONE; -} - -int SessionStageStub::HandleUpdateAppHookWindowInfo(MessageParcel& data, MessageParcel& reply) -{ - TLOGD(WmsLogTag::WMS_COMPAT, "in"); - sptr hookInfo = data.ReadParcelable(); - if (hookInfo == nullptr) { - TLOGE(WmsLogTag::WMS_COMPAT, "hookInfo is nullptr!"); - return ERR_INVALID_DATA; - } - UpdateAppHookWindowInfo(*hookInfo); + SetForceSplitEnable(isForceSplitEnabled, needUpdateViewport, static_cast(selectModeValue)); return ERR_NONE; } diff --git a/window_scene/session/host/include/main_session.h b/window_scene/session/host/include/main_session.h index 37692087c9..4df465516a 100644 --- a/window_scene/session/host/include/main_session.h +++ b/window_scene/session/host/include/main_session.h @@ -33,9 +33,6 @@ public: void NotifyForegroundInteractiveStatus(bool interactive) override; WSError TransferKeyEvent(const std::shared_ptr& keyEvent) override; void RectCheck(float curWidth, float curHeight, const ScreenMetrics& screenMetrics) override; - WMError GetAppForceLandscapeConfigEnable(bool& enableForceSplit) override; - WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) override; /* * Window Hierarchy @@ -89,10 +86,12 @@ public: bool IsFullScreenInForceSplit() override; void RegisterCompatibleModeChangeCallback(CompatibleModeChangeCallback&& callback) override; WSError NotifyCompatibleModeChange(CompatibleStyleMode mode) override; - void RegisterForceSplitEnableListener(NotifyForceSplitEnableFunc&& func) override; void RegisterPageEnableCallback(PageEnableCallback&& callback) override; + void RegisterSetSelectModeCallback(SetSelectModeCallback&& callback) override; WSError NotifyPageEnable(const std::string& action, const std::string& message) override; WSError UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) override; + WSError UpdateHookWindowInfo(const HookWindowInfo& hookWindowInfo) override; + WSError SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) override; WMError NotifySplitRatioChanged(float newRatio) override; /* @@ -108,6 +107,39 @@ public: void SetPrelaunch() override; bool IsPrelaunch() const override; + /* + * Window Layout + */ + /** + * @brief Main window implementation: update own limits and propagate to all children + * + * Main window updates its own attached window limits and then requests all attached + * child windows to update their limits as well. This ensures limits are propagated + * throughout the window hierarchy. + * + * @param sourcePersistentId the persistentId of the window providing the limits + * @param attachedWindowLimits the other window's limits + * @param isIntersectedHeightLimit whether to limit height with attached window's limits + * @param isIntersectedWidthLimit whether to limit width with attached window's limits + * @return Returns WSError::WS_OK if success, otherwise failed. + */ + WSError RequestUpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit = true, + bool isIntersectedWidthLimit = true, int32_t excludePersistentId = INVALID_SESSION_ID) override; + + /** + * @brief Main window implementation: remove own limits and propagate to all children + * + * Main window removes its own attached window limits and then requests all attached + * child windows to remove their limits as well. + * + * @param sourcePersistentId the persistentId of the source window whose limits should be removed + * @param excludePersistentId the persistentId of child window to exclude from notification + * @return Returns WSError::WS_OK if success, otherwise failed. + */ + WSError RequestRemoveAttachedWindowLimits(int32_t sourcePersistentId, + int32_t excludePersistentId = INVALID_SESSION_ID) override; + protected: void UpdatePointerArea(const WSRect& rect) override; bool CheckPointerEventDispatch(const std::shared_ptr& pointerEvent) const override; @@ -141,8 +173,8 @@ private: ForceSplitFullScreenChangeCallback forceSplitFullScreenChangeCallback_; std::atomic_bool isFullScreenInForceSplit_ { false }; CompatibleModeChangeCallback compatibleModeChangeCallback_; - NotifyForceSplitEnableFunc forceSplitEnableFunc_; PageEnableCallback pageEnableCallback_; + SetSelectModeCallback setSelectModeCallback_; /* * Prelaunch check diff --git a/window_scene/session/host/include/scene_session.h b/window_scene/session/host/include/scene_session.h index b7692dfea6..c48b0337e6 100644 --- a/window_scene/session/host/include/scene_session.h +++ b/window_scene/session/host/include/scene_session.h @@ -112,7 +112,7 @@ using NotifyForceSplitFunc = std::function; using PageEnableCallback = std::function; -using GetHookWindowInfoFunc = std::function; +using SetSelectModeCallback = std::function; using GetSelectModeFunc = std::function; using UpdatePrivateStateAndNotifyFunc = std::function; using UpdateScreenshotAppEventRegisteredFunc = std::function; @@ -489,8 +489,8 @@ public: virtual void RegisterForceSplitFullScreenChangeCallback(ForceSplitFullScreenChangeCallback&& callback) {} virtual bool IsFullScreenInForceSplit() { return false; } virtual void RegisterCompatibleModeChangeCallback(CompatibleModeChangeCallback&& callback) {} - virtual void RegisterForceSplitEnableListener(NotifyForceSplitEnableFunc&& func) {} virtual void RegisterPageEnableCallback(PageEnableCallback&& callback) {} + virtual void RegisterSetSelectModeCallback(SetSelectModeCallback&& callback) {} /* * PC Window @@ -980,13 +980,16 @@ public: WSError SetWindowAnchorInfo(const WindowAnchorInfo& windowAnchorInfo) override; WindowAnchorInfo GetWindowAnchorInfo() const { return windowAnchorInfo_; } void CalcSubWindowRectByAnchor(const WSRect& parentRect, WSRect& subRect); + void NotifyRelatedWindowsAttachStateChange(const sptr& parentSession, + bool wasAttached, bool isAttached, bool oldIsIntersectedWidthLimit, bool oldIsIntersectedHeightLimit); + void SyncAllAttachedLimitsToAttachingChild(const sptr& parentSession); + WSError NotifyAttachedWindowsLimitsChanged(const WindowLimits& newLimits) override; + void NotifyRelatedWindowsOnDestruction(); bool IsAnyParentSessionDragMoving() const override; bool IsAnyParentSessionDragZooming() const override; bool IsNeedNotifyDragEventOnNextVsync() const; void NotifiedDragEventOnNextVsync(); - void RegisterAppHookWindowInfoFunc(GetHookWindowInfoFunc&& func); - WMError GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) override; void RegisterSelectModeFunc(GetSelectModeFunc&& func); WMError GetSelectMode(SelectMode& selectMode) override; void SetFindScenePanelRsNodeByZOrderFunc(FindScenePanelRsNodeByZOrderFunc&& func); @@ -1242,6 +1245,7 @@ protected: void SetShouldFollowParentWhenShow(bool shouldFollow) { shouldFollowParentWhenShow_ = shouldFollow; } bool GetShouldFollowParentWhenShow() const { return shouldFollowParentWhenShow_; } void CheckSubSessionShouldFollowParent(uint64_t displayId); + bool ShouldNotifyAttachedWindow(const sptr& subSession) const; bool IsNeedConvertToRelativeRect(SizeChangeReason reason = SizeChangeReason::UNDEFINED) const override; void SetRequestMoveConfiguration(const MoveConfiguration& config) { requestMoveConfiguration_ = config; } MoveConfiguration GetRequestMoveConfiguration() const { return requestMoveConfiguration_; } @@ -1551,7 +1555,6 @@ private: /* * Window Layout */ - GetHookWindowInfoFunc getHookWindowInfoFunc_ = nullptr; GetSelectModeFunc getSelectModeFunc_ = nullptr; bool SaveAspectRatio(float ratio); WSError UpdateRectForDrag(const WSRect& rect); @@ -1568,6 +1571,8 @@ private: WindowLimits GetWindowLimits() const; bool ShouldSkipUpdateRect(const WSRect& rect); bool ShouldSkipUpdateRectNotify(const WSRect& rect); + bool ShouldProcessAttachStateChange(bool wasAttached, bool isAttached, + bool oldIsIntersectedWidthLimit, bool oldIsIntersectedHeightLimit, bool& isDetaching); /** * @brief Set surface bounds via the original surface node. @@ -1748,6 +1753,9 @@ private: bool isAncoForFloatingWindow_ = false; bool subWindowOutlineEnabled_ = false; std::atomic_bool isRegisterAcrossDisplaysChanged_ = false; + void OnSurfaceNodeChanged() override; + void UpdateSurfaceDarkMode(); + bool GetDarkMode() const; std::string colorMode_; bool hasDarkRes_ = false; mutable std::mutex colorModeMutex_; diff --git a/window_scene/session/host/include/session.h b/window_scene/session/host/include/session.h index 4b78ac3f92..5045c37d24 100644 --- a/window_scene/session/host/include/session.h +++ b/window_scene/session/host/include/session.h @@ -594,6 +594,8 @@ public: WSError SetPcAppInpadSpecificSystemBarInvisible(bool isPcAppInpadSpecificSystemBarInvisible); WSError SetPcAppInpadOrientationLandscape(bool isPcAppInpadOrientationLandscape); WSError SetMobileAppInPadLayoutFullScreen(bool isMobileAppInPadLayoutFullScreen); + virtual WSError SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) + { return WSError::WS_OK; } bool NeedNotify() const; void SetNeedNotify(bool needNotify); WSError SetTouchable(bool touchable); @@ -847,6 +849,17 @@ public: bool SessionIsSingleHandMode(); void SetClientDisplayId(DisplayId displayId); DisplayId GetClientDisplayId() const; + virtual WSError RequestUpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit = true, + bool isIntersectedWidthLimit = true, int32_t excludePersistentId = INVALID_SESSION_ID) + { + return WSError::WS_OK; + } + virtual WSError RequestRemoveAttachedWindowLimits(int32_t sourcePersistentId, + int32_t excludePersistentId = INVALID_SESSION_ID) + { + return WSError::WS_OK; + } virtual void RegisterNotifySurfaceBoundsChangeFunc(int32_t sessionId, NotifySurfaceBoundsChangeFunc&& func) {}; virtual void UnregisterNotifySurfaceBoundsChangeFunc(int32_t sessionId) {}; virtual bool IsAnyParentSessionDragMoving() const { return false; } @@ -858,8 +871,8 @@ public: virtual WSError UpdateGlobalDisplayRect(const WSRect& rect, SizeChangeReason reason); WSError NotifyClientToUpdateGlobalDisplayRect(const WSRect& rect, SizeChangeReason reason); const sptr& GetLayoutController() const { return layoutController_; } - WSError NotifyAppHookWindowInfoUpdated(); virtual WSError UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) { return WSError::WS_OK; } + virtual WSError UpdateHookWindowInfo(const HookWindowInfo& hookWindowInfo) { return WSError::WS_OK; } void NotifyWindowStatusDidChangeIfNeedWhenUpdateRect(SizeChangeReason reason); void SetGetRsCmdBlockingCountFunc(const GetRsCmdBlockingCountFunc& func); WSError UpdateClientRectInfo(const WSRect& rect, SizeChangeReason reason, @@ -1031,6 +1044,7 @@ protected: */ std::shared_ptr GetRSShadowContext(); std::shared_ptr GetRSLeashWinShadowContext(); + virtual void OnSurfaceNodeChanged() {} static std::shared_ptr mainHandler_; int32_t persistentId_ = INVALID_SESSION_ID; @@ -1429,4 +1443,4 @@ private: bool isLayerPartRender_ = false; }; } // namespace OHOS::Rosen -#endif // OHOS_ROSEN_WINDOW_SCENE_SESSION_H \ No newline at end of file +#endif // OHOS_ROSEN_WINDOW_SCENE_SESSION_H diff --git a/window_scene/session/host/include/sub_session.h b/window_scene/session/host/include/sub_session.h index bde8455416..d5672d653a 100644 --- a/window_scene/session/host/include/sub_session.h +++ b/window_scene/session/host/include/sub_session.h @@ -44,6 +44,38 @@ public: void SetParentSessionCallback(NotifySetParentSessionFunc&& func) override; WMError NotifySetParentSession(int32_t oldParentWindowId, int32_t newParentWindowId) override; + /* + * Window Layout + */ + /** + * @brief Sub window implementation: update own limits only + * + * Sub window only updates its own attached window limits. Does not propagate + * to other windows as the parent main window handles propagation. + * + * @param sourcePersistentId the persistentId of the window providing the limits + * @param attachedWindowLimits the other window's limits + * @param isIntersectedHeightLimit whether to limit height with attached window's limits + * @param isIntersectedWidthLimit whether to limit width with attached window's limits + * @return Returns WSError::WS_OK if success, otherwise failed. + */ + WSError RequestUpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit = true, + bool isIntersectedWidthLimit = true, int32_t excludePersistentId = INVALID_SESSION_ID) override; + + /** + * @brief Sub window implementation: remove own limits only + * + * Sub window only removes its own attached window limits. Does not propagate + * to other windows as the parent main window handles propagation. + * + * @param sourcePersistentId the persistentId of the source window whose limits should be removed + * @param excludePersistentId unused parameter for sub window + * @return Returns WSError::WS_OK if success, otherwise failed. + */ + WSError RequestRemoveAttachedWindowLimits(int32_t sourcePersistentId, + int32_t excludePersistentId = INVALID_SESSION_ID) override; + protected: void UpdatePointerArea(const WSRect& rect) override; bool CheckPointerEventDispatch(const std::shared_ptr& pointerEvent) const override; diff --git a/window_scene/session/host/include/zidl/session_interface.h b/window_scene/session/host/include/zidl/session_interface.h index 1a34221b4c..dba254e9ff 100644 --- a/window_scene/session/host/include/zidl/session_interface.h +++ b/window_scene/session/host/include/zidl/session_interface.h @@ -424,8 +424,6 @@ public: virtual WMError UpdateSessionPropertyByAction(const sptr& property, WSPropertyChangeAction action) { return WMError::WM_OK; } virtual WMError GetAppForceLandscapeConfig(AppForceLandscapeConfig& config) { return WMError::WM_OK; } - virtual WMError GetAppForceLandscapeConfigEnable(bool& enableForceSplit) { return WMError::WM_OK; } - virtual WMError GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) { return WMError::WM_OK; } virtual WMError GetSelectMode(SelectMode& selectMode) { return WMError::WM_OK; } virtual WSError AdjustKeyboardLayout(const KeyboardLayoutParams& params) { return WSError::WS_OK; } virtual WSError SetDialogSessionBackGestureEnabled(bool isEnabled) { return WSError::WS_OK; } @@ -589,6 +587,18 @@ public: virtual WMError IsMainWindowFullScreenAcrossDisplays(bool& isAcrossDisplays) { return WMError::WM_OK; } virtual WSError GetIsHighlighted(bool& isHighlighted) { return WSError::WS_OK; } + /** + * Notify related windows about limits change. + * Called when a window's limits change via setWindowLimits. + * + * @param newLimits The new window limits. + * @return Returns WSError::WS_OK if called success, otherwise failed. + */ + virtual WSError NotifyAttachedWindowsLimitsChanged(const WindowLimits& newLimits) + { + return WSError::WS_OK; + } + /** * @brief Notify when disableDelegator change to true * @@ -715,11 +725,6 @@ public: { return WSError::WS_OK; } - - virtual WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, SelectMode selectMode) - { - return WSError::WS_OK; - } virtual WSError NotifyPageEnable(const std::string& action, const std::string& message) { diff --git a/window_scene/session/host/include/zidl/session_ipc_interface_code.h b/window_scene/session/host/include/zidl/session_ipc_interface_code.h index 95a9db5861..c846cfc6a1 100644 --- a/window_scene/session/host/include/zidl/session_ipc_interface_code.h +++ b/window_scene/session/host/include/zidl/session_ipc_interface_code.h @@ -67,7 +67,6 @@ enum class SessionInterfaceCode { TRANS_ID_DEFAULT_DENSITY_ENABLED, TRANS_ID_UPDATE_COLOR_MODE, TRANS_ID_GET_FORCE_LANDSCAPE_CONFIG, - TRANS_ID_GET_HOOK_WINDOW_INFO, TRANS_ID_NOTIFY_WINDOW_STATUS_AFTER_SHOW_WINDOW, TRANS_ID_NOTIFY_PARENT_WINDOW_SIZE_CHANGE, TRANS_ID_NOTIFY_PARENT_WINDOW_STATUS_CHANGE, @@ -108,7 +107,6 @@ enum class SessionInterfaceCode { TRANS_ID_NOTIFY_DISABLE_DELEGATOR_CHANGE, TRANS_ID_SET_WINDOW_ANCHOR_INFO, TRANS_ID_SET_WINDOW_SHADOWS, - TRANS_ID_GET_FORCE_LANDSCAPE_CONFIG_ENABLE, TRANS_ID_GET_SELECT_MODE, // keyboard @@ -123,6 +121,7 @@ enum class SessionInterfaceCode { // Window Layout Global Coordinate System TRANS_ID_UPDATE_GLOBAL_DISPLAY_RECT, + TRANS_ID_NOTIFY_RELATED_WINDOWS_LIMITS_CHANGED, // Extension TRANS_ID_TRANSFER_ABILITY_RESULT = 500, diff --git a/window_scene/session/host/include/zidl/session_proxy.h b/window_scene/session/host/include/zidl/session_proxy.h index 5f117ed5f6..518531b4e3 100644 --- a/window_scene/session/host/include/zidl/session_proxy.h +++ b/window_scene/session/host/include/zidl/session_proxy.h @@ -139,7 +139,6 @@ public: WMError UpdateSessionPropertyByAction(const sptr& property, WSPropertyChangeAction action) override; WMError GetAppForceLandscapeConfig(AppForceLandscapeConfig& config) override; - WMError GetAppForceLandscapeConfigEnable(bool& enableForceSplit) override; WSError NotifyFrameLayoutFinishFromApp(bool notifyListener, const WSRect& rect) override; WMError NotifySnapshotUpdate() override; WMError NotifyRemovePrelaunchStartingWindow() override; @@ -204,9 +203,9 @@ public: WSError UpdateKeyFrameCloneNode(std::shared_ptr& rsKeyFrameNode, std::shared_ptr& rsTransaction) override; WSError SetDragKeyFramePolicy(const KeyFramePolicy& keyFramePolicy) override; - WMError GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) override; WMError GetSelectMode(SelectMode& selectMode) override; void NotifyWindowStatusDidChangeAfterShowWindow() override; + WSError NotifyAttachedWindowsLimitsChanged(const WindowLimits& newLimits) override; /** * Window Transition Animation For PC @@ -241,8 +240,6 @@ public: */ WSError NotifyIsFullScreenInForceSplitMode(bool isFullScreen) override; WSError NotifyCompatibleModeChange(CompatibleStyleMode mode) override; - WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) override; WSError NotifyPageEnable(const std::string& action, const std::string& message) override; WMError NotifySplitRatioChanged(float newRatio) override; diff --git a/window_scene/session/host/include/zidl/session_stub.h b/window_scene/session/host/include/zidl/session_stub.h index ea0512d47c..a7b78d229b 100644 --- a/window_scene/session/host/include/zidl/session_stub.h +++ b/window_scene/session/host/include/zidl/session_stub.h @@ -89,8 +89,7 @@ private: int HandleRestoreMainWindow(MessageParcel& data, MessageParcel& reply); int HandleRestoreFloatMainWindow(MessageParcel& data, MessageParcel& reply); int HandleGetAppForceLandscapeConfig(MessageParcel& data, MessageParcel& reply); - int HandleGetAppForceLandscapeConfigEnable(MessageParcel& data, MessageParcel& reply); - int HandleGetAppHookWindowInfoFromServer(MessageParcel& data, MessageParcel& reply); + int HandleNotifyAttachedWindowsLimitsChanged(MessageParcel& data, MessageParcel& reply); int HandleGetSelectMode(MessageParcel& data, MessageParcel& reply); int HandleNotifyWindowStatusDidChangeAfterShowWindow(MessageParcel& data, MessageParcel& reply); int HandleNotifyParentWindowSizeChange(MessageParcel& data, MessageParcel& reply); diff --git a/window_scene/session/host/src/main_session.cpp b/window_scene/session/host/src/main_session.cpp index 8b8c9fd63c..a746773949 100644 --- a/window_scene/session/host/src/main_session.cpp +++ b/window_scene/session/host/src/main_session.cpp @@ -677,6 +677,11 @@ void MainSession::RegisterPageEnableCallback(PageEnableCallback&& callback) pageEnableCallback_ = std::move(callback); } +void MainSession::RegisterSetSelectModeCallback(SetSelectModeCallback&& callback) +{ + setSelectModeCallback_ = std::move(callback); +} + WMError MainSession::NotifySplitRatioChanged(float newRatio) { return WMError::WM_OK; @@ -723,6 +728,67 @@ WSError MainSession::UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInf return sessionStage_->UpdateAppHookWindowInfo(hookWindowInfo); } +WSError MainSession::UpdateHookWindowInfo(const HookWindowInfo& hookWindowInfo) +{ + if (hookWindowInfo.widthHookRatio < 0.0f) { + TLOGE(WmsLogTag::WMS_COMPAT, "Invalid hook window parameters: widthHookRatio:%{public}f", + hookWindowInfo.widthHookRatio); + return WSError::WS_ERROR_INVALID_PARAM; + } + TLOGI(WmsLogTag::WMS_COMPAT, "hookWindowInfo:[%{public}s]", hookWindowInfo.ToString().c_str()); + + auto property = GetSessionProperty(); + if (property == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "id: %{public}d property is nullptr", persistentId_); + return WSError::WS_ERROR_NULLPTR; + } + HookWindowInfo preInfo = property->GetHookWindowInfo(); + HookWindowInfo newInfo = {}; + newInfo.enableHookWindow = hookWindowInfo.enableHookWindow; + newInfo.widthHookRatio = hookWindowInfo.widthHookRatio; + newInfo.notifyWindowChange = false; + newInfo.drawableRectHook = hookWindowInfo.drawableRectHook; + property->SetHookWindowInfo(newInfo); + if (preInfo.enableHookWindow != hookWindowInfo.enableHookWindow || + !MathHelper::NearZero(preInfo.widthHookRatio - hookWindowInfo.widthHookRatio) || + preInfo.drawableRectHook != hookWindowInfo.drawableRectHook) { + // Notify the client of the info change + auto ret = UpdateAppHookWindowInfo(hookWindowInfo); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_COMPAT, "UpdateAppHookWindowInfo failed, ret: %{public}d", ret); + return ret; + } + } + return WSError::WS_OK; +} + +WSError MainSession::SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) +{ + TLOGI(WmsLogTag::WMS_COMPAT, "isForceSplitEnabled: %{public}d, needUpdateViewport: %{public}d, " + "selectMode: %{public}u", isForceSplitEnabled, needUpdateViewport, selectMode); + if (!setSelectModeCallback_) { + TLOGE(WmsLogTag::WMS_COMPAT, "setSelectModeCallback_ is nullptr"); + return WSError::WS_ERROR_NULLPTR; + } + setSelectModeCallback_(selectMode); + auto property = GetSessionProperty(); + if (property == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "id: %{public}d property is nullptr", persistentId_); + return WSError::WS_ERROR_NULLPTR; + } + property->SetForceSplitEnable(isForceSplitEnabled); + if (!sessionStage_) { + TLOGE(WmsLogTag::WMS_COMPAT, "sessionStage_ is nullptr!"); + return WSError::WS_ERROR_NULLPTR; + } + auto ret = sessionStage_->SetForceSplitEnable(isForceSplitEnabled, needUpdateViewport, selectMode); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_COMPAT, "sessionStage SetForceSplitEnable failed, ret: %{public}d", ret); + return ret; + } + return WSError::WS_OK; +} + bool MainSession::RestoreAspectRatio(float ratio) { TLOGD(WmsLogTag::WMS_LAYOUT, "windowId: %{public}d, ratio: %{public}f", GetPersistentId(), ratio); @@ -736,23 +802,95 @@ bool MainSession::RestoreAspectRatio(float ratio) return true; } -WMError MainSession::GetAppForceLandscapeConfigEnable(bool& enableForceSplit) -{ - if (forceSplitEnableFunc_ == nullptr) { - TLOGE(WmsLogTag::WMS_COMPAT, "forceSplitEnableFunc_ is null"); - return WMError::WM_ERROR_NULLPTR; - } - enableForceSplit = forceSplitEnableFunc_(sessionInfo_.bundleName_); - return WMError::WM_OK; -} - -WSError MainSession::NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, SelectMode selectMode) +/** @note @window.layout */ +WSError MainSession::RequestUpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, bool isIntersectedWidthLimit, + int32_t excludePersistentId) { + int32_t winId = GetPersistentId(); if (!sessionStage_) { - TLOGE(WmsLogTag::WMS_COMPAT, "sessionStage_ is null"); + TLOGE(WmsLogTag::WMS_LAYOUT, "sessionStage_ is null for main window id=%{public}d", winId); return WSError::WS_ERROR_NULLPTR; } - return sessionStage_->NotifyAppForceLandscapeConfigEnableUpdated(needUpdateViewport, selectMode); + + // Update own limits first (skip if excluded - i.e., when propagating from/to self) + if (excludePersistentId != winId) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d updating limits from source id=%{public}d", + winId, sourcePersistentId); + const auto& property = GetSessionProperty(); + property->SetAttachedWindowLimits(sourcePersistentId, attachedWindowLimits); + AttachLimitOptions limitOptions; + limitOptions.isIntersectedHeightLimit = isIntersectedHeightLimit; + limitOptions.isIntersectedWidthLimit = isIntersectedWidthLimit; + property->SetAttachedLimitOptions(sourcePersistentId, limitOptions); + WSError ret = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedWindowLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d failed to update own limits", winId); + return ret; + } + } + + // Propagate to children (excluding specified one) + std::vector> subSessions = GetSubSession(); + for (auto& subSession : subSessions) { + if (!ShouldNotifyAttachedWindow(subSession)) { + continue; + } + // Skip excluded child + if (subSession->GetPersistentId() == excludePersistentId) { + continue; + } + + TLOGD(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d requesting child id=%{public}d to update limits " + "from source id=%{public}d", winId, subSession->GetPersistentId(), sourcePersistentId); + // All notifications use the input parameter values + subSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedWindowLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + } + return WSError::WS_OK; +} + +/** @note @window.layout */ +WSError MainSession::RequestRemoveAttachedWindowLimits(int32_t sourcePersistentId, + int32_t excludePersistentId) +{ + int32_t winId = GetPersistentId(); + if (!sessionStage_) { + TLOGE(WmsLogTag::WMS_LAYOUT, "sessionStage_ is null for main window id=%{public}d", winId); + return WSError::WS_ERROR_NULLPTR; + } + + // Remove own limits first (skip if excluded - i.e., when propagating from/to self) + if (excludePersistentId != winId) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d removing limits from source id=%{public}d", + winId, sourcePersistentId); + const auto& property = GetSessionProperty(); + property->RemoveAttachedWindowLimits(sourcePersistentId); + property->RemoveAttachedLimitOptions(sourcePersistentId); + WSError ret = sessionStage_->RemoveAttachedWindowLimits(sourcePersistentId); + if (ret != WSError::WS_OK) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d failed to remove own limits", winId); + return ret; + } + } + + // Propagate to children (excluding specified one) + std::vector> subSessions = GetSubSession(); + for (auto& subSession : subSessions) { + if (!ShouldNotifyAttachedWindow(subSession)) { + continue; + } + // Skip excluded child + if (subSession->GetPersistentId() == excludePersistentId) { + continue; + } + + TLOGD(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d requesting child id=%{public}d to remove limits " + "from source id=%{public}d", winId, subSession->GetPersistentId(), sourcePersistentId); + subSession->RequestRemoveAttachedWindowLimits(sourcePersistentId); + } + return WSError::WS_OK; } bool MainSession::GetSessionBoundedSystemTray( @@ -762,11 +900,6 @@ bool MainSession::GetSessionBoundedSystemTray( isSessionBoundedSystemTrayCallback_(callingPid, callingToken, instanceKey)); } -void MainSession::RegisterForceSplitEnableListener(NotifyForceSplitEnableFunc&& func) -{ - forceSplitEnableFunc_ = std::move(func); -} - void MainSession::RemovePrelaunchStartingWindow() { auto lifecycleListeners = GetListeners(); diff --git a/window_scene/session/host/src/scene_session.cpp b/window_scene/session/host/src/scene_session.cpp index 08b7c1f68d..e4eb5be6f6 100644 --- a/window_scene/session/host/src/scene_session.cpp +++ b/window_scene/session/host/src/scene_session.cpp @@ -144,6 +144,31 @@ bool isMainOrExtendScreenMode(const ScreenSourceMode& screenSourceMode) return screenSourceMode == ScreenSourceMode::SCREEN_MAIN || screenSourceMode == ScreenSourceMode::SCREEN_EXTEND; } + +void SetDarkColorModeToSurfaceNode(const std::shared_ptr& surfaceNode, bool isDarkMode) +{ + if (!surfaceNode) { + return; + } + AutoRSTransaction trans(surfaceNode); + surfaceNode->SetDarkColorMode(isDarkMode); +} + +bool GetSystemDarkMode() +{ + auto appContext = AbilityRuntime::Context::GetApplicationContext(); + if (appContext == nullptr) { + TLOGE(WmsLogTag::WMS_ATTRIBUTE, "app context is nullptr"); + return false; + } + auto config = appContext->GetConfiguration(); + if (config == nullptr) { + TLOGE(WmsLogTag::WMS_ATTRIBUTE, "app configuration is nullptr"); + return false; + } + return config->GetItem(AAFwk::GlobalConfigurationKey::SYSTEM_COLORMODE) == + AppExecFwk::ConfigurationInner::COLOR_MODE_DARK; +} } // namespace MaximizeMode SceneSession::maximizeMode_ = MaximizeMode::MODE_RECOVER; @@ -470,7 +495,11 @@ WSError SceneSession::Foreground( TLOGW(WmsLogTag::WMS_LIFE, "screen is locked, session %{public}d %{public}s start below lock screen permission verified", GetPersistentId(), sessionInfo_.bundleName_.c_str()); - } else { + } else if (sessionInfo_.isGamePrelaunch_) { + TLOGW(WmsLogTag::WMS_LIFE, + "[gameprelaunch]screen is locked, session %{public}d %{public}s start below game prelaunch", + GetPersistentId(), sessionInfo_.bundleName_.c_str()); + } else { TLOGW(WmsLogTag::WMS_LIFE, "failed: screen is locked, session %{public}d %{public}s show without ShowWhenLocked flag", GetPersistentId(), sessionInfo_.bundleName_.c_str()); @@ -837,6 +866,10 @@ WSError SceneSession::DisconnectTask(bool isFromClient, bool isSaveSnapshot) } session->Session::Disconnect(isFromClient); session->isTerminating_ = false; + + // Notify related windows to remove this window's limits from their maps + session->NotifyRelatedWindowsOnDestruction(); + if (session->specificCallback_ != nullptr) { session->specificCallback_->onHandleSecureSessionShouldHide_(session); session->isEnableGestureBack_ = true; @@ -7452,13 +7485,15 @@ WMError SceneSession::HandleActionUpdateWindowLimits(const sptrSetWindowLimits(property->GetWindowLimits()); sessionProperty->SetWindowLimitsVP(property->GetWindowLimitsVP()); sessionProperty->SetUserWindowLimits(property->GetUserWindowLimits()); + sessionProperty->SetLimitsForAttachedWindows(property->GetLimitsForAttachedWindows()); WindowLimits windowLimits = sessionProperty->GetWindowLimits(); WindowLimits windowLimitsVP = sessionProperty->GetWindowLimitsVP(); WindowLimits userWindowLimits = sessionProperty->GetUserWindowLimits(); - TLOGI(WmsLogTag::WMS_LAYOUT, "id:%{public}d, px:%{public}s, vp:%{public}s, userLimitsUnit:%{public}u", - GetPersistentId(), windowLimits.ToString().c_str(), windowLimitsVP.ToString().c_str(), - userWindowLimits.pixelUnit_); + WindowLimits limitsForAttachedWindows = sessionProperty->GetLimitsForAttachedWindows(); + TLOGI(WmsLogTag::WMS_LAYOUT, "id:%{public}d, px:%{public}s, vp:%{public}s, userLimitsUnit:%{public}u, " + "limitsForAttached:%{public}s", GetPersistentId(), windowLimits.ToString().c_str(), + windowLimitsVP.ToString().c_str(), userWindowLimits.pixelUnit_, limitsForAttachedWindows.ToString().c_str()); bool useVPLimits = (userWindowLimits.pixelUnit_ == PixelUnit::VP); const WindowLimits& limitsToNotify = useVPLimits ? windowLimitsVP : windowLimits; @@ -8403,9 +8438,12 @@ WMError SceneSession::OnUpdateColorMode(const std::string& colorMode, bool hasDa { TLOGI(WmsLogTag::WMS_ATTRIBUTE, "winId: %{public}d, colorMode: %{public}s, hasDarkRes: %{public}u", GetPersistentId(), colorMode.c_str(), hasDarkRes); - std::lock_guard lock(colorModeMutex_); - colorMode_ = colorMode; - hasDarkRes_ = hasDarkRes; + { + std::lock_guard lock(colorModeMutex_); + colorMode_ = colorMode; + hasDarkRes_ = hasDarkRes; + } + UpdateSurfaceDarkMode(); return WMError::WM_OK; } @@ -8420,6 +8458,28 @@ std::string SceneSession::GetAbilityColorMode() const return colorMode_; } +void SceneSession::OnSurfaceNodeChanged() +{ + UpdateSurfaceDarkMode(); +} + +bool SceneSession::GetDarkMode() const +{ + auto colorMode = GetAbilityColorMode(); + if (colorMode == AppExecFwk::ConfigurationInner::COLOR_MODE_DARK) { + return true; + } + if (colorMode == AppExecFwk::ConfigurationInner::COLOR_MODE_LIGHT) { + return false; + } + return GetSystemDarkMode(); +} + +void SceneSession::UpdateSurfaceDarkMode() +{ + SetDarkColorModeToSurfaceNode(GetShadowSurfaceNode(), GetDarkMode()); +} + /** @note @Window.Layout */ WMError SceneSession::UpdateWindowModeForUITest(int32_t updateMode) { @@ -8712,15 +8772,6 @@ void SceneSession::RegisterForceSplitListener(const NotifyForceSplitFunc& func) forceSplitFunc_ = func; } -void SceneSession::RegisterAppHookWindowInfoFunc(GetHookWindowInfoFunc&& func) -{ - if (!func) { - TLOGW(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, func is null", GetPersistentId()); - return; - } - getHookWindowInfoFunc_ = std::move(func); -} - void SceneSession::RegisterSelectModeFunc(GetSelectModeFunc&& func) { if (!func) { @@ -8795,23 +8846,6 @@ WMError SceneSession::GetAppForceLandscapeConfig(AppForceLandscapeConfig& config return WMError::WM_OK; } -WMError SceneSession::GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) -{ - return PostSyncTask([weakThis = wptr(this), &hookWindowInfo, where = __func__]() -> WMError { - auto session = weakThis.promote(); - if (!session) { - TLOGNE(WmsLogTag::WMS_LAYOUT, "%{public}s session is null", where); - return WMError::WM_ERROR_INVALID_SESSION; - } - if (!session->getHookWindowInfoFunc_) { - TLOGW(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, func is null", session->GetPersistentId()); - return WMError::WM_ERROR_NULLPTR; - } - hookWindowInfo = session->getHookWindowInfoFunc_(session->GetSessionInfo().bundleName_); - return WMError::WM_OK; - }, __func__); -} - WMError SceneSession::GetSelectMode(SelectMode& selectMode) { return PostSyncTask([weakThis = wptr(this), &selectMode, where = __func__]() -> WMError { @@ -8922,6 +8956,223 @@ bool SceneSession::CheckAndGetAbilityInfoByWant(const std::shared_ptronCheckAndGetAbilityInfoByWantCallback_(want, abilityInfo); } +/** @note @window.layout */ +bool SceneSession::ShouldProcessAttachStateChange(bool wasAttached, bool isAttached, + bool oldIsIntersectedWidthLimit, bool oldIsIntersectedHeightLimit, bool& isDetaching) +{ + // Only proceed if this is from attach/detach operation + if (!windowAnchorInfo_.isFromAttachOrDetach_) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Window id=%{public}d isFromAttachOrDetach_ is false, skip attach state change", + GetPersistentId()); + return false; + } + + // Calculate old and new effective intersected limits values + // The effective value is the actual result of whether limits intersection applies + bool oldEffectiveWidthLimit = wasAttached && oldIsIntersectedWidthLimit; + bool oldEffectiveHeightLimit = wasAttached && oldIsIntersectedHeightLimit; + bool newEffectiveWidthLimit = isAttached && windowAnchorInfo_.attachOptions.isIntersectedWidthLimit; + bool newEffectiveHeightLimit = isAttached && windowAnchorInfo_.attachOptions.isIntersectedHeightLimit; + + // Only proceed if effective limits have actually changed + bool widthLimitChanged = (oldEffectiveWidthLimit != newEffectiveWidthLimit); + bool heightLimitChanged = (oldEffectiveHeightLimit != newEffectiveHeightLimit); + + if (!widthLimitChanged && !heightLimitChanged) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Win id=%{public}d attachState: wasAttach=%{public}d isAttach=%{public}d, " + "widthInt: %{public}d->%{public}d, heightInt: %{public}d->%{public}d, eff unchanged, skip", + GetPersistentId(), wasAttached, isAttached, + oldIsIntersectedWidthLimit, windowAnchorInfo_.attachOptions.isIntersectedWidthLimit, + oldIsIntersectedHeightLimit, windowAnchorInfo_.attachOptions.isIntersectedHeightLimit); + return false; + } + + // Determine if this is a detach operation + isDetaching = (wasAttached && !isAttached); + + TLOGI(WmsLogTag::WMS_LAYOUT, "Window id=%{public}d effective intersected limits changed: " + "widthLimit %{public}d->%{public}d, heightLimit %{public}d->%{public}d, isDetaching=%{public}d", + GetPersistentId(), oldEffectiveWidthLimit, newEffectiveWidthLimit, + oldEffectiveHeightLimit, newEffectiveHeightLimit, isDetaching); + return true; +} + +/** @note @window.layout */ +void SceneSession::SyncAllAttachedLimitsToAttachingChild(const sptr& parentSession) +{ + if (!parentSession || !sessionStage_) { + TLOGW(WmsLogTag::WMS_LAYOUT, "parentSession or sessionStage_ is null"); + return; + } + auto parentSceneSession = static_cast(parentSession.GetRefPtr()); + if (!parentSceneSession) { + TLOGW(WmsLogTag::WMS_LAYOUT, "parentSceneSession is null"); + return; + } + const auto& parentProperty = parentSceneSession->GetSessionProperty(); + int32_t parentWinId = parentSceneSession->GetPersistentId(); + auto parentAttachedLimits = parentProperty->GetAttachedWindowLimitsList(); + auto parentAttachedOptions = parentProperty->GetAttachedLimitOptionsList(); + WindowLimits parentOwnLimits = parentProperty->GetLimitsForAttachedWindows(); + AttachLimitOptions defaultOptions{true, true}; + std::vector> limitsList; + std::vector> optionsList; + // Parent's own limits first (highest priority) + limitsList.emplace_back(parentWinId, parentOwnLimits); + optionsList.emplace_back(parentWinId, defaultOptions); + // Other attached windows' limits in original order + for (const auto& [sourceId, limits] : parentAttachedLimits) { + limitsList.emplace_back(sourceId, limits); + AttachLimitOptions options = defaultOptions; + for (const auto& [optSourceId, opt] : parentAttachedOptions) { + if (optSourceId == sourceId) { + options = opt; + break; + } + } + optionsList.emplace_back(sourceId, options); + } + TLOGI(WmsLogTag::WMS_LAYOUT, "Sync parent id=%{public}d to attaching child id=%{public}d, total=%{public}zu", + parentWinId, GetPersistentId(), limitsList.size()); + sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); +} + +/** @note @window.layout */ +void SceneSession::NotifyRelatedWindowsAttachStateChange(const sptr& parentSession, + bool wasAttached, bool isAttached, bool oldIsIntersectedWidthLimit, bool oldIsIntersectedHeightLimit) +{ + bool isDetaching = false; + if (!ShouldProcessAttachStateChange(wasAttached, isAttached, oldIsIntersectedWidthLimit, + oldIsIntersectedHeightLimit, isDetaching)) { + return; + } + + auto property = GetSessionProperty(); + int32_t winId = GetPersistentId(); + + if (isDetaching) { + // Detaching: remove this window's limits from parent and siblings' maps + // Also clear this window's list + TLOGI(WmsLogTag::WMS_LAYOUT, "Window id=%{public}d is detaching, notifying related windows", winId); + + // Clear this window's list + property->ClearAttachedWindowLimitsList(); + property->ClearAttachedLimitOptionsList(); + + // Notify parent (main window will propagate to siblings automatically) + if (parentSession) { + parentSession->RequestRemoveAttachedWindowLimits(winId); + } + } else { + // Attaching or limits changed: notify related windows with updated limits info + TLOGI(WmsLogTag::WMS_LAYOUT, "Window id=%{public}d notifying related windows with limits info", winId); + + // When first attaching, sync parent's full limits list to this child + if (!wasAttached && isAttached) { + SyncAllAttachedLimitsToAttachingChild(parentSession); + } + + WindowLimits newLimits = property->GetLimitsForAttachedWindows(); + bool newIsIntersectedHeightLimit = windowAnchorInfo_.attachOptions.isIntersectedHeightLimit; + bool newIsIntersectedWidthLimit = windowAnchorInfo_.attachOptions.isIntersectedWidthLimit; + + // Notify parent (main window will propagate to siblings and notify back) + // Exclude self to avoid redundant update + if (parentSession) { + parentSession->RequestUpdateAttachedWindowLimits(winId, newLimits, + newIsIntersectedHeightLimit, newIsIntersectedWidthLimit, winId); + } + } +} + +/** @note @window.layout */ +bool SceneSession::ShouldNotifyAttachedWindow(const sptr& subSession) const +{ + return subSession && subSession->windowAnchorInfo_.isAnchoredByAttach_ && + (subSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit || + subSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit); +} + +/** @note @window.layout */ +WSError SceneSession::NotifyAttachedWindowsLimitsChanged(const WindowLimits& newLimits) +{ + // Post async task to business thread + PostTask([weakThis = wptr(this), newLimits, where = __func__] { + auto session = weakThis.promote(); + if (!session) { + TLOGNE(WmsLogTag::WMS_LAYOUT, "%{public}s: session is null", where); + return; + } + + // Save the limits that are notified to attached windows + session->GetSessionProperty()->SetLimitsForAttachedWindows(newLimits); + + int32_t winId = session->GetPersistentId(); + bool isIntersectedHeightLimit = session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit; + bool isIntersectedWidthLimit = session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit; + WindowType windowType = session->GetWindowType(); + if (WindowHelper::IsMainWindow(windowType)) { + // Main window: propagate limits to all attached children (exclude self to skip redundant update) + TLOGND(WmsLogTag::WMS_LAYOUT, "%{public}s win id=%{public}d is main window, notifying attached children", + where, winId); + // Use default intersect flags for main window + session->RequestUpdateAttachedWindowLimits(winId, newLimits, true, true, winId); + } else if (WindowHelper::IsSubWindow(windowType)) { + // Child window: only notify parent, parent will notify siblings automatically + TLOGND(WmsLogTag::WMS_LAYOUT, "%{public}s window id=%{public}d is child window, notifying parent", + where, winId); + + auto parentSession = session->GetParentSession(); + if (parentSession) { + TLOGND(WmsLogTag::WMS_LAYOUT, + "%{public}s notifying parent window id=%{public}d about limits change", + where, parentSession->GetPersistentId()); + // Parent will record child's limits and notify all children (including siblings) + parentSession->RequestUpdateAttachedWindowLimits(winId, newLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit, winId); + } else { + TLOGNW(WmsLogTag::WMS_LAYOUT, "%{public}s parent session not found for window id=%{public}d", + where, winId); + } + } + }, __func__); + + return WSError::WS_OK; +} + +/** @note @window.layout */ +void SceneSession::NotifyRelatedWindowsOnDestruction() +{ + // Only proceed if this window had attach relationship with intersected limits + if (!windowAnchorInfo_.isAnchoredByAttach_ || + (!windowAnchorInfo_.attachOptions.isIntersectedWidthLimit && + !windowAnchorInfo_.attachOptions.isIntersectedHeightLimit)) { + return; + } + + int32_t winId = GetPersistentId(); + TLOGI(WmsLogTag::WMS_LAYOUT, "Window id=%{public}d is being destroyed, notifying related windows", winId); + + WindowType windowType = GetWindowType(); + if (WindowHelper::IsMainWindow(windowType)) { + // Main window: propagate limits removal to all attached children (exclude self) + TLOGD(WmsLogTag::WMS_LAYOUT, "Main window id=%{public}d being destroyed, notifying attached children", winId); + RequestRemoveAttachedWindowLimits(winId, winId); // Exclude self + } else if (WindowHelper::IsSubWindow(windowType)) { + // Child window: only notify parent, parent will notify siblings automatically + TLOGD(WmsLogTag::WMS_LAYOUT, "Child window id=%{public}d being destroyed, notifying parent", winId); + + auto parentSession = GetParentSession(); + if (parentSession) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Notifying parent window id=%{public}d to remove limits", + parentSession->GetPersistentId()); + // Parent will remove child's limits and notify other children (siblings) to remove child's limits + parentSession->RequestRemoveAttachedWindowLimits(winId); + } else { + TLOGW(WmsLogTag::WMS_LAYOUT, "Parent session not found for window id=%{public}d", winId); + } + } +} void SceneSession::SetWindowAnchorInfoChangeFunc(NotifyWindowAnchorInfoChangeFunc&& func) { @@ -8985,7 +9236,13 @@ WSError SceneSession::SetWindowAnchorInfo(const WindowAnchorInfo& windowAnchorIn return ret; } - PostTask([weakThis = wptr(this), windowAnchorInfo, weakParentSession = wptr(parentSession), where = __func__] { + // Store old attach state and intersected limits to detect state changes + bool oldIsAnchoredByAttach = windowAnchorInfo_.isAnchoredByAttach_; + bool oldIsIntersectedWidthLimit = windowAnchorInfo_.attachOptions.isIntersectedWidthLimit; + bool oldIsIntersectedHeightLimit = windowAnchorInfo_.attachOptions.isIntersectedHeightLimit; + + PostTask([weakThis = wptr(this), windowAnchorInfo, weakParentSession = wptr(parentSession), + oldIsAnchoredByAttach, oldIsIntersectedWidthLimit, oldIsIntersectedHeightLimit, where = __func__] { auto session = weakThis.promote(); if (!session) { TLOGNE(WmsLogTag::WMS_SUB, "%{public}s session is null", where); @@ -8996,6 +9253,7 @@ WSError SceneSession::SetWindowAnchorInfo(const WindowAnchorInfo& windowAnchorIn TLOGNE(WmsLogTag::WMS_LAYOUT, "%{public}s parentSession is null", where); return; } + session->windowAnchorInfo_ = windowAnchorInfo; if (session->onWindowAnchorInfoChangeFunc_) { session->onWindowAnchorInfoChangeFunc_(windowAnchorInfo); @@ -9010,6 +9268,10 @@ WSError SceneSession::SetWindowAnchorInfo(const WindowAnchorInfo& windowAnchorIn } else { TLOGI(WmsLogTag::WMS_SUB, "func is null"); } + + // Notify related windows about attach/detach for limits intersection + session->NotifyRelatedWindowsAttachStateChange(parentSession, oldIsAnchoredByAttach, + windowAnchorInfo.isAnchoredByAttach_, oldIsIntersectedWidthLimit, oldIsIntersectedHeightLimit); }); return WSError::WS_OK; } diff --git a/window_scene/session/host/src/session.cpp b/window_scene/session/host/src/session.cpp index aa425cf060..0f7e320c53 100644 --- a/window_scene/session/host/src/session.cpp +++ b/window_scene/session/host/src/session.cpp @@ -200,16 +200,19 @@ int32_t Session::GetCurrentRotation() const void Session::SetSurfaceNode(const std::shared_ptr& surfaceNode) { RSAdapterUtil::SetRSUIContext(surfaceNode, GetRSUIContext(), true); - std::lock_guard lock(surfaceNodeMutex_); - surfaceNode_ = surfaceNode; - if (surfaceNode_) { - surfaceNode_->MarkLayerPartRender(isLayerPartRender_); - } - shadowSurfaceNode_ = RSAdapterUtil::IsClientMultiInstanceEnabled() && surfaceNode_ ? - surfaceNode_->CreateShadowSurfaceNode() : nullptr; + { + std::lock_guard lock(surfaceNodeMutex_); + surfaceNode_ = surfaceNode; + if (surfaceNode_) { + surfaceNode_->MarkLayerPartRender(isLayerPartRender_); + } + shadowSurfaceNode_ = RSAdapterUtil::IsClientMultiInstanceEnabled() && surfaceNode_ ? + surfaceNode_->CreateShadowSurfaceNode() : nullptr; - // Reset move drag shadow surface node when surface node changes. - moveDragShadowSurfaceNode_ = nullptr; + // Reset move drag shadow surface node when surface node changes. + moveDragShadowSurfaceNode_ = nullptr; + } + OnSurfaceNodeChanged(); } std::shared_ptr Session::GetSurfaceNode() const @@ -1679,7 +1682,6 @@ __attribute__((no_sanitize("cfi"))) WSError Session::ConnectInner(const sptrSetCurrentRotation(currentRotation_); windowEventChannel_ = eventChannel; SetSurfaceNode(surfaceNode); @@ -1773,6 +1775,8 @@ void Session::InitSessionPropertyWhenConnect(const sptr& } if (SessionHelper::IsMainWindow(GetWindowType())) { property->SetIsPcAppInPad(GetSessionProperty()->GetIsPcAppInPad()); + property->SetForceSplitEnable(GetSessionProperty()->GetForceSplitEnable()); + property->SetHookWindowInfo(GetSessionProperty()->GetHookWindowInfo()); } property->SetSkipSelfWhenShowOnVirtualScreen(GetSessionProperty()->GetSkipSelfWhenShowOnVirtualScreen()); property->SetSkipEventOnCastPlus(GetSessionProperty()->GetSkipEventOnCastPlus()); @@ -1819,7 +1823,6 @@ WSError Session::Reconnect(const sptr& sessionStage, const sptrNotifyAppForceLandscapeConfigUpdated(); } -WSError Session::NotifyAppHookWindowInfoUpdated() -{ - if (!sessionStage_) { - return WSError::WS_ERROR_NULLPTR; - } - return sessionStage_->NotifyAppHookWindowInfoUpdated(); -} - void Session::SetParentSession(const sptr& session) { if (session == nullptr) { @@ -6143,4 +6138,4 @@ WSError Session::SetIsShowDecorInFreeMultiWindow(bool isShow) } return WSError::WS_OK; } -} // namespace OHOS::Rosen \ No newline at end of file +} // namespace OHOS::Rosen diff --git a/window_scene/session/host/src/sub_session.cpp b/window_scene/session/host/src/sub_session.cpp index 6ae92ad63a..33dc062101 100644 --- a/window_scene/session/host/src/sub_session.cpp +++ b/window_scene/session/host/src/sub_session.cpp @@ -379,6 +379,54 @@ WMError SubSession::NotifySetParentSession(int32_t oldParentWindowId, int32_t ne }, __func__); } +/** @note @window.layout */ +WSError SubSession::RequestUpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, bool isIntersectedWidthLimit, + int32_t excludePersistentId) +{ + if (!sessionStage_) { + TLOGE(WmsLogTag::WMS_LAYOUT, "sessionStage_ is null for sub window id=%{public}d", GetPersistentId()); + return WSError::WS_ERROR_NULLPTR; + } + + // Sub window: only update own limits + TLOGD(WmsLogTag::WMS_LAYOUT, "Sub window id=%{public}d updating limits from source id=%{public}d", + GetPersistentId(), sourcePersistentId); + const auto& property = GetSessionProperty(); + property->SetAttachedWindowLimits(sourcePersistentId, attachedWindowLimits); + AttachLimitOptions limitOptions; + limitOptions.isIntersectedHeightLimit = isIntersectedHeightLimit; + limitOptions.isIntersectedWidthLimit = isIntersectedWidthLimit; + property->SetAttachedLimitOptions(sourcePersistentId, limitOptions); + return sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedWindowLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); +} + +/** @note @window.layout */ +WSError SubSession::RequestRemoveAttachedWindowLimits(int32_t sourcePersistentId, + int32_t excludePersistentId) +{ + int32_t winId = GetPersistentId(); + if (!sessionStage_) { + TLOGE(WmsLogTag::WMS_LAYOUT, "sessionStage_ is null for sub window id=%{public}d", winId); + return WSError::WS_ERROR_NULLPTR; + } + + const auto& property = GetSessionProperty(); + if (sourcePersistentId == winId) { + // This window is detaching - clear all attached limits lists + TLOGI(WmsLogTag::WMS_LAYOUT, "Id=%{public}u is detaching, clearing all attached limits", winId); + property->ClearAttachedWindowLimitsList(); + property->ClearAttachedLimitOptionsList(); + } else { + TLOGI(WmsLogTag::WMS_LAYOUT, "Id=%{public}d removing limits from source id=%{public}d", + winId, sourcePersistentId); + property->RemoveAttachedWindowLimits(sourcePersistentId); + property->RemoveAttachedLimitOptions(sourcePersistentId); + } + return sessionStage_->RemoveAttachedWindowLimits(sourcePersistentId); +} + void SubSession::HandleCrossMoveToSurfaceNode(WSRect& globalRect) { auto movedSurfaceNode = GetMoveDragTargetSurfaceNode(); diff --git a/window_scene/session/host/src/zidl/session_proxy.cpp b/window_scene/session/host/src/zidl/session_proxy.cpp index e62f11a225..58d5924394 100644 --- a/window_scene/session/host/src/zidl/session_proxy.cpp +++ b/window_scene/session/host/src/zidl/session_proxy.cpp @@ -371,6 +371,13 @@ WSError SessionProxy::Connect(const sptr& sessionStage, const spt property->SetPcAppInpadSpecificSystemBarInvisible(reply.ReadBool()); property->SetPcAppInpadOrientationLandscape(reply.ReadBool()); property->SetMobileAppInPadLayoutFullScreen(reply.ReadBool()); + property->SetForceSplitEnable(reply.ReadBool()); + sptr hookWindowInfo = reply.ReadParcelable(); + if (hookWindowInfo == nullptr) { + TLOGE(WmsLogTag::WMS_COMPAT, "read hookWindowInfo is nullptr!"); + return WSError::WS_ERROR_IPC_FAILED; + } + property->SetHookWindowInfo(*hookWindowInfo); property->SetCompatibleModeProperty(reply.ReadParcelable()); property->SetUseControlState(reply.ReadBool()); property->SetAncoRealBundleName(reply.ReadString()); @@ -2880,71 +2887,6 @@ WMError SessionProxy::GetAppForceLandscapeConfig(AppForceLandscapeConfig& config return static_cast(ret); } -WMError SessionProxy::GetAppForceLandscapeConfigEnable(bool& enableForceSplit) -{ - MessageParcel data; - MessageParcel reply; - MessageOption option(MessageOption::TF_SYNC); - if (!data.WriteInterfaceToken(GetDescriptor())) { - TLOGE(WmsLogTag::WMS_COMPAT, "WriteInterfaceToken failed"); - return WMError::WM_ERROR_IPC_FAILED; - } - sptr remote = Remote(); - if (remote == nullptr) { - TLOGE(WmsLogTag::WMS_COMPAT, "remote is null"); - return WMError::WM_ERROR_IPC_FAILED; - } - int sendCode = remote->SendRequest( - static_cast(SessionInterfaceCode::TRANS_ID_GET_FORCE_LANDSCAPE_CONFIG_ENABLE), - data, reply, option); - if (sendCode != ERR_NONE) { - TLOGE(WmsLogTag::WMS_COMPAT, "SendRequest failed, code: %{public}d", sendCode); - return WMError::WM_ERROR_IPC_FAILED; - } - if (!reply.ReadBool(enableForceSplit)) { - TLOGE(WmsLogTag::WMS_COMPAT, "Read enableForceSplit failed"); - return WMError::WM_ERROR_IPC_FAILED; - } - int32_t ret = 0; - if (!reply.ReadInt32(ret)) { - TLOGE(WmsLogTag::WMS_COMPAT, "read ret failed"); - return WMError::WM_ERROR_IPC_FAILED; - } - - return static_cast(ret); -} - -WMError SessionProxy::GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) -{ - MessageParcel data; - MessageParcel reply; - MessageOption option(MessageOption::TF_SYNC); - if (!data.WriteInterfaceToken(GetDescriptor())) { - TLOGE(WmsLogTag::WMS_LAYOUT, "WriteInterfaceToken failed"); - return WMError::WM_ERROR_IPC_FAILED; - } - sptr remote = Remote(); - if (!remote) { - TLOGE(WmsLogTag::WMS_LAYOUT, "remote is null"); - return WMError::WM_ERROR_IPC_FAILED; - } - uint32_t requestCode = static_cast(SessionInterfaceCode::TRANS_ID_GET_HOOK_WINDOW_INFO); - if (remote->SendRequest(requestCode, data, reply, option) != ERR_NONE) { - TLOGE(WmsLogTag::WMS_LAYOUT, "SendRequest failed"); - return WMError::WM_ERROR_IPC_FAILED; - } - sptr replyInfo = reply.ReadParcelable(); - if (replyInfo) { - hookWindowInfo = *replyInfo; - } - int32_t ret = 0; - if (!reply.ReadInt32(ret)) { - TLOGE(WmsLogTag::WMS_LAYOUT, "read ret failed"); - return WMError::WM_ERROR_IPC_FAILED; - } - return static_cast(ret); -} - WMError SessionProxy::GetSelectMode(SelectMode& selectMode) { MessageParcel data; @@ -4532,39 +4474,6 @@ WSError SessionProxy::NotifyCompatibleModeChange(CompatibleStyleMode mode) return WSError::WS_OK; } -WSError SessionProxy::NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, SelectMode selectMode) -{ - MessageParcel data; - MessageParcel reply; - MessageOption option(MessageOption::TF_ASYNC); - if (!data.WriteInterfaceToken(GetDescriptor())) { - TLOGE(WmsLogTag::WMS_COMPAT, "WriteInterfaceToken failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - if (!data.WriteBool(needUpdateViewport)) { - TLOGE(WmsLogTag::WMS_COMPAT, "Write needUpdateViewport failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - if (!data.WriteUint32(static_cast(selectMode))) { - TLOGE(WmsLogTag::WMS_COMPAT, "Write selectMode failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - - sptr remote = Remote(); - if (remote == nullptr) { - TLOGE(WmsLogTag::WMS_COMPAT, "remote is null"); - return WSError::WS_ERROR_IPC_FAILED; - } - - if (remote->SendRequest( - static_cast(SessionInterfaceCode::TRANS_ID_GET_FORCE_LANDSCAPE_CONFIG_ENABLE), - data, reply, option) != ERR_NONE) { - TLOGE(WmsLogTag::WMS_COMPAT, "SendRequest failed"); - return WSError::WS_ERROR_IPC_FAILED; - } - return WSError::WS_OK; -} - WSError SessionProxy::NotifyPageEnable(const std::string& action, const std::string& message) { MessageParcel data; @@ -4595,6 +4504,36 @@ WSError SessionProxy::NotifyPageEnable(const std::string& action, const std::str return WSError::WS_OK; } +/** @note @window.layout */ +WSError SessionProxy::NotifyAttachedWindowsLimitsChanged(const WindowLimits& newLimits) +{ + TLOGD(WmsLogTag::WMS_LAYOUT, "in"); + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + if (!data.WriteInterfaceToken(GetDescriptor())) { + TLOGE(WmsLogTag::WMS_LAYOUT, "WriteInterfaceToken failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + if (!newLimits.Marshalling(data)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Write newLimits failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + sptr remote = Remote(); + if (remote == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "remote is null"); + return WSError::WS_ERROR_IPC_FAILED; + } + int sendCode = remote->SendRequest( + static_cast(SessionInterfaceCode::TRANS_ID_NOTIFY_RELATED_WINDOWS_LIMITS_CHANGED), + data, reply, option); + if (sendCode != ERR_NONE) { + TLOGE(WmsLogTag::WMS_LAYOUT, "SendRequest failed"); + return WSError::WS_ERROR_IPC_FAILED; + } + return WSError::WS_OK; +} + WSError SessionProxy::RestartApp(const std::shared_ptr& want) { MessageParcel data; diff --git a/window_scene/session/host/src/zidl/session_stub.cpp b/window_scene/session/host/src/zidl/session_stub.cpp index 493a6b6a92..9bd12f3209 100644 --- a/window_scene/session/host/src/zidl/session_stub.cpp +++ b/window_scene/session/host/src/zidl/session_stub.cpp @@ -255,10 +255,6 @@ int SessionStub::ProcessRemoteRequest(uint32_t code, MessageParcel& data, Messag return HandleTitleAndDockHoverShowChange(data, reply); case static_cast(SessionInterfaceCode::TRANS_ID_GET_FORCE_LANDSCAPE_CONFIG): return HandleGetAppForceLandscapeConfig(data, reply); - case static_cast(SessionInterfaceCode::TRANS_ID_GET_FORCE_LANDSCAPE_CONFIG_ENABLE): - return HandleGetAppForceLandscapeConfigEnable(data, reply); - case static_cast(SessionInterfaceCode::TRANS_ID_GET_HOOK_WINDOW_INFO): - return HandleGetAppHookWindowInfoFromServer(data, reply); case static_cast(SessionInterfaceCode::TRANS_ID_GET_SELECT_MODE): return HandleGetSelectMode(data, reply); case static_cast(SessionInterfaceCode::TRANS_ID_NOTIFY_WINDOW_STATUS_AFTER_SHOW_WINDOW): @@ -361,6 +357,8 @@ int SessionStub::ProcessRemoteRequest(uint32_t code, MessageParcel& data, Messag return HandleNotifyCompatibleModeChange(data, reply); case static_cast(SessionInterfaceCode::TRANS_ID_NOTIFY_PAGE_ENABLE): return HandleNotifyPageEnable(data, reply); + case static_cast(SessionInterfaceCode::TRANS_ID_NOTIFY_RELATED_WINDOWS_LIMITS_CHANGED): + return HandleNotifyAttachedWindowsLimitsChanged(data, reply); case static_cast(SessionInterfaceCode::TRANS_ID_RESTART_APP): return HandleRestartApp(data, reply); case static_cast(SessionInterfaceCode::TRANS_ID_SEND_COMMAND_EVENT): @@ -588,6 +586,9 @@ int SessionStub::HandleConnect(MessageParcel& data, MessageParcel& reply) reply.WriteBool(property->GetPcAppInpadSpecificSystemBarInvisible()); reply.WriteBool(property->GetPcAppInpadOrientationLandscape()); reply.WriteBool(property->GetMobileAppInPadLayoutFullScreen()); + reply.WriteBool(property->GetForceSplitEnable()); + HookWindowInfo hookWindowInfo = property->GetHookWindowInfo(); + reply.WriteParcelable(&hookWindowInfo); reply.WriteParcelable(property->GetCompatibleModeProperty()); reply.WriteBool(property->GetUseControlState()); reply.WriteString(property->GetAncoRealBundleName()); @@ -2100,38 +2101,6 @@ int SessionStub::HandleGetAppForceLandscapeConfig(MessageParcel& data, MessagePa return ERR_NONE; } -int SessionStub::HandleGetAppForceLandscapeConfigEnable(MessageParcel& data, MessageParcel& reply) -{ - TLOGD(WmsLogTag::DEFAULT, "called"); - bool enableForceSplit = false; - WMError ret = GetAppForceLandscapeConfigEnable(enableForceSplit); - if (!reply.WriteBool(enableForceSplit)) { - TLOGE(WmsLogTag::DEFAULT, "write enableForceSplit failed"); - return ERR_INVALID_DATA; - } - if (!reply.WriteInt32(static_cast(ret))) { - TLOGE(WmsLogTag::DEFAULT, "write ret failed"); - return ERR_INVALID_DATA; - } - return ERR_NONE; -} - -int SessionStub::HandleGetAppHookWindowInfoFromServer(MessageParcel& data, MessageParcel& reply) -{ - TLOGD(WmsLogTag::WMS_LAYOUT, "in"); - HookWindowInfo hookWindowInfo{}; - WMError ret = GetAppHookWindowInfoFromServer(hookWindowInfo); - if (!reply.WriteParcelable(&hookWindowInfo)) { - TLOGE(WmsLogTag::WMS_LAYOUT, "write hookWindowInfo failed"); - return ERR_INVALID_DATA; - } - if (!reply.WriteInt32(static_cast(ret))) { - TLOGE(WmsLogTag::WMS_LAYOUT, "write ret failed"); - return ERR_INVALID_DATA; - } - return ERR_NONE; -} - int SessionStub::HandleGetSelectMode(MessageParcel& data, MessageParcel& reply) { TLOGD(WmsLogTag::WMS_LAYOUT, "in"); @@ -2840,6 +2809,22 @@ int SessionStub::HandleNotifySplitRatioChanged(MessageParcel& data, MessageParce return ERR_NONE; } +/** @note @window.layout */ +int SessionStub::HandleNotifyAttachedWindowsLimitsChanged(MessageParcel& data, MessageParcel& reply) +{ + auto newLimits = std::shared_ptr(WindowLimits::Unmarshalling(data)); + if (newLimits == nullptr) { + TLOGE(WmsLogTag::WMS_LAYOUT, "Read newLimits failed"); + return ERR_INVALID_DATA; + } + WSError errCode = NotifyAttachedWindowsLimitsChanged(*newLimits); + if (!reply.WriteInt32(static_cast(errCode))) { + TLOGE(WmsLogTag::WMS_LAYOUT, "write errCode fail"); + return ERR_INVALID_DATA; + } + return ERR_NONE; +} + int SessionStub::HandleNotifyCompatibleModeChange(MessageParcel& data, MessageParcel& reply) { int32_t mode = 0; @@ -2876,23 +2861,6 @@ int SessionStub::HandleNotifyPageEnable(MessageParcel& data, MessageParcel& repl return ERR_NONE; } -int SessionStub::HandleNotifyAppForceLandscapeConfigEnableUpdated(MessageParcel& data, MessageParcel& reply) -{ - TLOGD(WmsLogTag::WMS_COMPAT, "in"); - bool needUpdateViewport = false; - if (!data.ReadBool(needUpdateViewport)) { - TLOGE(WmsLogTag::WMS_COMPAT, "read needUpdateViewport failed"); - return ERR_INVALID_DATA; - } - uint32_t selectModeValue = 0; - if (!data.ReadUint32(selectModeValue)) { - TLOGE(WmsLogTag::WMS_COMPAT, "read selectModeValue failed"); - return ERR_INVALID_DATA; - } - NotifyAppForceLandscapeConfigEnableUpdated(needUpdateViewport, static_cast(selectModeValue)); - return ERR_NONE; -} - int SessionStub::HandleRestartApp(MessageParcel& data, MessageParcel& reply) { TLOGD(WmsLogTag::WMS_LIFE, "in"); diff --git a/window_scene/session/libscene_session.map b/window_scene/session/libscene_session.map index aaca06ca23..e6e0230492 100644 --- a/window_scene/session/libscene_session.map +++ b/window_scene/session/libscene_session.map @@ -154,8 +154,8 @@ OHOS::Rosen::SceneSession::UpdateNormalModalUIExtension*; OHOS::Rosen::SceneSession::UpdatePrivacyModeControlInfo*; OHOS::Rosen::SceneSession::MaskSupportEnterWaterfallMode*; - OHOS::Rosen::SceneSession::RegisterAppHookWindowInfoFunc*; OHOS::Rosen::SceneSession::RegisterSelectModeFunc*; + OHOS::Rosen::SceneSession::RegisterSetSelectModeCallback*; OHOS::Rosen::SceneSession::RegisterGetIsDockAutoHideFunc*; OHOS::Rosen::SceneSession::ConfigDockAutoHide*; OHOS::Rosen::SceneSession::RemoveFingerPointerDownStatus*; diff --git a/window_scene/session/libscreen_session.map b/window_scene/session/libscreen_session.map index 0b30a3ba41..582370fd8d 100644 --- a/window_scene/session/libscreen_session.map +++ b/window_scene/session/libscreen_session.map @@ -91,6 +91,11 @@ OHOS::Rosen::ScreenProperty::UpdateScreenRotation*; OHOS::Rosen::ScreenProperty::SetRogScreenResolution*; OHOS::Rosen::ScreenProperty::UpdateVirtualPixelRatio*; + OHOS::Rosen::ScreenProperty::GetScreenRotation*; + OHOS::Rosen::ScreenProperty::GetDisplayMode*; + OHOS::Rosen::ScreenProperty::SetDisplayOrientation*; + OHOS::Rosen::ScreenProperty::SetDeviceOrientation*; + OHOS::Rosen::ScreenProperty::GetDeviceOrientation*; OHOS::Rosen::ScreenSession::AddHdrFormats*; OHOS::Rosen::ScreenSession::AddRotationCorrection*; OHOS::Rosen::ScreenSession::BeforeScreenPropertyChange*; diff --git a/window_scene/session_manager/BUILD.gn b/window_scene/session_manager/BUILD.gn index f203e5fe04..250ba31eab 100644 --- a/window_scene/session_manager/BUILD.gn +++ b/window_scene/session_manager/BUILD.gn @@ -236,6 +236,7 @@ ohos_shared_library("scene_session_manager") { sources = scene_session_manager_sources deps = scene_session_manager_deps external_deps = scene_session_manager_external_deps + ldflags = [ "-Wl,-Bsymbolic-functions" ] public_external_deps = [ "ability_runtime:session_handler", @@ -311,9 +312,9 @@ ohos_shared_library("scene_session_manager") { } session_manager_sources = [ - "../session_manager_service/src/session_manager_service_proxy.cpp", - "src/session_manager.cpp", - "src/zidl/scene_session_manager_proxy.cpp", + "../session_manager_service/src/session_manager_service_proxy.cpp", + "src/session_manager.cpp", + "src/zidl/scene_session_manager_proxy.cpp", "src/zidl/session_lifecycle_listener_proxy.cpp", ] @@ -416,6 +417,8 @@ ohos_shared_library("session_manager") { "samgr:samgr_proxy", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] + innerapi_tags = [ "platformsdk_indirect" ] part_name = "window_manager" subsystem_name = "window" @@ -468,6 +471,8 @@ ohos_shared_library("session_manager_lite") { "samgr:samgr_proxy", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] + innerapi_tags = [ "platformsdk_indirect" ] part_name = "window_manager" subsystem_name = "window" diff --git a/window_scene/session_manager/include/scene_session_manager.h b/window_scene/session_manager/include/scene_session_manager.h index 6ae52b8c21..1687f60cb6 100644 --- a/window_scene/session_manager/include/scene_session_manager.h +++ b/window_scene/session_manager/include/scene_session_manager.h @@ -658,8 +658,6 @@ public: void SetHasRootSceneRequestedVsyncFunc(HasRootSceneRequestedVsyncFunc&& func); void SetRequestVsyncByRootSceneWhenModeChangeFunc(RequestVsyncByRootSceneWhenModeChangeFunc&& func); WMError UpdateWindowModeByIdForUITest(int32_t windowId, int32_t updateMode) override; - WMError UpdateAppHookWindowInfo(const std::string& bundleName, const HookWindowInfo& hookWindowInfo); - HookWindowInfo GetAppHookWindowInfo(const std::string& bundleName); void UpdateAppHookWindowInfoWhenSwitchFreeMultiWindow(bool isOpenFreeMultiWindow); void UpdateRsCmdBlockingCount(bool enable); int32_t GetOrResetRsCmdBlockingCount(); @@ -724,10 +722,7 @@ public: void UpdateSecSurfaceInfo(std::shared_ptr secExtensionData, uint64_t userId); void UpdateConstrainedModalUIExtInfo(std::shared_ptr constrainedModalUIExtData, uint64_t userId); WSError SetAppForceLandscapeConfig(const std::string& bundleName, AppForceLandscapeConfig& config); - WSError SetAppForceLandscapeConfigEnable(const std::string& bundleName, bool enableForceLandscape, - bool needUpdateViewport, SelectMode selectMode); AppForceLandscapeConfig GetAppForceLandscapeConfig(const std::string& bundleName); - bool GetAppForceLandscapeConfigEnable(const std::string& bundleName); WMError GetWindowStyleType(WindowStyleType& windowStyletype) override; WMError GetProcessSurfaceNodeIdByPersistentId(const int32_t pid, const std::vector& persistentIds, std::vector& surfaceNodeIds) override; @@ -1746,8 +1741,6 @@ private: bool singleHandModeEnable_ = true; SingleHandBackgroundLayoutConfig singleHandBackgroundLayoutConfig_; std::unordered_set appsWithDeduplicatedWindowStatus_; - std::shared_mutex appHookWindowInfoMapMutex_; - std::unordered_map appHookWindowInfoMap_; void InitVsyncStation(); void BindVsyncStation(const sptr& sceneSession); bool GetDisplaySizeById(DisplayId displayId, int32_t& displayWidth, int32_t& displayHeight); @@ -2054,6 +2047,7 @@ private: */ void NotifyIsFullScreenInForceSplitMode(uint32_t uid, bool isFullScreen); std::unordered_set fullScreenInForceSplitUidSet_; + std::shared_mutex fullScreenInForceSplitUidSetMutex_; PageEnableFunc pageEnableFunc_; WSError NotifyPageEnableFunc(const std::string& bundleName, int32_t windowId, const std::string& action, const std::string& message); diff --git a/window_scene/session_manager/libscene_session_manager.map b/window_scene/session_manager/libscene_session_manager.map index ab076c2295..e8279269a7 100644 --- a/window_scene/session_manager/libscene_session_manager.map +++ b/window_scene/session_manager/libscene_session_manager.map @@ -153,7 +153,6 @@ OHOS::Rosen::SceneSessionManager::SetAlivePersistentIds*; OHOS::Rosen::SceneSessionManager::SetAppDragResizeTypeInner*; OHOS::Rosen::SceneSessionManager::SetAppForceLandscapeConfig*; - OHOS::Rosen::SceneSessionManager::SetAppForceLandscapeConfigEnable*; OHOS::Rosen::SceneSessionManager::SetSelectMode*; OHOS::Rosen::SceneSessionManager::GetSelectMode*; OHOS::Rosen::SceneSessionManager::SetBehindWindowFilterEnabled*; @@ -204,7 +203,6 @@ OHOS::Rosen::SceneSessionManager::UpdateAllStartingWindowRdb*; OHOS::Rosen::SceneSessionManager::UpdateAppBoundSystemTrayStatus*; OHOS::Rosen::SceneSessionManager::UpdateAppHookDisplayInfo*; - OHOS::Rosen::SceneSessionManager::UpdateAppHookWindowInfo*; OHOS::Rosen::SceneSessionManager::UpdateAvoidAreaForLSStateChange*; OHOS::Rosen::SceneSessionManager::UpdateDisplayHookInfo*; OHOS::Rosen::SceneSessionManager::UpdateMaximizeMode*; diff --git a/window_scene/session_manager/src/scene_session_manager.cpp b/window_scene/session_manager/src/scene_session_manager.cpp index 1a66de22cd..ec8b76efaf 100644 --- a/window_scene/session_manager/src/scene_session_manager.cpp +++ b/window_scene/session_manager/src/scene_session_manager.cpp @@ -171,8 +171,6 @@ constexpr std::size_t MAX_SNAPSHOT_IN_RECENT_PAD = 0; constexpr std::size_t MAX_SNAPSHOT_IN_RECENT_PHONE = 0; constexpr uint64_t NOTIFY_START_ABILITY_TIMEOUT = 4000; constexpr uint64_t START_UI_ABILITY_TIMEOUT = 5000; -constexpr int32_t FORCE_SPLIT_MODE = 5; -constexpr int32_t NAV_FORCE_SPLIT_MODE = 6; constexpr int32_t SHOWABILITY_SCENARIOS = 0x00000002; const std::string FB_PANEL_NAME = "Fb_panel"; constexpr std::size_t MAX_APP_BOUND_TRAY_MAP_SIZE = 50; @@ -2999,9 +2997,6 @@ sptr SceneSessionManager::CreateSceneSession(const SessionInfo& se sceneSession->RegisterForceSplitListener([this](const std::string& bundleName) { return this->GetAppForceLandscapeConfig(bundleName); }); - sceneSession->RegisterAppHookWindowInfoFunc([this](const std::string& bundleName) { - return this->GetAppHookWindowInfo(bundleName); - }); sceneSession->RegisterSelectModeFunc([this]() { return this->GetSelectMode(); }); @@ -3067,13 +3062,13 @@ sptr SceneSessionManager::CreateSceneSession(const SessionInfo& se }); if (SessionHelper::IsMainWindow(sceneSession->GetWindowType())) { - sceneSession->RegisterForceSplitEnableListener([this](const std::string& bundleName) { - return this->GetAppForceLandscapeConfigEnable(bundleName); - }); sceneSession->RegisterPageEnableCallback([this](const std::string& bundleName, int32_t windowId, const std::string& action, const std::string& message) { return this->NotifyPageEnableFunc(bundleName, windowId, action, message); }); + sceneSession->RegisterSetSelectModeCallback([this](SelectMode selectMode) { + return this->SetSelectMode(selectMode); + }); } DragResizeType dragResizeType = DragResizeType::RESIZE_TYPE_UNDEFINED; GetAppDragResizeType(sessionInfo.bundleName_, dragResizeType); @@ -4317,7 +4312,7 @@ WSError SceneSessionManager::RequestSceneSessionBackground(const sptr(sceneSession->GetSessionInfo().isSystem_)); auto sceneSessionInfo = SetAbilitySessionInfo(sceneSession); auto retCode = AAFwk::AbilityManagerClient::GetInstance()->MinimizeUIAbilityBySCB(sceneSessionInfo, isDelegator, - static_cast(WindowStateChangeReason::ABILITY_CALL)); + static_cast(WindowStateChangeReason::ABILITY_CALL), static_cast(reason)); if (retCode != ERR_OK) { RecordLifeCycleExceptionEvent(sceneSession, retCode, WSErrorReason::WS_REASON_WINDOW_MINIMIZE_ERR, "minimize ability failed"); @@ -6574,7 +6569,8 @@ void SceneSessionManager::UpdateAllStartingWindowRdb() static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_DISABLE) | static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_HAP_MODULE) | static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_ABILITY) | - static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_ONLY_WITH_LAUNCHER_ABILITY), + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_ONLY_WITH_LAUNCHER_ABILITY) | + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_EXCLUDE_EXT), bundleInfos, currentUserId_)); if (ret != 0) { TLOGNE(WmsLogTag::WMS_PATTERN, "%{public}s GetBundleInfosV9 error:%{public}d", where, ret); @@ -6678,7 +6674,8 @@ void SceneSessionManager::GetStartupPage(const SessionInfo& sessionInfo, Startin } } else { if (!bundleMgr_->QueryAbilityInfo( - want, AppExecFwk::GET_ABILITY_INFO_DEFAULT, AppExecFwk::Constants::ANY_USERID, abilityInfo)) { + want, AppExecFwk::GET_ABILITY_INFO_DEFAULT | AppExecFwk::GET_ABILITY_INFO_EXCLUDE_EXT, + AppExecFwk::Constants::ANY_USERID, abilityInfo)) { TLOGE(WmsLogTag::WMS_PATTERN, "Get ability info from BMS failed!"); return; } @@ -6822,10 +6819,6 @@ void SceneSessionManager::PreLoadStartingWindow(sptr sceneSession) TLOGD(WmsLogTag::WMS_PATTERN, "not supported"); return; } - if (IsSyncLoadStartingWindow()) { - TLOGD(WmsLogTag::WMS_PATTERN, "sync load starting window"); - return; - } const char* const where = __func__; auto loadTask = [this, weakSceneSession = wptr(sceneSession), where]() { HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "ssm:PreLoadStartingWindow"); @@ -6846,6 +6839,10 @@ void SceneSessionManager::PreLoadStartingWindow(sptr sceneSession) sceneSession->PreloadSnapshot(); return; } + if (IsSyncLoadStartingWindow()) { + TLOGND(WmsLogTag::WMS_PATTERN, "sync load starting window"); + return; + } StartingWindowInfo startingWindowInfo; GetStartupPage(sessionInfo, startingWindowInfo); uint32_t resId = 0; @@ -6955,7 +6952,8 @@ void SceneSessionManager::OnBundleUpdated(const std::string& bundleName, int use bool ret = bundleMgr_->GetBundleInfoV9(bundleName, static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_DISABLE) | static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_HAP_MODULE) | - static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_ABILITY), + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_ABILITY) | + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_EXCLUDE_EXT), bundleInfo, currentUserId_); if (ret == 0) { std::vector> inputValues; @@ -13088,6 +13086,7 @@ void SceneSessionManager::SetSessionVisibilityInfo(const sptr& ses windowId, session->GetCallingPid(), session->GetCallingUid(), visibleState, session->GetWindowType()); windowVisibilityInfo->SetAppIndex(session->GetSessionInfo().appIndex_); windowVisibilityInfo->SetBundleName(session->GetSessionInfo().bundleName_); + windowVisibilityInfo->SetModuleName(session->GetSessionInfo().moduleName_); windowVisibilityInfo->SetAbilityName(session->GetSessionInfo().abilityName_); windowVisibilityInfo->SetIsSystem(session->GetSessionInfo().isSystem_); windowVisibilityInfo->SetZOrder(session->GetZOrder()); @@ -13572,6 +13571,7 @@ void SceneSessionManager::WindowDestroyNotifyVisibility(const sptr WINDOW_VISIBILITY_STATE_TOTALLY_OCCUSION, sceneSession->GetWindowType()); windowVisibilityInfo->SetAppIndex(sceneSession->GetSessionInfo().appIndex_); windowVisibilityInfo->SetBundleName(sceneSession->GetSessionInfo().bundleName_); + windowVisibilityInfo->SetModuleName(sceneSession->GetSessionInfo().moduleName_); windowVisibilityInfo->SetAbilityName(sceneSession->GetSessionInfo().abilityName_); windowVisibilityInfo->SetIsSystem(sceneSession->GetSessionInfo().isSystem_); windowVisibilityInfo->SetZOrder(sceneSession->GetZOrder()); @@ -16155,7 +16155,7 @@ WMError SceneSessionManager::GetAllWindowLayoutInfo(DisplayId displayId, if (isVirtualDisplay) { globalScaledRect.posY_ -= GetFoldLowerScreenPosY(); } - HookWindowInfo hookWindowInfo = GetAppHookWindowInfo(session->GetSessionInfo().bundleName_); + HookWindowInfo hookWindowInfo = session->GetSessionProperty()->GetHookWindowInfo(); if (hookWindowInfo.enableHookWindow && !session->IsFullScreenInForceSplit() && WindowHelper::IsMainWindow(session->GetWindowType()) && !MathHelper::NearEqual(hookWindowInfo.widthHookRatio, HookWindowInfo::DEFAULT_WINDOW_SIZE_HOOK_RATIO)) { @@ -16411,6 +16411,7 @@ WMError SceneSessionManager::GetVisibilityWindowInfo(std::vectorGetCallingUid(), session->GetVisibilityState(), session->GetWindowType(), windowStatus, rect, session->GetSessionInfo().bundleName_, session->GetSessionInfo().abilityName_, session->IsFocused()); + windowVisibilityInfo->SetModuleName(session->GetSessionInfo().moduleName_); windowVisibilityInfo->SetAppIndex(session->GetSessionInfo().appIndex_); windowVisibilityInfo->SetIsSystem(session->GetSessionInfo().isSystem_); windowVisibilityInfo->SetZOrder(session->GetZOrder()); @@ -16429,7 +16430,7 @@ WMError SceneSessionManager::GetVisibilityWindowInfo(std::vectorGetWindowId(), displayId); TLOGD(WmsLogTag::WMS_ATTRIBUTE, "%{public}s: wid=%{public}d, globalDisplayRect=%{public}s", where, static_cast(session->GetPersistentId()), globalDisplayRect.ToString().c_str()); - HookWindowInfo hookWindowInfo = GetAppHookWindowInfo(session->GetSessionInfo().bundleName_); + HookWindowInfo hookWindowInfo = session->GetSessionProperty()->GetHookWindowInfo(); if (hookWindowInfo.enableHookWindow && !session->IsFullScreenInForceSplit() && WindowHelper::IsMainWindow(session->GetWindowType()) && !MathHelper::NearEqual(hookWindowInfo.widthHookRatio, HookWindowInfo::DEFAULT_WINDOW_SIZE_HOOK_RATIO)) { @@ -17961,7 +17962,7 @@ WMError SceneSessionManager::UpdateDisplayHookInfo(int32_t uid, uint32_t width, dmHookInfo.displayOrientation_ = 0; dmHookInfo.enableHookDisplayOrientation_ = false; { - std::shared_lock lock(appHookWindowInfoMapMutex_); + std::shared_lock lock(fullScreenInForceSplitUidSetMutex_); dmHookInfo.isFullScreenInForceSplit_ = fullScreenInForceSplitUidSet_.find(uid) != fullScreenInForceSplitUidSet_.end(); } ScreenSessionManagerClient::GetInstance().UpdateDisplayHookInfo(uid, enable, dmHookInfo); @@ -17987,89 +17988,21 @@ WMError SceneSessionManager::UpdateAppHookDisplayInfo(int32_t uid, const HookInf .posX_ = hookInfo.actualRect_.posX_, .posY_ = hookInfo.actualRect_.posY_, .width_ = hookInfo.actualRect_.width_, .height_ = hookInfo.actualRect_.height_}; { - std::shared_lock lock(appHookWindowInfoMapMutex_); + std::shared_lock lock(fullScreenInForceSplitUidSetMutex_); dmHookInfo.isFullScreenInForceSplit_ = fullScreenInForceSplitUidSet_.find(uid) != fullScreenInForceSplitUidSet_.end(); } ScreenSessionManagerClient::GetInstance().UpdateDisplayHookInfo(uid, enable, dmHookInfo); return WMError::WM_OK; } -WMError SceneSessionManager::UpdateAppHookWindowInfo(const std::string& bundleName, - const HookWindowInfo& hookWindowInfo) -{ - if (bundleName.empty()) { - TLOGE(WmsLogTag::WMS_COMPAT, "Bundle name is empty"); - return WMError::WM_ERROR_NULLPTR; - } - if (hookWindowInfo.widthHookRatio < 0.0f) { - TLOGE(WmsLogTag::WMS_COMPAT, "Invalid hook window parameters: widthHookRatio:%{public}f, " - "bundleName:%{public}s", hookWindowInfo.widthHookRatio, bundleName.c_str()); - return WMError::WM_ERROR_INVALID_PARAM; - } - TLOGI(WmsLogTag::WMS_COMPAT, "bundleName:%{public}s, hookWindowInfo:[%{public}s]", bundleName.c_str(), - hookWindowInfo.ToString().c_str()); - - HookWindowInfo preInfo; - { - std::unique_lock lock(appHookWindowInfoMapMutex_); - if (appHookWindowInfoMap_.count(bundleName)) { - preInfo = appHookWindowInfoMap_[bundleName]; - } else { - preInfo = {}; - } - HookWindowInfo newInfo = {}; - newInfo.enableHookWindow = hookWindowInfo.enableHookWindow; - newInfo.widthHookRatio = hookWindowInfo.widthHookRatio; - newInfo.notifyWindowChange = false; - newInfo.drawableRectHook = hookWindowInfo.drawableRectHook; - appHookWindowInfoMap_[bundleName] = newInfo; - } - - if (preInfo.enableHookWindow != hookWindowInfo.enableHookWindow || - !MathHelper::NearZero(preInfo.widthHookRatio - hookWindowInfo.widthHookRatio) || - preInfo.drawableRectHook != hookWindowInfo.drawableRectHook) { - //Notify the client of the info change - std::shared_lock lock(sceneSessionMapMutex_); - for (const auto& [_, session] : sceneSessionMap_) { - if (session && session->GetSessionInfo().bundleName_ == bundleName) { - session->UpdateAppHookWindowInfo(hookWindowInfo); - } - } - } - return WMError::WM_OK; -} - -HookWindowInfo SceneSessionManager::GetAppHookWindowInfo(const std::string& bundleName) -{ - if (bundleName.empty()) { - TLOGW(WmsLogTag::WMS_LAYOUT, "Empty bundle name requested"); - return {}; - } - std::shared_lock lock(appHookWindowInfoMapMutex_); - const auto& it = appHookWindowInfoMap_.find(bundleName); - if (it == appHookWindowInfoMap_.end()) { - TLOGD(WmsLogTag::WMS_LAYOUT, "app: %{public}s, hookWindowInfo not find", bundleName.c_str()); - return {}; - } - return it->second; -} - void SceneSessionManager::UpdateAppHookWindowInfoWhenSwitchFreeMultiWindow(bool isOpenFreeMultiWindow) { - std::unordered_set bundleNames; - { - std::unique_lock lock(appHookWindowInfoMapMutex_); - for (auto& [bundleName, hookWindowInfo] : appHookWindowInfoMap_) { + std::shared_lock lock(sceneSessionMapMutex_); + for (const auto& [_, session] : sceneSessionMap_) { + if (session && SessionHelper::IsMainWindow(session->GetWindowType())) { + auto hookWindowInfo = session->GetSessionProperty()->GetHookWindowInfo(); hookWindowInfo.enableHookWindow = !isOpenFreeMultiWindow; - bundleNames.insert(bundleName); - } - } - { - std::shared_lock lock(sceneSessionMapMutex_); - for (const auto& [_, session] : sceneSessionMap_) { - if (session && bundleNames.count(session->GetSessionInfo().bundleName_)) { - session->NotifyAppHookWindowInfoUpdated(); - } + session->UpdateHookWindowInfo(hookWindowInfo); } } } @@ -18322,71 +18255,12 @@ WSError SceneSessionManager::SetAppForceLandscapeConfig(const std::string& bundl } TLOGI(WmsLogTag::WMS_COMPAT, - "bundleName:%{public}s, config:[mode_%{public}d, supportSplit_%{public}d, ignoreOrientation_%{public}d, " - "containsSysConfig_%{public}d, isSysRouter_%{public}d, sysHomePage_%{public}s, sysConfigJsonStr_%{public}s, " - "containsAppConfig_%{public}d, isAppRouter_%{public}d, appConfigJsonStr_%{public}s]", - bundleName.c_str(), - config.mode_, - config.supportSplit_, - config.ignoreOrientation_, - config.containsSysConfig_, - config.isSysRouter_, - config.sysHomePage_.c_str(), - config.sysConfigJsonStr_.c_str(), - config.containsAppConfig_, - config.isAppRouter_, + "bundleName:%{public}s, config:[containsSysConfig_%{public}d, isSysRouter_%{public}d, sysHomePage_%{public}s, " + "sysConfigJsonStr_%{public}s, containsAppConfig_%{public}d, isAppRouter_%{public}d, " + "appConfigJsonStr_%{public}s]", + bundleName.c_str(), config.containsSysConfig_, config.isSysRouter_, config.sysHomePage_.c_str(), + config.sysConfigJsonStr_.c_str(), config.containsAppConfig_, config.isAppRouter_, config.appConfigJsonStr_.c_str()); - if (preConfig.mode_ == FORCE_SPLIT_MODE || config.mode_ == FORCE_SPLIT_MODE || - preConfig.mode_ == NAV_FORCE_SPLIT_MODE || config.mode_ == NAV_FORCE_SPLIT_MODE) { - //Notify the client of the mode change - std::shared_lock lock(sceneSessionMapMutex_); - for (const auto& iter : sceneSessionMap_) { - auto& session = iter.second; - if (session && session->GetSessionInfo().bundleName_ == bundleName) { - session->NotifyAppForceLandscapeConfigUpdated(); - } - } - } - return WSError::WS_OK; -} - -WSError SceneSessionManager::SetAppForceLandscapeConfigEnable(const std::string& bundleName, - const bool enableForceSplit, bool needUpdateViewport, SelectMode selectMode) -{ - if (bundleName.empty()) { - TLOGE(WmsLogTag::WMS_COMPAT, "bundle name is empty"); - return WSError::WS_ERROR_NULLPTR; - } - - // update selectMode - SetSelectMode(selectMode); - - AppForceLandscapeConfig config; - { - std::unique_lock lock(appForceLandscapeMutex_); - if (appForceLandscapeMap_.count(bundleName)) { - config = appForceLandscapeMap_[bundleName]; - } else { - TLOGI(WmsLogTag::WMS_COMPAT, "app: %{public}s, config not find", bundleName.c_str()); - } - config.configEnable_ = enableForceSplit; - appForceLandscapeMap_[bundleName] = config; - } - std::map> sceneSessionMapCopy; - { - std::shared_lock lock(sceneSessionMapMutex_); - sceneSessionMapCopy = sceneSessionMap_; - } - for (const auto& iter : sceneSessionMapCopy) { - auto& session = iter.second; - if (session && session->GetSessionInfo().bundleName_ == bundleName && - SessionHelper::IsMainWindow(session->GetWindowType())) { - session->NotifyAppForceLandscapeConfigEnableUpdated(needUpdateViewport, selectMode); - } - } - TLOGI(WmsLogTag::WMS_COMPAT, "bundleName:%{public}s, enable:%{public}d, needUpdateViewport:%{public}d, " - "selectMode: %{public}d", bundleName.c_str(), enableForceSplit, needUpdateViewport, - static_cast(selectMode)); return WSError::WS_OK; } @@ -18404,22 +18278,6 @@ AppForceLandscapeConfig SceneSessionManager::GetAppForceLandscapeConfig(const st return appForceLandscapeMap_[bundleName]; } -bool SceneSessionManager::GetAppForceLandscapeConfigEnable(const std::string& bundleName) -{ - if (bundleName.empty()) { - return false; - } - std::shared_lock lock(appForceLandscapeMutex_); - if (appForceLandscapeMap_.empty() || - appForceLandscapeMap_.find(bundleName) == appForceLandscapeMap_.end()) { - TLOGD(WmsLogTag::WMS_COMPAT, "app: %{public}s, config not find", bundleName.c_str()); - return false; - } - TLOGI(WmsLogTag::WMS_COMPAT, "bundleName:%{public}s, enable:%{public}d", - bundleName.c_str(), appForceLandscapeMap_[bundleName].configEnable_); - return appForceLandscapeMap_[bundleName].configEnable_; -} - WMError SceneSessionManager::TerminateSessionByPersistentId(int32_t persistentId) { if (!SessionPermission::VerifyCallingPermission(PermissionConstants::PERMISSION_KILL_APP_PROCESS)) { @@ -20805,10 +20663,10 @@ void SceneSessionManager::DeleteAllOutline(const sptr& remoteObje void SceneSessionManager::NotifyIsFullScreenInForceSplitMode(uint32_t uid, bool isFullScreen) { if (isFullScreen) { - std::unique_lock lock(appHookWindowInfoMapMutex_); + std::unique_lock lock(fullScreenInForceSplitUidSetMutex_); fullScreenInForceSplitUidSet_.insert(uid); } else { - std::unique_lock lock(appHookWindowInfoMapMutex_); + std::unique_lock lock(fullScreenInForceSplitUidSetMutex_); fullScreenInForceSplitUidSet_.erase(uid); } ScreenSessionManagerClient::GetInstance().NotifyIsFullScreenInForceSplitMode(uid, isFullScreen); diff --git a/window_scene/session_manager_service/BUILD.gn b/window_scene/session_manager_service/BUILD.gn index 2fcc33429b..497e483d63 100644 --- a/window_scene/session_manager_service/BUILD.gn +++ b/window_scene/session_manager_service/BUILD.gn @@ -56,6 +56,8 @@ ohos_shared_library("session_manager_service") { "samgr:samgr_proxy", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] + innerapi_tags = [ "platformsdk" ] part_name = "window_manager" subsystem_name = "window" diff --git a/window_scene/test/dms_unittest/dms_test_framework/dms_test_base.h b/window_scene/test/dms_unittest/dms_test_framework/dms_test_base.h index e32337644f..d4a6f2fcf8 100644 --- a/window_scene/test/dms_unittest/dms_test_framework/dms_test_base.h +++ b/window_scene/test/dms_unittest/dms_test_framework/dms_test_base.h @@ -86,6 +86,7 @@ public: MOCK_METHOD(void, OnAnimationFinish, (), (override)); MOCK_METHOD(void, SetInternalClipToBounds, (ScreenId screenId, bool clipToBounds), (override)); MOCK_METHOD(void, OnTentModeChange, (TentMode tentMode), (override)); + MOCK_METHOD(void, OnTransRSEvent, (const sptr& param), (override)); }; class DisplayManagerAgentMock : public IRemoteStub { diff --git a/window_scene/test/dms_unittest/fold_screen_base_controller_test.cpp b/window_scene/test/dms_unittest/fold_screen_base_controller_test.cpp index 6d193155cd..2162650284 100644 --- a/window_scene/test/dms_unittest/fold_screen_base_controller_test.cpp +++ b/window_scene/test/dms_unittest/fold_screen_base_controller_test.cpp @@ -390,6 +390,19 @@ HWTEST_F(FoldScreenBaseControllerTest, GetCurrentDisplayMode, TestSize.Level1) FoldScreenBasePolicy::GetInstance().currentDisplayMode_ = FoldDisplayMode::MAIN; EXPECT_EQ(controller.GetCurrentDisplayMode(), FoldDisplayMode::MAIN); } + +/** + * @tc.name: GetScreenActiveModeRectMap + * @tc.desc: test function : GetScreenActiveModeRectMap + * @tc.type: FUNC + */ +HWTEST_F(FoldScreenBaseControllerTest, GetScreenActiveModeRectMap, TestSize.Level1) +{ + auto controller = FoldScreenBaseController(); + auto screenActiveModeRectMapTemp = FoldScreenBasePolicy::GetInstance().GetScreenActiveModeRectMap(); + auto screenActiveModeRectMap = controller.GetScreenActiveModeRectMap(); + EXPECT_EQ(screenActiveModeRectMapTemp.size(), screenActiveModeRectMap.size()); +} } // namespace } // namespace DMS } // namespace Rosen diff --git a/window_scene/test/dms_unittest/fold_screen_base_policy_test.cpp b/window_scene/test/dms_unittest/fold_screen_base_policy_test.cpp index 3e7db36fe2..766d653d45 100644 --- a/window_scene/test/dms_unittest/fold_screen_base_policy_test.cpp +++ b/window_scene/test/dms_unittest/fold_screen_base_policy_test.cpp @@ -603,6 +603,21 @@ HWTEST_F(FoldScreenBasePolicyTest, GetScreenActiveModeRectMapTest01, TestSize.Le EXPECT_EQ(result[FoldDisplayMode::FULL], rect); FoldScreenBasePolicy::GetInstance().screenActiveModeRectMap_[FoldDisplayMode::FULL] = rectOld; } + +/** + * @tc.name: GetScreenActiveModeRectMap + * @tc.desc: test function : GetScreenActiveModeRectMap + * @tc.type: FUNC + */ +HWTEST_F(FoldScreenBasePolicyTest, GetScreenActiveModeRectMap, TestSize.Level1) +{ + FoldScreenBasePolicy* policy = mockBasePolicy.get(); + policy->screenActiveModeRectMap_.clear(); + RRect bounds = RRect{{0, 0, 100, 100}, 0.0f, 0.0f}; + policy->screenActiveModeRectMap_.insert(std::make_pair(FoldDisplayMode::MAIN, bounds)); + auto map = policy->GetScreenActiveModeRectMap(); + EXPECT_EQ(map.size(), 1); +} } } } // namespace Rosen diff --git a/window_scene/test/dms_unittest/mock/rs/common/rs_event_def.h b/window_scene/test/dms_unittest/mock/rs/common/rs_event_def.h new file mode 100644 index 0000000000..5a0a28a854 --- /dev/null +++ b/window_scene/test/dms_unittest/mock/rs/common/rs_event_def.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RS_EVENT_DEF_H +#define RS_EVENT_DEF_H + +#include + +namespace OHOS { +namespace Rosen { + +/* + * @brief Enumerates events. + * @note If you need to add new event, please coordinate with the downstream + */ +enum HwcEvent { + PREVALIDATE_LOW_TEMP = 0, + PREVALIDATE_DFR_MODE, // Screen refresh mode + PREVALIDATE_DSI_MODE, // Screen interrupt mode + PREVALIDATE_DSTCOLOR_MODE, // Screen color space mode + HWCEVENT_TUI_ENTER, // enter tui layer + HWCEVENT_TUI_EXIT, // exit tui layer + HWCEVENT_EXT_SCREEN_NOT_SUPPORT = 7, // external screen not support + HWCEVENT_CALLBACK_MAX, +}; + +/* + * @brief Enumerates events that need to be exposed to the upstream. + */ +enum class RSExposedEventType : uint32_t { + EXT_SCREEN_UNSUPPORT = 0, + EXPOSED_EVENT_INVALID, +}; + +/* + * @brief Base data structure to the exposed event. + */ +struct RSExposedEventDataBase { + RSExposedEventDataBase() : type_(RSExposedEventType::EXPOSED_EVENT_INVALID) {} + RSExposedEventDataBase(RSExposedEventDataBase&& other) = default; + ~RSExposedEventDataBase() = default; + + RSExposedEventType type_; +}; + +/* + * @brief Unified callback functor to the exposed event + */ +using RSExposedEventCallback = std::function&)>; + +/* + * @brief External screen not-support event. + */ +struct RSExtScreenUnsupportData : RSExposedEventDataBase { + RSExtScreenUnsupportData() {type_ = RSExposedEventType::EXT_SCREEN_UNSUPPORT;} +}; +} // namespace Rosen +} // namespace OHOS +#endif // RS_EVENT_DEF_H \ No newline at end of file diff --git a/window_scene/test/dms_unittest/mock/rs/transaction/mock_rs_interfaces.cpp b/window_scene/test/dms_unittest/mock/rs/transaction/mock_rs_interfaces.cpp index 8c4075ce3e..b0381dbbbe 100644 --- a/window_scene/test/dms_unittest/mock/rs/transaction/mock_rs_interfaces.cpp +++ b/window_scene/test/dms_unittest/mock/rs/transaction/mock_rs_interfaces.cpp @@ -274,5 +274,16 @@ ScreenId RSInterfaces::GetActiveScreenId() { return 0; } + +int32_t RSInterfaces::RegisterExposedEventCallback( + const RSExposedEventType type, const RSExposedEventCallback& callback) +{ + return 0; +} + +int32_t RSInterfaces::UnRegisterExposedEventCallback(const RSExposedEventType type) +{ + return 0; +} } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/dms_unittest/mock/rs/transaction/rs_interfaces.h b/window_scene/test/dms_unittest/mock/rs/transaction/rs_interfaces.h index b61fb2671d..38939caf19 100644 --- a/window_scene/test/dms_unittest/mock/rs/transaction/rs_interfaces.h +++ b/window_scene/test/dms_unittest/mock/rs/transaction/rs_interfaces.h @@ -25,6 +25,7 @@ #include "pixel_map.h" #include "transaction/rs_render_service_client.h" #include "screen_manager/rs_screen_mode_info.h" +#include "common/rs_event_def.h" namespace OHOS { namespace Rosen { @@ -145,6 +146,8 @@ public: int32_t RemoveVirtualScreenWhiteList(ScreenId id, const std::vector& whiteList); int32_t SetLogicalCameraRotationCorrection(ScreenId id, ScreenRotation screenRotation); ScreenId GetActiveScreenId(); + int32_t RegisterExposedEventCallback(const RSExposedEventType type, const RSExposedEventCallback& callback); + int32_t UnRegisterExposedEventCallback(const RSExposedEventType type); }; } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/dms_unittest/screen_session_manager_client_proxy_test.cpp b/window_scene/test/dms_unittest/screen_session_manager_client_proxy_test.cpp index 85b3bf02da..c4baaa6bb0 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_client_proxy_test.cpp +++ b/window_scene/test/dms_unittest/screen_session_manager_client_proxy_test.cpp @@ -734,5 +734,50 @@ HWTEST_F(ScreenSessionManagerClientProxyTest, SetInternalClipToBounds, TestSize. logMsg.clear(); MockMessageParcel::ClearAllErrorFlag(); } + +/** + * @tc.name: OnTransRSEvent + * @tc.desc: OnTransRSEvent test + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientProxyTest, OnTransRSEvent, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + MockMessageParcel::ClearAllErrorFlag(); + + auto proxy = sptr::MakeSptr(nullptr); + sptr rsEvent = new RSExtScreenUnsupportEventData(); + proxy->OnTransRSEvent(rsEvent); + EXPECT_TRUE(logMsg.find("remote is nullptr") != std::string::npos); + logMsg.clear(); + + sptr remoteMocker = sptr::MakeSptr(); + proxy = sptr::MakeSptr(remoteMocker); + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); + ASSERT_NE(proxy, nullptr); + proxy->OnTransRSEvent(rsEvent); + EXPECT_TRUE(logMsg.find("WriteInterfaceToken failed") != std::string::npos); + logMsg.clear(); + + MockMessageParcel::ClearAllErrorFlag(); + MockMessageParcel::SetWriteUint32ErrorFlag(true); + proxy->OnTransRSEvent(rsEvent); + EXPECT_TRUE(logMsg.find("Write event type failed") != std::string::npos); + logMsg.clear(); + + MockMessageParcel::ClearAllErrorFlag(); + remoteMocker->SetRequestResult(ERR_INVALID_DATA); + proxy->OnTransRSEvent(rsEvent); + EXPECT_TRUE(logMsg.find("SendRequest failed") != std::string::npos); + logMsg.clear(); + + MockMessageParcel::ClearAllErrorFlag(); + remoteMocker->SetRequestResult(ERR_NONE); + proxy->OnTransRSEvent(rsEvent); + EXPECT_FALSE(logMsg.find("SendRequest failed") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/dms_unittest/screen_session_manager_client_stub_test.cpp b/window_scene/test/dms_unittest/screen_session_manager_client_stub_test.cpp index b65d8cc200..e83393cf60 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_client_stub_test.cpp +++ b/window_scene/test/dms_unittest/screen_session_manager_client_stub_test.cpp @@ -1134,5 +1134,101 @@ HWTEST_F(ScreenSessionManagerClientStubTest, HandleSetInternalClipToBounds, Test int ret = screenSessionManagerClientStub_->HandleSetInternalClipToBounds(data, reply); EXPECT_EQ(ret, 0); } + +/** + * @tc.name: HandleTransRSEvent + * @tc.desc: HandleTransRSEvent test + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientStubTest, HandleTransRSEvent, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + + data.WriteInterfaceToken(ScreenSessionManagerClientStub::GetDescriptor()); + + // Write event type - EXT_SCREEN_UNSUPPORT + uint32_t eventType = static_cast(RSExposedEventType::EXT_SCREEN_UNSUPPORT); + data.WriteUint32(eventType); + + int ret = screenSessionManagerClientStub_->HandleTransRSEvent(data, reply); + EXPECT_EQ(ret, 0); +} + +/** + * @tc.name: HandleTransRSEvent02 + * @tc.desc: HandleTransRSEvent test with unknown event type + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientStubTest, HandleTransRSEvent02, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + + data.WriteInterfaceToken(ScreenSessionManagerClientStub::GetDescriptor()); + + uint32_t eventType = 999; + data.WriteUint32(eventType); + + int ret = screenSessionManagerClientStub_->HandleTransRSEvent(data, reply); + EXPECT_EQ(ret, 0); +} + +/** + * @tc.name: CreateEventByType + * @tc.desc: CreateEventByType test with EXT_SCREEN_UNSUPPORT + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientStubTest, CreateEventByType, TestSize.Level1) +{ + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr event = screenSessionManagerClientStub_->CreateEventByType(type); + ASSERT_NE(event, nullptr); +} + +/** + * @tc.name: CreateEventByType02 + * @tc.desc: CreateEventByType test with unknown type + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientStubTest, CreateEventByType02, TestSize.Level1) +{ + RSExposedEventType type = static_cast(999); + sptr event = screenSessionManagerClientStub_->CreateEventByType(type); + EXPECT_EQ(event, nullptr); +} + +/** + * @tc.name: ReadRSEventFromParcel + * @tc.desc: ReadRSEventFromParcel test with valid event + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientStubTest, ReadRSEventFromParcel, TestSize.Level1) +{ + MessageParcel data; + + // Write event type - EXT_SCREEN_UNSUPPORT + uint32_t eventType = static_cast(RSExposedEventType::EXT_SCREEN_UNSUPPORT); + data.WriteUint32(eventType); + + sptr event = screenSessionManagerClientStub_->ReadRSEventFromParcel(data); + ASSERT_NE(event, nullptr); +} + +/** + * @tc.name: ReadRSEventFromParcel02 + * @tc.desc: ReadRSEventFromParcel test with unknown event type + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientStubTest, ReadRSEventFromParcel02, TestSize.Level1) +{ + MessageParcel data; + + uint32_t eventType = 999; + data.WriteUint32(eventType); + + sptr event = screenSessionManagerClientStub_->ReadRSEventFromParcel(data); + EXPECT_EQ(event, nullptr); +} } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/dms_unittest/screen_session_manager_client_test.cpp b/window_scene/test/dms_unittest/screen_session_manager_client_test.cpp index 37b263d37c..e490303b73 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_client_test.cpp +++ b/window_scene/test/dms_unittest/screen_session_manager_client_test.cpp @@ -22,6 +22,7 @@ #include "window_manager_hilog.h" #include "scene_board_judgement.h" #include "fold_screen_state_internel.h" +#include "rs_event_data_manager.h" using namespace testing; using namespace testing::ext; @@ -89,6 +90,18 @@ public: ScreenSessionManagerClient* screenSessionManagerClient_; }; +class MockTransRSEventListener : public ITransRSEventListener { +public: + MockTransRSEventListener() : eventReceived_(false) {} + void OnTransRSEvent(const sptr& param) override + { + eventReceived_ = true; + eventData_ = param; + } + bool eventReceived_; + sptr eventData_; +}; + void ScreenSessionManagerClientTest::SetUp() { screenSessionManagerClient_ = &ScreenSessionManagerClient::GetInstance(); @@ -2674,5 +2687,185 @@ HWTEST_F(ScreenSessionManagerClientTest, OnPropertyChanged05, TestSize.Level1) LOG_SetCallback(nullptr); screenSessionManagerClient_->screenSessionMap_.erase(screenId); } + +/** + * @tc.name: RegisterTransRSEventListener01 + * @tc.desc: RegisterTransRSEventListener with null listener + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, RegisterTransRSEventListener01, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = nullptr; + + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener); + EXPECT_TRUE(logMsg.find("Failed to register transRSEvent listener, listener is null") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: RegisterTransRSEventListener02 + * @tc.desc: RegisterTransRSEventListener with duplicate listener + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, RegisterTransRSEventListener02, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = new MockTransRSEventListener(); + + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener); + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener); + EXPECT_TRUE(logMsg.find("Listener already exists") != std::string::npos); + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: UnRegisterTransRSEventListener01 + * @tc.desc: UnRegisterTransRSEventListener with null listener + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, UnRegisterTransRSEventListener01, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = nullptr; + + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener); + EXPECT_TRUE(logMsg.find("listener is null") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: UnRegisterTransRSEventListener02 + * @tc.desc: UnRegisterTransRSEventListener successfully + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, UnRegisterTransRSEventListener02, TestSize.Level1) +{ + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = new MockTransRSEventListener(); + + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener); + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener); + EXPECT_TRUE(listener->eventReceived_ == false); +} + +/** + * @tc.name: UnRegisterTransRSEventListener03 + * @tc.desc: UnRegisterTransRSEventListener with listener not found + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, UnRegisterTransRSEventListener03, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = new MockTransRSEventListener(); + + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener); + EXPECT_TRUE(logMsg.find("No listeners for type") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: UnRegisterTransRSEventListener04 + * @tc.desc: UnRegisterTransRSEventListener removes empty listener list + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, UnRegisterTransRSEventListener04, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = new MockTransRSEventListener(); + + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener); + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener); + EXPECT_TRUE(logMsg.find("Remove empty listener list") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: OnTransRSEvent01 + * @tc.desc: OnTransRSEvent with null data + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, OnTransRSEvent01, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + sptr data = nullptr; + + screenSessionManagerClient_->OnTransRSEvent(data); + EXPECT_TRUE(logMsg.find("data is null") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: OnTransRSEvent02 + * @tc.desc: OnTransRSEvent with no listeners for event type + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, OnTransRSEvent02, TestSize.Level1) +{ + logMsg.clear(); + LOG_SetCallback(MyLogCallback); + sptr data = new RSExtScreenUnsupportEventData(); + + screenSessionManagerClient_->OnTransRSEvent(data); + EXPECT_TRUE(logMsg.find("No listeners for type") != std::string::npos); + logMsg.clear(); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: OnTransRSEvent03 + * @tc.desc: OnTransRSEvent successfully dispatches to listener + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, OnTransRSEvent03, TestSize.Level1) +{ + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener = new MockTransRSEventListener(); + sptr data = new RSExtScreenUnsupportEventData(); + + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener); + screenSessionManagerClient_->OnTransRSEvent(data); + EXPECT_TRUE(listener->eventReceived_); + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener); +} + +/** + * @tc.name: OnTransRSEvent04 + * @tc.desc: OnTransRSEvent dispatches to multiple listeners + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerClientTest, OnTransRSEvent04, TestSize.Level1) +{ + RSExposedEventType type = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + sptr listener1 = new MockTransRSEventListener(); + sptr listener2 = new MockTransRSEventListener(); + sptr data = new RSExtScreenUnsupportEventData(); + + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener1); + screenSessionManagerClient_->RegisterTransRSEventListener(type, listener2); + screenSessionManagerClient_->OnTransRSEvent(data); + EXPECT_TRUE(listener1->eventReceived_); + EXPECT_TRUE(listener2->eventReceived_); + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener1); + screenSessionManagerClient_->UnRegisterTransRSEventListener(type, listener2); +} } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/dms_unittest/screen_session_manager_proxy_test.cpp b/window_scene/test/dms_unittest/screen_session_manager_proxy_test.cpp index bae17ca5a0..9cb385bf7f 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_proxy_test.cpp +++ b/window_scene/test/dms_unittest/screen_session_manager_proxy_test.cpp @@ -2868,5 +2868,108 @@ HWTEST_F(ScreenSessionManagerProxyTest, SetOrientation02, TestSize.Level1) EXPECT_FALSE(logMsg.find("SendRequest failed") != std::string::npos); LOG_SetCallback(nullptr); } + +/** + * @tc.name: IsCapturedByBundleNameList001 + * @tc.desc: IsCapturedByBundleNameList with remote is nullptr + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerProxyTest, IsCapturedByBundleNameList001, TestSize.Level1) +{ + sptr remoteMocker = nullptr; + auto proxy = sptr::MakeSptr(remoteMocker); + std::vector bundleNameList = {"com.test.app"}; + auto ret = proxy->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); +} + +/** + * @tc.name: IsCapturedByBundleNameList002 + * @tc.desc: IsCapturedByBundleNameList with WriteInterfaceToken failed + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerProxyTest, IsCapturedByBundleNameList002, TestSize.Level1) +{ + MockMessageParcel::ClearAllErrorFlag(); + + sptr remoteMocker = sptr::MakeSptr(); + auto proxy = sptr::MakeSptr(remoteMocker); + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); + + std::vector bundleNameList = {"com.test.app"}; + auto ret = proxy->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); + + MockMessageParcel::ClearAllErrorFlag(); +} + +/** + * @tc.name: IsCapturedByBundleNameList003 + * @tc.desc: IsCapturedByBundleNameList with WriteStringVector failed + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerProxyTest, IsCapturedByBundleNameList003, TestSize.Level1) +{ + MockMessageParcel::ClearAllErrorFlag(); + + sptr remoteMocker = sptr::MakeSptr(); + auto proxy = sptr::MakeSptr(remoteMocker); + MockMessageParcel::SetWriteStringVectorErrorFlag(true); + + std::vector bundleNameList = {"com.test.app"}; + auto ret = proxy->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); + + MockMessageParcel::ClearAllErrorFlag(); +} + +/** + * @tc.name: IsCapturedByBundleNameList004 + * @tc.desc: IsCapturedByBundleNameList with SendRequest failed + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerProxyTest, IsCapturedByBundleNameList004, TestSize.Level1) +{ + MockMessageParcel::ClearAllErrorFlag(); + + sptr remoteMocker = sptr::MakeSptr(); + auto proxy = sptr::MakeSptr(remoteMocker); + remoteMocker->SetRequestResult(ERR_INVALID_DATA); + + std::vector bundleNameList = {"com.test.app"}; + auto ret = proxy->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); + + remoteMocker->SetRequestResult(ERR_NONE); + MockMessageParcel::ClearAllErrorFlag(); +} + +/** + * @tc.name: IsCapturedByBundleNameList005 + * @tc.desc: normal + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerProxyTest, IsCapturedByBundleNameList005, TestSize.Level1) +{ + MockMessageParcel::ClearAllErrorFlag(); + + std::vector bundleNameList = {"com.test.app"}; + auto ret = screenSessionManagerProxy->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); +} + +/** + * @tc.name: IsCapturedByBundleNameList006 + * @tc.desc: empty bundleNameList + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerProxyTest, IsCapturedByBundleNameList006, TestSize.Level1) +{ + MockMessageParcel::ClearAllErrorFlag(); + + std::vector bundleNameList; + auto ret = screenSessionManagerProxy->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); +} } } \ No newline at end of file diff --git a/window_scene/test/dms_unittest/screen_session_manager_stub_test.cpp b/window_scene/test/dms_unittest/screen_session_manager_stub_test.cpp index ab62d5850a..02f4f76717 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_stub_test.cpp +++ b/window_scene/test/dms_unittest/screen_session_manager_stub_test.cpp @@ -3826,6 +3826,56 @@ HWTEST_F(ScreenSessionManagerStubTest, SetPowerStateForAodNnormalTest, TestSize. int res = stub_->OnRemoteRequest(code, data, reply, option); EXPECT_EQ(res, 0); } + +/** + * @tc.name: IsCapturedByBundleNameList001 + * @tc.desc: IsCapturedByBundleNameList test normal input + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerStubTest, IsCapturedByBundleNameList001, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + data.WriteInterfaceToken(ScreenSessionManagerStub::GetDescriptor()); + + std::vector bundleNameList = {"com.test.app1", "com.test.app2"}; + data.WriteStringVector(bundleNameList); + + uint32_t code = static_cast( + DisplayManagerMessage::TRANS_ID_DEVICE_IS_CAPTURE_BY_BUNDLE_LIST); + int res = stub_->OnRemoteRequest(code, data, reply, option); + EXPECT_EQ(res, 0); + + bool result = reply.ReadBool(); + EXPECT_EQ(result, false); +} + +/** + * @tc.name: IsCapturedByBundleNameList002 + * @tc.desc: empty bundleNameList + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerStubTest, IsCapturedByBundleNameList002, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + data.WriteInterfaceToken(ScreenSessionManagerStub::GetDescriptor()); + + std::vector bundleNameList; + data.WriteStringVector(bundleNameList); + + uint32_t code = static_cast( + DisplayManagerMessage::TRANS_ID_DEVICE_IS_CAPTURE_BY_BUNDLE_LIST); + int res = stub_->OnRemoteRequest(code, data, reply, option); + EXPECT_EQ(res, 0); + + bool result = reply.ReadBool(); + EXPECT_EQ(result, false); +} } } } \ No newline at end of file diff --git a/window_scene/test/dms_unittest/screen_session_manager_test.cpp b/window_scene/test/dms_unittest/screen_session_manager_test.cpp index 839dc0d2f7..e4ee90994b 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_test.cpp +++ b/window_scene/test/dms_unittest/screen_session_manager_test.cpp @@ -1291,6 +1291,173 @@ HWTEST_F(ScreenSessionManagerTest, HookDisplayInfoByUid04, TestSize.Level1) ssm_->displayHookMap_.erase(uid); ssm_->DestroyVirtualScreen(screenId); } + +/** + * @tc.name: OnTransRSEvent + * @tc.desc: OnTransRSEvent all branches test + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, OnTransRSEvent, TestSize.Level1) +{ + ASSERT_NE(ssm_, nullptr); + + ssm_->OnTransRSEvent(nullptr); + + auto unknownData = std::make_shared(); + unknownData->type_ = static_cast(999); + ssm_->OnTransRSEvent(unknownData); + + auto validData = std::make_shared(); + validData->type_ = RSExposedEventType::EXT_SCREEN_UNSUPPORT; + auto originalProxy = ssm_->clientProxy_; + ssm_->clientProxy_ = nullptr; + ssm_->OnTransRSEvent(validData); + + ssm_->clientProxy_ = originalProxy; + ssm_->OnTransRSEvent(validData); +} + +/** + * @tc.name: IsCapturedByBundleNameList001 + * @tc.desc: The package name in bundleNameList matches the virtual screen. + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, IsCapturedByBundleNameList001, TestSize.Level1) +{ + sptr displayManagerAgent = new DisplayManagerAgentDefault(); + VirtualScreenOption virtualOption; + virtualOption.name_ = "TestVirtualScreen"; + virtualOption.bundleName_ = "com.test.recorder"; + virtualOption.caller_ = VirtualScreenCaller::NATIVE_SCREEN_MANAGER; + ScreenId screenId = ssm_->CreateVirtualScreen(virtualOption, displayManagerAgent->AsObject()); + + std::vector bundleNameList = {"com.test.recorder"}; + bool ret = ssm_->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, true); + + ssm_->DestroyVirtualScreen(screenId); +} + +/** + * @tc.name: IsCapturedByBundleNameList002 + * @tc.desc: The package name in bundleNameList does not match the virtual screen. + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, IsCapturedByBundleNameList002, TestSize.Level1) +{ + // 1. 创建虚拟屏,设置 bundleName + sptr displayManagerAgent = new DisplayManagerAgentDefault(); + VirtualScreenOption virtualOption; + virtualOption.name_ = "TestVirtualScreen"; + virtualOption.bundleName_ = "com.test.recorder"; + virtualOption.caller_ = VirtualScreenCaller::NATIVE_SCREEN_MANAGER; + ScreenId screenId = ssm_->CreateVirtualScreen(virtualOption, displayManagerAgent->AsObject()); + + std::vector bundleNameList = {"com.other.app"}; + bool ret = ssm_->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, false); + + ssm_->DestroyVirtualScreen(screenId); +} + +/** + * @tc.name: IsCapturedByBundleNameList003 + * @tc.desc: The bundleNameList contains multiple package names. + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, IsCapturedByBundleNameList003, TestSize.Level1) +{ + sptr agent = new DisplayManagerAgentDefault(); + VirtualScreenOption option; + option.name_ = "VirtualScreen"; + option.bundleName_ = "com.target.app"; + option.caller_ = VirtualScreenCaller::NATIVE_SCREEN_MANAGER; + ScreenId screenId = ssm_->CreateVirtualScreen(option, agent->AsObject()); + + std::vector bundleNameList = {"com.app1", "com.target.app", "com.app3"}; + bool ret = ssm_->IsCapturedByBundleNameList(bundleNameList); + EXPECT_EQ(ret, true); + + ssm_->DestroyVirtualScreen(screenId); +} + +/** + * @tc.name: SetOptionConfig_BundleName001 + * @tc.desc: caller is NATIVE_SCREEN_MANAGEG + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, SetOptionConfig_BundleName001, TestSize.Level1) +{ + ASSERT_NE(ssm_, nullptr); + sptr displayManagerAgent = new DisplayManagerAgentDefault(); + VirtualScreenOption virtualOption; + virtualOption.name_ = "testVirtualScreen"; + auto screenId = ssm_->CreateVirtualScreen(virtualOption, displayManagerAgent->AsObject()); + + VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::NATIVE_SCREEN_MANAGER; + option.bundleName_ = "com.test.native"; + + ssm_->SetOptionConfig(screenId, option); + sptr screenSession = ssm_->GetScreenSession(screenId); + EXPECT_EQ(screenSession->GetBundleName(), "com.test.native"); + + ssm_->DestroyVirtualScreen(screenId); +} + +/** + * @tc.name: SetOptionConfig_BundleName002 + * @tc.desc: caller is JS_DISPLAY_MANAGER + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, SetOptionConfig_BundleName002, TestSize.Level1) +{ + g_logMsg.clear(); + LOG_SetCallback(MyLogCallback); + ASSERT_NE(ssm_, nullptr); + sptr displayManagerAgent = new DisplayManagerAgentDefault(); + VirtualScreenOption virtualOption; + virtualOption.name_ = "testVirtualScreen"; + auto screenId = ssm_->CreateVirtualScreen(virtualOption, displayManagerAgent->AsObject()); + + VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::JS_DISPLAY_MANAGER; + option.bundleName_ = ""; + + ssm_->SetOptionConfig(screenId, option); + sptr screenSession = ssm_->GetScreenSession(screenId); + EXPECT_TRUE(g_logMsg.find("bundleInfo null") != std::string::npos); + + LOG_SetCallback(nullptr); + ssm_->DestroyVirtualScreen(screenId); +} + +/** + * @tc.name: SetOptionConfig_BundleName003 + * @tc.desc: caller is UNKNOWN + * @tc.type: FUNC + */ +HWTEST_F(ScreenSessionManagerTest, SetOptionConfig_BundleName003, TestSize.Level1) +{ + g_logMsg.clear(); + LOG_SetCallback(MyLogCallback); + ASSERT_NE(ssm_, nullptr); + sptr displayManagerAgent = new DisplayManagerAgentDefault(); + VirtualScreenOption virtualOption; + virtualOption.name_ = "testVirtualScreen"; + auto screenId = ssm_->CreateVirtualScreen(virtualOption, displayManagerAgent->AsObject()); + + VirtualScreenOption option; + option.caller_ = VirtualScreenCaller::UNKNOWN; + option.bundleName_ = ""; + + ssm_->SetOptionConfig(screenId, option); + sptr screenSession = ssm_->GetScreenSession(screenId); + EXPECT_TRUE(g_logMsg.find("bundleInfo null") != std::string::npos); + + LOG_SetCallback(nullptr); + ssm_->DestroyVirtualScreen(screenId); +} } } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/dms_unittest/screen_session_manager_test/BUILD.gn b/window_scene/test/dms_unittest/screen_session_manager_test/BUILD.gn index 88e6a07ef2..329f6fab65 100644 --- a/window_scene/test/dms_unittest/screen_session_manager_test/BUILD.gn +++ b/window_scene/test/dms_unittest/screen_session_manager_test/BUILD.gn @@ -78,6 +78,7 @@ test_source_common = [ "${window_base_path}/window_scene/screen_session_manager/src/multi_screen_power_change_manager.cpp", "${window_base_path}/window_scene/screen_session_manager/src/multi_screen_mode_change_manager.cpp", "${window_base_path}/window_scene/screen_session_manager/src/publish/screen_session_publish.cpp", + "${window_base_path}/window_scene/screen_session_manager/src/rs_event_data_manager.cpp", "${window_base_path}/window_scene/screen_session_manager/src/screen_aod_plugin.cpp", "${window_base_path}/window_scene/screen_session_manager/src/screen_cutout_controller.cpp", "${window_base_path}/window_scene/screen_session_manager/src/screen_edid_parse.cpp", diff --git a/window_scene/test/dms_unittest/test_client.h b/window_scene/test/dms_unittest/test_client.h index a8c4d142a7..4b885f1eec 100644 --- a/window_scene/test/dms_unittest/test_client.h +++ b/window_scene/test/dms_unittest/test_client.h @@ -69,6 +69,7 @@ public: void SetInternalClipToBounds(ScreenId screenId, bool clipToBounds) override {}; sptr AsObject() override {return testPtr;}; void OnTentModeChange(TentMode tentMode) override {}; + void OnTransRSEvent(const sptr& param) override {}; sptr testPtr; }; } diff --git a/window_scene/test/mock/mock_message_parcel.cpp b/window_scene/test/mock/mock_message_parcel.cpp index 41e9ce1a53..6ed61a3a13 100644 --- a/window_scene/test/mock/mock_message_parcel.cpp +++ b/window_scene/test/mock/mock_message_parcel.cpp @@ -40,6 +40,7 @@ bool g_setReadFloatErrorFlag = false; bool g_setWriteUint64VectorErrorFlag = false; bool g_setReadStringVectorErrorFlag = false; bool g_setReadStringErrorFlag = false; +bool g_setWriteStringVectorErrorFlag = false; std::vector g_int32Cache; int32_t g_WriteInt32ErrorCount = 0; int32_t g_WriteBoolErrorCount = 0; @@ -79,6 +80,7 @@ void MockMessageParcel::ClearAllErrorFlag() g_setReadStringVectorErrorFlag = false; g_setWriteUint64VectorErrorFlag = false; g_setReadStringErrorFlag = false; + g_setWriteStringVectorErrorFlag = false; } void MockMessageParcel::SetWriteBoolErrorFlag(bool flag) @@ -181,6 +183,11 @@ void MockMessageParcel::SetReadStringErrorFlag(bool flag) g_setReadStringErrorFlag = flag; } +void MockMessageParcel::SetWriteStringVectorErrorFlag(bool flag) +{ + g_setWriteStringVectorErrorFlag = flag; +} + void MockMessageParcel::SetWriteUint64VectorErrorFlag(bool flag) { g_setWriteUint64VectorErrorFlag = flag; @@ -394,6 +401,14 @@ bool Parcel::WriteStringVector(const std::vector& val) } return true; } +#else +bool Parcel::WriteStringVector(const std::vector& val) +{ + if (g_setWriteStringVectorErrorFlag) { + return false; + } + return true; +} #endif bool Parcel::ReadStringVector(std::vector* val) diff --git a/window_scene/test/mock/mock_message_parcel.h b/window_scene/test/mock/mock_message_parcel.h index 38456e196d..9af4a9a804 100644 --- a/window_scene/test/mock/mock_message_parcel.h +++ b/window_scene/test/mock/mock_message_parcel.h @@ -43,6 +43,7 @@ public: static void SetReadFloatErrorFlag(bool flag); static void SetReadStringVectorErrorFlag(bool flag); static void SetReadStringErrorFlag(bool flag); + static void SetWriteStringVectorErrorFlag(bool flag); static void SetWriteInt32ErrorCount(int count); static void SetWriteBoolErrorCount(int count); static void SetWriteParcelableErrorCount(int count); diff --git a/window_scene/test/mock/mock_session.h b/window_scene/test/mock/mock_session.h index 731dcd13c4..e27ad3be14 100644 --- a/window_scene/test/mock/mock_session.h +++ b/window_scene/test/mock/mock_session.h @@ -56,8 +56,8 @@ public: MOCK_METHOD1(OnNeedAvoid, WSError(bool status)); MOCK_METHOD1(SetGlobalMaximizeMode, WSError(MaximizeMode mode)); MOCK_METHOD1(NotifyExtensionTimeout, void(int32_t errorCode)); + MOCK_METHOD1(NotifyAttachedWindowsLimitsChanged, WSError(const WindowLimits& limits)); MOCK_METHOD1(GetAppForceLandscapeConfig, WMError(AppForceLandscapeConfig& config)); - MOCK_METHOD1(GetAppHookWindowInfoFromServer, WMError(HookWindowInfo& hookWindowInfo)); MOCK_METHOD1(SetDialogSessionBackGestureEnabled, WSError(bool isEnabled)); MOCK_METHOD1(SetActive, WSError(bool active)); MOCK_METHOD1(SyncSessionEvent, WSError(SessionEvent event)); @@ -76,6 +76,7 @@ public: MOCK_METHOD0(NotifyFloatViewPrepareClose, void(void)); MOCK_METHOD1(UpdateFloatView, WMError(const FloatViewTemplateInfo& fvTemplateInfo)); MOCK_METHOD1(RestoreFloatViewMainWindow, WMError(const std::shared_ptr& wantParams)); + MOCK_METHOD1(GetSelectMode, WMError(SelectMode& selectMode)); }; } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/mock/mock_session_stage.h b/window_scene/test/mock/mock_session_stage.h index a07ab66a53..051e2ec4c9 100644 --- a/window_scene/test/mock/mock_session_stage.h +++ b/window_scene/test/mock/mock_session_stage.h @@ -63,6 +63,13 @@ public: MOCK_METHOD2(NotifyDensityFollowHost, WSError(bool isFollowHost, float densityValue)); MOCK_METHOD1(NotifyWindowVisibility, WSError(bool isVisible)); MOCK_METHOD1(NotifyWindowOcclusionState, WSError(const WindowVisibilityState state)); + MOCK_METHOD4(UpdateAttachedWindowLimits, WSError(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, + bool isIntersectedWidthLimit)); + MOCK_METHOD1(RemoveAttachedWindowLimits, WSError(int32_t sourcePersistentId)); + MOCK_METHOD2(SyncAllAttachedLimitsToChild, WSError( + const std::vector>& limitsList, + const std::vector>& optionsList)); MOCK_METHOD1(NotifyTransformChange, void(const Transform& transform)); MOCK_METHOD1(NotifySingleHandTransformChange, void(const SingleHandTransform& singleHandTransform)); MOCK_METHOD(void, NotifyGlobalScaledRectChange, (const Rect& globalScaledRect), (override)); @@ -101,8 +108,6 @@ public: MOCK_METHOD1(NotifyAppUseControlStatus, void(bool isUseControl)); MOCK_METHOD1(NotifyExtensionSecureLimitChange, WSError(bool isLimit)); MOCK_METHOD0(NotifyAppForceLandscapeConfigUpdated, WSError(void)); - MOCK_METHOD2(NotifyAppForceLandscapeConfigEnableUpdated, WSError(bool needUpdateViewport, SelectMode selectMode)); - MOCK_METHOD0(NotifyAppHookWindowInfoUpdated, WSError(void)); MOCK_METHOD1(GetRouterStackInfo, WMError(std::string& routerStackInfo)); MOCK_METHOD1(SendFbActionEvent, WSError(const std::string& action)); MOCK_METHOD1(UpdateIsShowDecorInFreeMultiWindow, WSError(bool isShow)); @@ -112,6 +117,8 @@ public: MOCK_METHOD1(UpdateWindowUIType, WSError(WindowUIType windowUIType)); MOCK_METHOD1(UpdatePropertyWhenTriggerMode, WSError(const sptr& property)); MOCK_METHOD1(UpdateAppHookWindowInfo, WSError(const HookWindowInfo& hookWindowInfo)); + MOCK_METHOD3(SetForceSplitEnable, WSError(bool isForceSplitEnabled, bool needUpdateViewport, + SelectMode selectMode)); MOCK_METHOD2(SendFvActionEvent, WSError(const std::string& action, const std::string& reason)); MOCK_METHOD2(SyncFvWindowInfo, WSError(const FloatViewWindowInfo& windowInfo, const std::string& reason)); MOCK_METHOD1(SyncFvLimits, WSError(const FloatViewLimits& limits)); diff --git a/window_scene/test/mock/mock_session_stub.h b/window_scene/test/mock/mock_session_stub.h index eb49fb690a..f58614de4f 100644 --- a/window_scene/test/mock/mock_session_stub.h +++ b/window_scene/test/mock/mock_session_stub.h @@ -75,7 +75,6 @@ public: MOCK_METHOD2(HandleNotifyExtensionTimeout, int(MessageParcel& data, MessageParcel& reply)); MOCK_METHOD2(HandleGetStatusBarHeight, int(MessageParcel& data, MessageParcel& reply)); MOCK_METHOD2(HandleGetAppForceLandscapeConfig, int(MessageParcel& data, MessageParcel& reply)); - MOCK_METHOD2(HandleGetAppHookWindowInfoFromServer, int(MessageParcel& data, MessageParcel& reply)); MOCK_METHOD2(HandleNotifySecureLimitChange, int(MessageParcel& data, MessageParcel& reply)); MOCK_METHOD2(HandleGetAllAvoidAreas, int(MessageParcel& data, MessageParcel& reply)); MOCK_METHOD3(GetAvoidAreaByType, AvoidArea(AvoidAreaType type, const WSRect& rect, int32_t apiVersion)); diff --git a/window_scene/test/unittest/BUILD.gn b/window_scene/test/unittest/BUILD.gn index f343458243..20815299a9 100755 --- a/window_scene/test/unittest/BUILD.gn +++ b/window_scene/test/unittest/BUILD.gn @@ -106,6 +106,7 @@ group("unittest") { ":ws_session_manager_service_recover_proxy_test", ":ws_session_manager_test", ":ws_session_proxy_immersive_test", + ":ws_session_proxy_layout_test", ":ws_session_proxy_lifecycle_test", ":ws_session_proxy_mock_test", ":ws_session_proxy_property_test", @@ -113,6 +114,7 @@ group("unittest") { ":ws_session_specific_window_test", ":ws_session_stage_proxy_lifecycle_test", ":ws_session_stage_proxy_test", + ":ws_session_stage_stub_layout_test", ":ws_session_stage_stub_lifecycle_test", ":ws_session_stage_stub_test", ":ws_session_stub_immersive_test", @@ -574,6 +576,16 @@ ohos_unittest("ws_session_stage_proxy_lifecycle_test") { external_deps += [ "ability_base:base" ] } +ohos_unittest("ws_session_stage_stub_layout_test") { + module_out_path = module_out_path + + sources = [ "session_stage_stub_layout_test.cpp" ] + + deps = [ ":ws_unittest_common" ] + + external_deps = test_external_deps +} + ohos_unittest("ws_session_stage_stub_lifecycle_test") { module_out_path = module_out_path @@ -1189,6 +1201,34 @@ ohos_unittest("ws_session_proxy_test") { ] } +ohos_unittest("ws_session_proxy_layout_test") { + module_out_path = module_out_path + + include_dirs = [ + "${window_base_path}/window_scene/session/host/include/zidl", + "${window_base_path}/window_scene/test/mock", + ] + sources = [ + "${window_base_path}/window_scene/test/mock/mock_message_parcel.cpp", + "session_proxy_layout_test.cpp", + ] + + defines = [ + "ENABLE_MOCK_READ_UINT32", + "ENABLE_MOCK_READ_INT32", + ] + + deps = [ + ":ws_unittest_common", + ] + + external_deps = test_external_deps + external_deps += [ + "ability_base:session_info", + "ability_base:want", + ] +} + ohos_unittest("ws_session_proxy_lifecycle_test") { module_out_path = module_out_path diff --git a/window_scene/test/unittest/layout/main_session_layout_test.cpp b/window_scene/test/unittest/layout/main_session_layout_test.cpp index d5c2f1c857..c621af9086 100644 --- a/window_scene/test/unittest/layout/main_session_layout_test.cpp +++ b/window_scene/test/unittest/layout/main_session_layout_test.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -23,6 +24,7 @@ #include "session/screen/include/screen_session.h" #include "window_helper.h" #include "window_manager_hilog.h" +#include "test/mock/mock_session_stage.h" using namespace testing; using namespace testing::ext; @@ -38,6 +40,43 @@ public: private: RSSurfaceNode::SharedPtr CreateRSSurfaceNode(); + +protected: + // Helper function to create SessionInfo with test name + SessionInfo CreateSessionInfo(const std::string& name) const + { + SessionInfo info; + info.bundleName_ = name; + info.moduleName_ = name; + info.abilityName_ = name; + return info; + } + + // Helper function to create attached WindowAnchorInfo + WindowAnchorInfo CreateAttachInfo(bool heightLimit = true, bool widthLimit = true) const + { + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.isFromAttachOrDetach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = heightLimit; + anchorInfo.attachOptions.isIntersectedWidthLimit = widthLimit; + return anchorInfo; + } + + // Helper function to create sub session with attach info and mock stage + struct SubSessionWithMock { + sptr session; + sptr mockStage; + }; + SubSessionWithMock CreateSubSessionWithMock(const std::string& name) + { + SubSessionWithMock result; + result.session = sptr::MakeSptr(CreateSessionInfo(name), nullptr); + result.mockStage = sptr::MakeSptr(); + result.session->sessionStage_ = result.mockStage; + result.session->SetWindowAnchorInfo(CreateAttachInfo()); + return result; + } }; void MainSessionLayoutTest::SetUpTestCase() {} @@ -164,6 +203,594 @@ HWTEST_F(MainSessionLayoutTest, HandleSubSessionSurfaceNodeByWindowAnchor, TestS mainSession->HandleSubSessionSurfaceNodeByWindowAnchor(SizeChangeReason::DRAG_END, 0); EXPECT_EQ(subSession->cloneNodeCountDuringCross_, 1); } + +/** + * @tc.name: RequestUpdateAttachedWindowLimits01 + * @tc.desc: Test main window updates own limits and propagates to children + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimits01, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestUpdateAttachedWindowLimits01"; + info.moduleName_ = "RequestUpdateAttachedWindowLimits01"; + info.abilityName_ = "RequestUpdateAttachedWindowLimits01"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 1001; + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + bool isIntersectedHeightLimit = true; + bool isIntersectedWidthLimit = true; + + // Test with null sessionStage_ + mainSession->sessionStage_ = nullptr; + WSError ret = mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, ret); + + // Test with valid sessionStage_ + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, isIntersectedHeightLimit, isIntersectedWidthLimit)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + ret = mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimits02 + * @tc.desc: Test main window propagates to attached children only + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimits02, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestUpdateAttachedWindowLimits02"; + info.moduleName_ = "RequestUpdateAttachedWindowLimits02"; + info.abilityName_ = "RequestUpdateAttachedWindowLimits02"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + sptr subSession1 = sptr::MakeSptr(info, nullptr); + sptr subStage1 = sptr::MakeSptr(); + subSession1->sessionStage_ = subStage1; + sptr subSession2 = sptr::MakeSptr(info, nullptr); + sptr subStage2 = sptr::MakeSptr(); + subSession2->sessionStage_ = subStage2; + + // Setup: subSession1 is attached, subSession2 is not + subSession1->windowAnchorInfo_.isAnchoredByAttach_ = true; + subSession1->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + subSession2->windowAnchorInfo_.isAnchoredByAttach_ = false; + + mainSession->subSession_.emplace_back(subSession1); + mainSession->subSession_.emplace_back(subSession2); + + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 1001; + + // Main session updates own limits + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Only subSession1 should be notified (has attach enabled) + EXPECT_CALL(*subStage1, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // subSession2 should NOT be notified (no attach relationship) + EXPECT_CALL(*subStage2, UpdateAttachedWindowLimits(testing::_, testing::_, testing::_, testing::_)) + .Times(0); + + WSError ret = mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + true, true); // use default excludePersistentId=INVALID_SESSION_ID so main updates itself + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits01 + * @tc.desc: Test main window removes own limits and propagates to children + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimits01, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestRemoveAttachedWindowLimits01"; + info.moduleName_ = "RequestRemoveAttachedWindowLimits01"; + info.abilityName_ = "RequestRemoveAttachedWindowLimits01"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 2001; + + // Test with null sessionStage_ + mainSession->sessionStage_ = nullptr; + WSError ret = mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, ret); + + // Test with valid sessionStage_ + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + EXPECT_CALL(*mainSessionStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + ret = mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimitsPropagate01 + * @tc.desc: Test MainSession RequestUpdateAttachedWindowLimits propagation to multiple sub sessions + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimitsPropagate01, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestUpdateAttachedWindowLimitsPropagate01"); + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create sub sessions with mock stages and attach info + auto sub1 = CreateSubSessionWithMock("Sub1"); + auto sub2 = CreateSubSessionWithMock("Sub2"); + auto sub3 = CreateSubSessionWithMock("Sub3"); + + // Add sub sessions to main session + mainSession->subSession_.emplace_back(sub1.session); + mainSession->subSession_.emplace_back(sub2.session); + mainSession->subSession_.emplace_back(sub3.session); + sub1.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub2.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub3.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub1.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub2.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub3.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 100; + + // All sub sessions should be notified via UpdateAttachedWindowLimits + EXPECT_CALL(*sub1.mockStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*sub2.mockStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*sub3.mockStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Propagate to all sub sessions (exclude self to skip redundant update) + mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, newLimits, + true, true, mainSession->GetPersistentId()); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimitsPropagate02 + * @tc.desc: Test MainSession RequestUpdateAttachedWindowLimits with exclude persistentId + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimitsPropagate02, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestUpdateAttachedWindowLimitsPropagate02"); + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create sub sessions with mock stages and attach info + auto sub1 = CreateSubSessionWithMock("Sub1"); + auto sub2 = CreateSubSessionWithMock("Sub2"); + auto sub3 = CreateSubSessionWithMock("Sub3"); + + // Add sub sessions to main session + mainSession->subSession_.emplace_back(sub1.session); + mainSession->subSession_.emplace_back(sub2.session); + mainSession->subSession_.emplace_back(sub3.session); + sub1.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub2.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub3.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub1.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub2.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub3.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 100; + + // Main session should update its own limits + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // subSession1 and subSession3 should be notified via UpdateAttachedWindowLimits, but NOT subSession2 (excluded) + EXPECT_CALL(*sub1.mockStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*sub2.mockStage, UpdateAttachedWindowLimits( + testing::_, testing::_, testing::_, testing::_)) + .Times(0); + EXPECT_CALL(*sub3.mockStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Propagate to sub sessions, excluding subSession2 + mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, newLimits, + true, true, sub2.session->GetPersistentId()); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimitsPropagate03 + * @tc.desc: Test MainSession RequestUpdateAttachedWindowLimits with empty sub sessions + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimitsPropagate03, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestUpdateAttachedWindowLimitsPropagate03"; + info.moduleName_ = "RequestUpdateAttachedWindowLimitsPropagate03"; + info.abilityName_ = "RequestUpdateAttachedWindowLimitsPropagate03"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 100; + + // No sub sessions, and excludePersistentId == GetPersistentId(), so no update to own limits + // Verify that UpdateAttachedWindowLimits is NOT called on mainSessionStage + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + testing::_, testing::_, testing::_, testing::_)) + .Times(0); + mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, newLimits, + true, true, mainSession->GetPersistentId()); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimitsPropagate04 + * @tc.desc: Test MainSession RequestUpdateAttachedWindowLimits with sub sessions without attach relationship + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimitsPropagate04, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestUpdateAttachedWindowLimitsPropagate04"; + info.moduleName_ = "RequestUpdateAttachedWindowLimitsPropagate04"; + info.abilityName_ = "RequestUpdateAttachedWindowLimitsPropagate04"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + sptr subSession1 = sptr::MakeSptr(info, nullptr); + sptr subSession2 = sptr::MakeSptr(info, nullptr); + + // Create mock session stages for sub sessions + sptr subSessionStage1 = sptr::MakeSptr(); + sptr subSessionStage2 = sptr::MakeSptr(); + subSession1->sessionStage_ = subSessionStage1; + subSession2->sessionStage_ = subSessionStage2; + + // Set up attach info WITHOUT intersected limits + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.isFromAttachOrDetach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = false; + anchorInfo.attachOptions.isIntersectedWidthLimit = false; + subSession1->SetWindowAnchorInfo(anchorInfo); + subSession2->SetWindowAnchorInfo(anchorInfo); + + // Add sub sessions to main session + mainSession->subSession_.emplace_back(subSession1); + mainSession->subSession_.emplace_back(subSession2); + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 100; + + // Should not notify sub sessions without intersected limits + EXPECT_CALL(*subSessionStage1, UpdateAttachedWindowLimits( + testing::_, testing::_, testing::_, testing::_)) + .Times(0); + EXPECT_CALL(*subSessionStage2, UpdateAttachedWindowLimits( + testing::_, testing::_, testing::_, testing::_)) + .Times(0); + + // Main session should NOT update its own limits since excludePersistentId == GetPersistentId() + mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, newLimits, + true, true, mainSession->GetPersistentId()); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimitsPropagate01 + * @tc.desc: Test MainSession RequestRemoveAttachedWindowLimits with multiple sub sessions + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimitsPropagate01, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestRemoveAttachedWindowLimitsPropagate01"); + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create sub sessions with mock stages and attach info + auto sub1 = CreateSubSessionWithMock("Sub1"); + auto sub2 = CreateSubSessionWithMock("Sub2"); + auto sub3 = CreateSubSessionWithMock("Sub3"); + + // Add sub sessions to main session + mainSession->subSession_.emplace_back(sub1.session); + mainSession->subSession_.emplace_back(sub2.session); + mainSession->subSession_.emplace_back(sub3.session); + sub1.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub2.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub3.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub1.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub2.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub3.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + int32_t sourcePersistentId = 100; + + // All sub sessions should have their limits removed + EXPECT_CALL(*sub1.mockStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*sub2.mockStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*sub3.mockStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Remove limits from all sub sessions (exclude self) + mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, mainSession->GetPersistentId()); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimitsPropagate02 + * @tc.desc: Test MainSession RequestRemoveAttachedWindowLimits with exclude persistentId + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimitsPropagate02, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestRemoveAttachedWindowLimitsPropagate02"); + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create sub sessions with mock stages and attach info + auto sub1 = CreateSubSessionWithMock("Sub1"); + auto sub2 = CreateSubSessionWithMock("Sub2"); + auto sub3 = CreateSubSessionWithMock("Sub3"); + + // Add sub sessions to main session + mainSession->subSession_.emplace_back(sub1.session); + mainSession->subSession_.emplace_back(sub2.session); + mainSession->subSession_.emplace_back(sub3.session); + sub1.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub2.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub3.session->windowAnchorInfo_.isAnchoredByAttach_ = true; + sub1.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub2.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + sub3.session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + int32_t sourcePersistentId = 100; + + // Main session should remove its own limits + EXPECT_CALL(*mainSessionStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // subSession1 and subSession3 should have their limits removed, but NOT subSession2 (excluded) + EXPECT_CALL(*sub1.mockStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*sub2.mockStage, RemoveAttachedWindowLimits(testing::_)) + .Times(0); + EXPECT_CALL(*sub3.mockStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Remove limits from sub sessions, excluding subSession2 + mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, sub2.session->GetPersistentId()); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimitsPropagate03 + * @tc.desc: Test MainSession RequestRemoveAttachedWindowLimits with empty sub sessions + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimitsPropagate03, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestRemoveAttachedWindowLimitsPropagate03"; + info.moduleName_ = "RequestRemoveAttachedWindowLimitsPropagate03"; + info.abilityName_ = "RequestRemoveAttachedWindowLimitsPropagate03"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + int32_t sourcePersistentId = 100; + + // No sub sessions, and excludePersistentId == GetPersistentId(), so no remove from own limits + // Verify that RemoveAttachedWindowLimits is NOT called on mainSessionStage + EXPECT_CALL(*mainSessionStage, RemoveAttachedWindowLimits(testing::_)) + .Times(0); + mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, mainSession->GetPersistentId()); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimitsPropagate04 + * @tc.desc: Test MainSession RequestRemoveAttachedWindowLimits with sub sessions without attach relationship + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimitsPropagate04, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestRemoveAttachedWindowLimitsPropagate04"; + info.moduleName_ = "RequestRemoveAttachedWindowLimitsPropagate04"; + info.abilityName_ = "RequestRemoveAttachedWindowLimitsPropagate04"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + sptr subSession1 = sptr::MakeSptr(info, nullptr); + sptr subSession2 = sptr::MakeSptr(info, nullptr); + + // Create mock session stages for sub sessions + sptr subSessionStage1 = sptr::MakeSptr(); + sptr subSessionStage2 = sptr::MakeSptr(); + subSession1->sessionStage_ = subSessionStage1; + subSession2->sessionStage_ = subSessionStage2; + + // Set up attach info WITHOUT intersected limits (should not be notified) + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.isFromAttachOrDetach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = false; + anchorInfo.attachOptions.isIntersectedWidthLimit = false; + subSession1->SetWindowAnchorInfo(anchorInfo); + subSession2->SetWindowAnchorInfo(anchorInfo); + + // Add sub sessions to main session + mainSession->subSession_.emplace_back(subSession1); + mainSession->subSession_.emplace_back(subSession2); + + int32_t sourcePersistentId = 100; + + // Should not notify sub sessions without intersected limits + EXPECT_CALL(*subSessionStage1, RemoveAttachedWindowLimits(testing::_)) + .Times(0); + EXPECT_CALL(*subSessionStage2, RemoveAttachedWindowLimits(testing::_)) + .Times(0); + + // Main session should NOT remove its own limits since excludePersistentId == GetPersistentId() + mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, mainSession->GetPersistentId()); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimits05 + * @tc.desc: Test MainSession RequestUpdateAttachedWindowLimits with null sessionStage + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimits05, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestUpdateAttachedWindowLimits05"; + info.moduleName_ = "RequestUpdateAttachedWindowLimits05"; + info.abilityName_ = "RequestUpdateAttachedWindowLimits05"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Do NOT set sessionStage_ - leave it as null + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 100; + + // Should return WS_ERROR_NULLPTR since sessionStage_ is null + WSError res = mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, newLimits, + true, true, mainSession->GetPersistentId()); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, res); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimits06 + * @tc.desc: Test MainSession RequestUpdateAttachedWindowLimits with UpdateAttachedWindowLimits failure + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestUpdateAttachedWindowLimits06, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestUpdateAttachedWindowLimits06"; + info.moduleName_ = "RequestUpdateAttachedWindowLimits06"; + info.abilityName_ = "RequestUpdateAttachedWindowLimits06"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + int32_t sourcePersistentId = 100; + + // Set excludePersistentId to a value different from GetPersistentId() so mainSession updates its own limits + int32_t excludePersistentId = INVALID_SESSION_ID; + + // Main session's UpdateAttachedWindowLimits should fail + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_ERROR_IPC_FAILED)); + + // Should return WS_ERROR_IPC_FAILED since UpdateAttachedWindowLimits failed + WSError res = mainSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, newLimits, + true, true, excludePersistentId); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits05 + * @tc.desc: Test MainSession RequestRemoveAttachedWindowLimits with null sessionStage + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimits05, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestRemoveAttachedWindowLimits05"; + info.moduleName_ = "RequestRemoveAttachedWindowLimits05"; + info.abilityName_ = "RequestRemoveAttachedWindowLimits05"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Do NOT set sessionStage_ - leave it as null + + int32_t sourcePersistentId = 100; + + // Should return WS_ERROR_NULLPTR since sessionStage_ is null + WSError res = mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, mainSession->GetPersistentId()); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, res); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits06 + * @tc.desc: Test MainSession RequestRemoveAttachedWindowLimits with RemoveAttachedWindowLimits failure + * @tc.type: FUNC + */ +HWTEST_F(MainSessionLayoutTest, RequestRemoveAttachedWindowLimits06, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RequestRemoveAttachedWindowLimits06"; + info.moduleName_ = "RequestRemoveAttachedWindowLimits06"; + info.abilityName_ = "RequestRemoveAttachedWindowLimits06"; + sptr mainSession = sptr::MakeSptr(info, nullptr); + + // Create mock session stage for main session + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + int32_t sourcePersistentId = 100; + + // Set excludePersistentId to a value different from GetPersistentId() so mainSession removes its own limits + int32_t excludePersistentId = INVALID_SESSION_ID; + + // Main session's RemoveAttachedWindowLimits should fail + EXPECT_CALL(*mainSessionStage, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_ERROR_IPC_FAILED)); + + // Should return WS_ERROR_IPC_FAILED since RemoveAttachedWindowLimits failed + WSError res = mainSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, excludePersistentId); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/layout/scene_session_layout_test.cpp b/window_scene/test/unittest/layout/scene_session_layout_test.cpp index 57179312c8..032183f02c 100755 --- a/window_scene/test/unittest/layout/scene_session_layout_test.cpp +++ b/window_scene/test/unittest/layout/scene_session_layout_test.cpp @@ -70,6 +70,49 @@ RSSurfaceNode::SharedPtr SceneSessionLayoutTest::CreateRSSurfaceNode() return surfaceNode; } +// Helper: Create a main session with attach info +sptr CreateMainSessionWithAttach(const std::string& name, sptr& mockStage) +{ + SessionInfo info; + info.abilityName_ = name; + info.bundleName_ = name; + sptr session = sptr::MakeSptr(info, nullptr); + session->GetSessionProperty()->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + mockStage = sptr::MakeSptr(); + session->sessionStage_ = mockStage; + + // Main session does not need windowAnchorInfo_ to be set + // It only checks children's windowAnchorInfo_ when notifying attached windows + + return session; +} + +// Helper: Create a child session with attach info +sptr CreateChildSessionWithAttach(const std::string& name, + bool isHeightLimit = true, bool isWidthLimit = true) +{ + SessionInfo info; + info.abilityName_ = name; + info.bundleName_ = name; + info.windowType_ = static_cast(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + sptr session = sptr::MakeSptr(info, nullptr); + + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = isHeightLimit; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = isWidthLimit; + + return session; +} + +// Helper: Set up parent-child relationship +void SetupParentChild(sptr parent, sptr child) +{ + child->parentSession_ = parent; + parent->subSession_.push_back(child); +} + namespace { /** * @tc.name: UpdateRect01 @@ -91,7 +134,7 @@ HWTEST_F(SceneSessionLayoutTest, UpdateRect01, TestSize.Level1) WSRect rect({ 1, 1, 1, 1 }); SizeChangeReason reason = SizeChangeReason::UNDEFINED; WSError result = sceneSession->UpdateRect(rect, reason, "SceneSessionLayoutTest"); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); } /** @@ -114,19 +157,19 @@ HWTEST_F(SceneSessionLayoutTest, UpdateRect02, TestSize.Level0) WSRect rect({ 1, 1, 1, 1 }); SizeChangeReason reason = SizeChangeReason::UNDEFINED; WSError result = sceneSession->UpdateRect(rect, reason, "SceneSessionLayoutTest"); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); sceneSession->GetLayoutController()->SetSessionRect(rect); result = sceneSession->UpdateRect(rect, reason, "SceneSessionLayoutTest"); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); sceneSession->Session::UpdateSizeChangeReason(SizeChangeReason::DRAG_END); result = sceneSession->UpdateRect(rect, reason, "SceneSessionLayoutTest"); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); WSRect rect2({ 0, 0, 0, 0 }); result = sceneSession->UpdateRect(rect2, reason, "SceneSessionLayoutTest"); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); } /** @@ -558,19 +601,19 @@ HWTEST_F(SceneSessionLayoutTest, SetAspectRatio2, TestSize.Level0) float ratio = 0.0001; sceneSession->moveDragController_ = nullptr; auto result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); ASSERT_EQ(sceneSession->GetAspectRatio(), ratio); sceneSession->moveDragController_ = sptr::MakeSptr(wptr(sceneSession)); result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); ASSERT_EQ(sceneSession->GetAspectRatio(), ratio); sptr property = sptr::MakeSptr(); property->SetWindowType(WindowType::APP_MAIN_WINDOW_BASE); sceneSession->SetSessionProperty(property); result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); ASSERT_EQ(sceneSession->GetAspectRatio(), ratio); } @@ -597,7 +640,7 @@ HWTEST_F(SceneSessionLayoutTest, SetAspectRatio3, TestSize.Level1) limits.minHeight_ = 1; property->SetWindowLimits(limits); auto result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); + EXPECT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); } /** @@ -622,7 +665,7 @@ HWTEST_F(SceneSessionLayoutTest, SetAspectRatio4, TestSize.Level1) limits.minWidth_ = 10; property->SetWindowLimits(limits); auto result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); + EXPECT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); } /** @@ -640,13 +683,13 @@ HWTEST_F(SceneSessionLayoutTest, SetAspectRatio5, TestSize.Level0) float ratio = 0.0001; auto result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); sptr property = sptr::MakeSptr(); property->SetWindowType(WindowType::APP_MAIN_WINDOW_BASE); sceneSession->SetSessionProperty(property); result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_OK); + EXPECT_EQ(result, WSError::WS_OK); ASSERT_EQ(sceneSession->GetAspectRatio(), ratio); } @@ -674,7 +717,7 @@ HWTEST_F(SceneSessionLayoutTest, SetAspectRatio6, TestSize.Level0) property->SetWindowLimits(limits); sceneSession->SetSessionProperty(property); auto result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); + EXPECT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); } /** @@ -701,7 +744,7 @@ HWTEST_F(SceneSessionLayoutTest, SetAspectRatio7, TestSize.Level0) property->SetWindowLimits(limits); sceneSession->SetSessionProperty(property); auto result = sceneSession->SetAspectRatio(ratio); - ASSERT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); + EXPECT_EQ(result, WSError::WS_ERROR_INVALID_PARAM); } /** @@ -1262,6 +1305,1169 @@ HWTEST_F(SceneSessionLayoutTest, ExecuteWindowStatusChangeNotification, TestSize usleep(WAIT_SYNC_NS); } +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged01 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with main window and attached children + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged01, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyAttachedWindowsLimitsChanged01"; + info.bundleName_ = "NotifyAttachedWindowsLimitsChanged01"; + + sptr mainSession = sptr::MakeSptr(info, nullptr); + mainSession->GetSessionProperty()->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Set up attach info on main session + mainSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create child session with attach + sptr childSessionStage = sptr::MakeSptr(); + sptr childSession = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged01_child"); + childSession->sessionStage_ = childSessionStage; + SetupParentChild(mainSession, childSession); + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Main window should notify its children via UpdateAttachedWindowLimits (async operation) + EXPECT_CALL(*childSessionStage, UpdateAttachedWindowLimits( + mainSession->GetPersistentId(), testing::_, true, true)) + .Times(1) + .WillOnce(testing::Return(WSError::WS_OK)); + mainSession->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged02 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with child window notifying parent + * Parent should then notify all children (including siblings) automatically + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged02, TestSize.Level1) +{ + sptr mainSessionStage; + sptr mainSession = + CreateMainSessionWithAttach("NotifyAttachedWindowsLimitsChanged02_main", mainSessionStage); + + // Create first child session (the one that will trigger notification) + sptr childSession1 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged02_child1", true, true); + SetupParentChild(mainSession, childSession1); + + // Create second child session (sibling that should be notified) + sptr childSession2Stage = sptr::MakeSptr(); + sptr childSession2 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged02_child2", false, true); + childSession2->sessionStage_ = childSession2Stage; + SetupParentChild(mainSession, childSession2); + + WindowLimits newLimits = { 1800, 1200, 100, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Child window should notify parent and parent should notify sibling (async operation) + // Parent Session calls its SessionStage to update own limits (excludePersistentId defaults to INVALID_SESSION_ID) + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + childSession1->GetPersistentId(), testing::_, true, true)) + .Times(1) + .WillOnce(testing::Return(WSError::WS_OK)); + // Parent then propagates to sibling via UpdateAttachedWindowLimits + EXPECT_CALL(*childSession2Stage, UpdateAttachedWindowLimits( + childSession1->GetPersistentId(), testing::_, true, true)) + .Times(1) + .WillOnce(testing::Return(WSError::WS_OK)); + childSession1->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession1->parentSession_ = nullptr; + childSession2->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged03 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with main window notifying multiple children + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged03, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyAttachedWindowsLimitsChanged03"; + info.bundleName_ = "NotifyAttachedWindowsLimitsChanged03"; + + sptr mainSession = sptr::MakeSptr(info, nullptr); + mainSession->GetSessionProperty()->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Set up attach info on main session + mainSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create multiple child sessions with different limit settings + sptr childSessionStage1 = sptr::MakeSptr(); + sptr childSessionStage2 = sptr::MakeSptr(); + sptr childSessionStage3 = sptr::MakeSptr(); + + sptr childSession1 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged03_child1", true, false); + childSession1->sessionStage_ = childSessionStage1; + SetupParentChild(mainSession, childSession1); + + sptr childSession2 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged03_child2", false, true); + childSession2->sessionStage_ = childSessionStage2; + SetupParentChild(mainSession, childSession2); + + sptr childSession3 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged03_child3", true, false); + childSession3->sessionStage_ = childSessionStage3; + SetupParentChild(mainSession, childSession3); + + WindowLimits newLimits = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Main window should notify all its children via UpdateAttachedWindowLimits (async operation) + EXPECT_CALL(*childSessionStage1, UpdateAttachedWindowLimits( + mainSession->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*childSessionStage2, UpdateAttachedWindowLimits( + mainSession->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*childSessionStage3, UpdateAttachedWindowLimits( + mainSession->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + mainSession->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession1->parentSession_ = nullptr; + childSession2->parentSession_ = nullptr; + childSession3->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged04 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with child window notifying parent + * Parent should then notify all other children (siblings) automatically + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged04, TestSize.Level1) +{ + sptr mainSessionStage; + sptr mainSession = + CreateMainSessionWithAttach("NotifyAttachedWindowsLimitsChanged04_main", mainSessionStage); + + // Create the child session that will trigger notification + sptr childSession1 = CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged04_child1"); + sptr childSessionStage1 = sptr::MakeSptr(); + childSession1->sessionStage_ = childSessionStage1; + SetupParentChild(mainSession, childSession1); + + // Create sibling child sessions with different limit settings + sptr childSessionStage2 = sptr::MakeSptr(); + sptr childSessionStage3 = sptr::MakeSptr(); + + sptr childSession2 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged04_child2", true, false); + childSession2->sessionStage_ = childSessionStage2; + SetupParentChild(mainSession, childSession2); + + sptr childSession3 = + CreateChildSessionWithAttach("NotifyAttachedWindowsLimitsChanged04_child3", false, true); + childSession3->sessionStage_ = childSessionStage3; + SetupParentChild(mainSession, childSession3); + + WindowLimits newLimits = { 1900, 1100, 120, 280, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Set up expectations: child notifies parent + // Parent Session calls its SessionStage to update own limits + EXPECT_CALL(*mainSessionStage, UpdateAttachedWindowLimits( + childSession1->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Parent then notifies other children via UpdateAttachedWindowLimits + EXPECT_CALL(*childSessionStage2, UpdateAttachedWindowLimits( + childSession1->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*childSessionStage3, UpdateAttachedWindowLimits( + childSession1->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Child window should notify parent, and parent should notify all siblings automatically + childSession1->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession1->parentSession_ = nullptr; + childSession2->parentSession_ = nullptr; + childSession3->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged05 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged without attach relationship + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged05, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyAttachedWindowsLimitsChanged05"; + info.bundleName_ = "NotifyAttachedWindowsLimitsChanged05"; + + sptr session = sptr::MakeSptr(info, nullptr); + + // Set up without attach relationship + session->windowAnchorInfo_.isAnchoredByAttach_ = false; + + WindowLimits newLimits = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // No attach relationship - no parent to notify, so this should complete without errors + // This tests the path when isAnchoredByAttach_ is false + auto result = session->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); // Wait for async task to complete + + // Verify function returns OK and limits were set in property + EXPECT_EQ(result, WSError::WS_OK); + WindowLimits savedLimits = session->GetSessionProperty()->GetLimitsForAttachedWindows(); + EXPECT_EQ(savedLimits.maxWidth_, 1800); + EXPECT_EQ(savedLimits.maxHeight_, 900); + EXPECT_EQ(savedLimits.minWidth_, 150); + EXPECT_EQ(savedLimits.minHeight_, 250); +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged06 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with sub window but no parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged06, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyAttachedWindowsLimitsChanged06"; + info.bundleName_ = "NotifyAttachedWindowsLimitsChanged06"; + info.windowType_ = static_cast(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info for sub window + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Deliberately don't set parent - parentSession will be null + // This should log a warning but not crash + WindowLimits newLimits = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + auto result = session->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); // Wait for async task to complete + + // Verify function returns OK, limits were set, and no crash occurred + EXPECT_EQ(result, WSError::WS_OK); + WindowLimits savedLimits = session->GetSessionProperty()->GetLimitsForAttachedWindows(); + EXPECT_EQ(savedLimits.maxWidth_, 1800); + EXPECT_EQ(savedLimits.maxHeight_, 900); + EXPECT_EQ(savedLimits.minWidth_, 150); + EXPECT_EQ(savedLimits.minHeight_, 250); +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged07 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with system modal window + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyAttachedWindowsLimitsChanged07, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyAttachedWindowsLimitsChanged07"; + info.bundleName_ = "NotifyAttachedWindowsLimitsChanged07"; + info.windowType_ = static_cast(WindowType::WINDOW_TYPE_FLOAT); + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info for system modal window + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // System modal window is neither main nor sub window, so it should not trigger any notifications + WindowLimits newLimits = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + auto result = session->NotifyAttachedWindowsLimitsChanged(newLimits); + usleep(WAIT_SYNC_NS); // Wait for async task to complete + + // Verify function returns OK and limits were set (even though no notifications are sent) + EXPECT_EQ(result, WSError::WS_OK); + WindowLimits savedLimits = session->GetSessionProperty()->GetLimitsForAttachedWindows(); + EXPECT_EQ(savedLimits.maxWidth_, 1800); + EXPECT_EQ(savedLimits.maxHeight_, 900); + EXPECT_EQ(savedLimits.minWidth_, 150); + EXPECT_EQ(savedLimits.minHeight_, 250); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange01 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange when isFromAttachOrDetach_ is false + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange01, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange01"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange01"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with isFromAttachOrDetach_ = false + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = false; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr parentSession = nullptr; + + // Should return early without any operations + session->NotifyRelatedWindowsAttachStateChange(parentSession, false, true, false, false); + + // Verify no changes to property lists + EXPECT_TRUE(session->GetSessionProperty()->GetAttachedWindowLimitsList().empty()); + EXPECT_TRUE(session->GetSessionProperty()->GetAttachedLimitOptionsList().empty()); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange02 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange when effective limits unchanged + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange02, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange02"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange02"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits enabled + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr parentSession = nullptr; + + // wasAttached=false, isAttached=false + // Old effective: false && true = false, New effective: false && true = false (no change) + session->NotifyRelatedWindowsAttachStateChange(parentSession, false, false, true, true); + + // Verify no changes to property lists + EXPECT_TRUE(session->GetSessionProperty()->GetAttachedWindowLimitsList().empty()); + EXPECT_TRUE(session->GetSessionProperty()->GetAttachedLimitOptionsList().empty()); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange03 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange attach scenario without parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange03, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange03"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange03"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr parentSession = nullptr; + + // wasAttached=false, isAttached=true (attach scenario) + // Without parent, should not call any request methods + session->NotifyRelatedWindowsAttachStateChange(parentSession, false, true, false, false); + + // Verify no crash and lists remain empty + EXPECT_TRUE(session->GetSessionProperty()->GetAttachedWindowLimitsList().empty()); + EXPECT_TRUE(session->GetSessionProperty()->GetAttachedLimitOptionsList().empty()); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange04 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange attach scenario with parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange04, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange04"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange04"; + + // Create parent session (MainSession overrides RequestUpdateAttachedWindowLimits) + sptr parentSession = sptr::MakeSptr(info, nullptr); + sptr parentSceneSession = static_cast(parentSession.GetRefPtr()); + sptr parentSessionStage = sptr::MakeSptr(); + parentSceneSession->sessionStage_ = parentSessionStage; + + // Create child session + sptr childSession = sptr::MakeSptr(info, nullptr); + childSession->sessionStage_ = mockSessionStage_; + + // Set up attach info for child + childSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + childSession->windowAnchorInfo_.isFromAttachOrDetach_ = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Set window limits for notification + childSession->GetSessionProperty()->SetLimitsForAttachedWindows( + { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + + // Expect parent to receive RequestUpdateAttachedWindowLimits call + EXPECT_CALL(*parentSessionStage, UpdateAttachedWindowLimits( + childSession->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // wasAttached=false, isAttached=true (attach scenario) + childSession->NotifyRelatedWindowsAttachStateChange(parentSession, false, true, false, false); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange05 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange detach scenario without parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange05, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange05"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange05"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Add some limits to the lists to verify they get cleared + auto property = session->GetSessionProperty(); + property->SetAttachedWindowLimits(100, { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + AttachLimitOptions options = { true, true }; + property->SetAttachedLimitOptions(100, options); + + // Verify lists are not empty before + EXPECT_FALSE(property->GetAttachedWindowLimitsList().empty()); + EXPECT_FALSE(property->GetAttachedLimitOptionsList().empty()); + + sptr parentSession = nullptr; + + // wasAttached=true, isAttached=false (detach scenario) + session->NotifyRelatedWindowsAttachStateChange(parentSession, true, false, true, true); + + // Verify lists were cleared + EXPECT_TRUE(property->GetAttachedWindowLimitsList().empty()); + EXPECT_TRUE(property->GetAttachedLimitOptionsList().empty()); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange06 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange detach scenario with parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange06, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange06"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange06"; + + // Create parent session (MainSession overrides RequestRemoveAttachedWindowLimits) + sptr parentSession = sptr::MakeSptr(info, nullptr); + sptr parentSceneSession = static_cast(parentSession.GetRefPtr()); + sptr parentSessionStage = sptr::MakeSptr(); + parentSceneSession->sessionStage_ = parentSessionStage; + + // Create child session + sptr childSession = sptr::MakeSptr(info, nullptr); + childSession->sessionStage_ = mockSessionStage_; + + // Set up attach info for child + childSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + childSession->windowAnchorInfo_.isFromAttachOrDetach_ = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Expect parent to receive RequestRemoveAttachedWindowLimits call + EXPECT_CALL(*parentSessionStage, RemoveAttachedWindowLimits(childSession->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // wasAttached=true, isAttached=false (detach scenario) + childSession->NotifyRelatedWindowsAttachStateChange(parentSession, true, false, true, true); + + // Verify lists were cleared + EXPECT_TRUE(childSession->GetSessionProperty()->GetAttachedWindowLimitsList().empty()); + EXPECT_TRUE(childSession->GetSessionProperty()->GetAttachedLimitOptionsList().empty()); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange07 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange when limits change without parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange07, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange07"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange07"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; // New value + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr parentSession = nullptr; + + // wasAttached=true, isAttached=true (attach state unchanged) + // old height limit was false, new is true (limits changed) + // Without parent, should not call any request methods + session->NotifyRelatedWindowsAttachStateChange(parentSession, true, true, true, false); + + // Verify no crash + EXPECT_TRUE(session->GetWindowAnchorInfo().isAnchoredByAttach_); +} + +/** + * @tc.name: NotifyRelatedWindowsAttachStateChange08 + * @tc.desc: Test NotifyRelatedWindowsAttachStateChange when limits change with parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsAttachStateChange08, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsAttachStateChange08"; + info.bundleName_ = "NotifyRelatedWindowsAttachStateChange08"; + + // Create parent session (MainSession overrides RequestUpdateAttachedWindowLimits) + sptr parentSession = sptr::MakeSptr(info, nullptr); + sptr parentSceneSession = static_cast(parentSession.GetRefPtr()); + sptr parentSessionStage = sptr::MakeSptr(); + parentSceneSession->sessionStage_ = parentSessionStage; + + // Create child session + sptr childSession = sptr::MakeSptr(info, nullptr); + childSession->sessionStage_ = mockSessionStage_; + + // Set up attach info with new limits (height enabled, width enabled) + childSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + childSession->windowAnchorInfo_.isFromAttachOrDetach_ = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; // New value (changed from false) + childSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Set window limits for notification + childSession->GetSessionProperty()->SetLimitsForAttachedWindows( + { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + + // Expect parent to receive RequestUpdateAttachedWindowLimits call + // wasAttached=true, isAttached=true, but old height limit was false + // Old effective: true && false = false (height), New effective: true && true = true (height) + EXPECT_CALL(*parentSessionStage, UpdateAttachedWindowLimits( + childSession->GetPersistentId(), testing::_, true, true)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Limits changed scenario + childSession->NotifyRelatedWindowsAttachStateChange(parentSession, true, true, true, false); +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction01 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with main window and attached children + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction01, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsOnDestruction01"; + info.bundleName_ = "NotifyRelatedWindowsOnDestruction01"; + + sptr mainSession = sptr::MakeSptr(info, nullptr); + mainSession->GetSessionProperty()->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Set up attach info on main session + mainSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create child session with attach + SessionInfo childInfo; + childInfo.abilityName_ = "NotifyRelatedWindowsOnDestruction01_child"; + childInfo.bundleName_ = "NotifyRelatedWindowsOnDestruction01_child"; + childInfo.windowType_ = static_cast(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + sptr childSession = sptr::MakeSptr(childInfo, nullptr); + + childSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + childSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Create mock session stage for child + sptr childSessionStage = sptr::MakeSptr(); + childSession->sessionStage_ = childSessionStage; + + // Add child to main session + mainSession->subSession_.push_back(childSession); + childSession->parentSession_ = mainSession; + + // Main window being destroyed should call RemoveAttachedWindowLimits on child + EXPECT_CALL(*childSessionStage, RemoveAttachedWindowLimits(mainSession->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + mainSession->NotifyRelatedWindowsOnDestruction(); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction02 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with child window notifying parent + * Parent should then notify other children (siblings) to remove limits + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction02, TestSize.Level1) +{ + sptr mainSessionStage; + sptr mainSession = + CreateMainSessionWithAttach("NotifyRelatedWindowsOnDestruction02_main", mainSessionStage); + + // Create first child session (the one being destroyed) + sptr childSession1 = + CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction02_child1", true, false); + SetupParentChild(mainSession, childSession1); + + // Create second child session (sibling that should be notified) + sptr childSession2Stage = sptr::MakeSptr(); + sptr childSession2 = + CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction02_child2", false, true); + childSession2->sessionStage_ = childSession2Stage; + SetupParentChild(mainSession, childSession2); + + // Child being destroyed notifies parent via RemoveAttachedWindowLimits + EXPECT_CALL(*mainSessionStage, RemoveAttachedWindowLimits(childSession1->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Parent then notifies sibling via RemoveAttachedWindowLimits + EXPECT_CALL(*childSession2Stage, RemoveAttachedWindowLimits(childSession1->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Child window being destroyed should notify parent, and parent should notify sibling + childSession1->NotifyRelatedWindowsOnDestruction(); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession1->parentSession_ = nullptr; + childSession2->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction03 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with main window notifying multiple children + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction03, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsOnDestruction03"; + info.bundleName_ = "NotifyRelatedWindowsOnDestruction03"; + + sptr mainSession = sptr::MakeSptr(info, nullptr); + mainSession->GetSessionProperty()->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Set up attach info on main session + mainSession->windowAnchorInfo_.isAnchoredByAttach_ = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + mainSession->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + sptr mainSessionStage = sptr::MakeSptr(); + mainSession->sessionStage_ = mainSessionStage; + + // Create multiple child sessions with attach + sptr childSessionStage1 = sptr::MakeSptr(); + sptr childSessionStage2 = sptr::MakeSptr(); + + sptr childSession1 = + CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction03_child1", true, false); + childSession1->sessionStage_ = childSessionStage1; + SetupParentChild(mainSession, childSession1); + + sptr childSession2 = + CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction03_child2", false, true); + childSession2->sessionStage_ = childSessionStage2; + SetupParentChild(mainSession, childSession2); + + // Main window being destroyed should call RemoveAttachedWindowLimits on all children + EXPECT_CALL(*childSessionStage1, RemoveAttachedWindowLimits(mainSession->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*childSessionStage2, RemoveAttachedWindowLimits(mainSession->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Main window being destroyed should notify all its children + mainSession->NotifyRelatedWindowsOnDestruction(); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession1->parentSession_ = nullptr; + childSession2->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction04 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with child window notifying parent + * Parent should then notify all other children (multiple siblings) to remove limits + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction04, TestSize.Level1) +{ + sptr mainSessionStage; + sptr mainSession = + CreateMainSessionWithAttach("NotifyRelatedWindowsOnDestruction04_main", mainSessionStage); + + // Create the child session that will be destroyed + sptr childSession1 = CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction04_child1"); + SetupParentChild(mainSession, childSession1); + + // Create sibling child sessions with different limit settings + sptr childSessionStage2 = sptr::MakeSptr(); + sptr childSessionStage3 = sptr::MakeSptr(); + + sptr childSession2 = + CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction04_child2", true, false); + childSession2->sessionStage_ = childSessionStage2; + SetupParentChild(mainSession, childSession2); + + sptr childSession3 = + CreateChildSessionWithAttach("NotifyRelatedWindowsOnDestruction04_child3", false, true); + childSession3->sessionStage_ = childSessionStage3; + SetupParentChild(mainSession, childSession3); + + // Child being destroyed notifies parent via RemoveAttachedWindowLimits + EXPECT_CALL(*mainSessionStage, RemoveAttachedWindowLimits(childSession1->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Parent then notifies all siblings via RemoveAttachedWindowLimits + EXPECT_CALL(*childSessionStage2, RemoveAttachedWindowLimits(childSession1->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + EXPECT_CALL(*childSessionStage3, RemoveAttachedWindowLimits(childSession1->GetPersistentId())) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + // Child window being destroyed should notify parent, and parent should notify all siblings + childSession1->NotifyRelatedWindowsOnDestruction(); + + // Clean up: break circular sptr references + mainSession->subSession_.clear(); + childSession1->parentSession_ = nullptr; + childSession2->parentSession_ = nullptr; + childSession3->parentSession_ = nullptr; +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction05 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction without attach relationship + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction05, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsOnDestruction05"; + info.bundleName_ = "NotifyRelatedWindowsOnDestruction05"; + + sptr session = sptr::MakeSptr(info, nullptr); + + // Set up without attach - should return early + session->windowAnchorInfo_.isAnchoredByAttach_ = false; + + session->NotifyRelatedWindowsOnDestruction(); + + // Verify early return path - no attach relationship + EXPECT_FALSE(session->GetWindowAnchorInfo().isAnchoredByAttach_); +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction06 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with attach but no intersected limits + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction06, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsOnDestruction06"; + info.bundleName_ = "NotifyRelatedWindowsOnDestruction06"; + + sptr session = sptr::MakeSptr(info, nullptr); + + // Set up with attach but no intersected limits - should return early + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = false; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = false; + + session->NotifyRelatedWindowsOnDestruction(); + + // Verify early return path - attach exists but no intersected limits + EXPECT_TRUE(session->GetWindowAnchorInfo().isAnchoredByAttach_); + EXPECT_FALSE(session->GetWindowAnchorInfo().attachOptions.isIntersectedHeightLimit); + EXPECT_FALSE(session->GetWindowAnchorInfo().attachOptions.isIntersectedWidthLimit); +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction07 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with sub window but no parent + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction07, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsOnDestruction07"; + info.bundleName_ = "NotifyRelatedWindowsOnDestruction07"; + info.windowType_ = static_cast(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info for sub window + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Deliberately don't set parent - parentSession will be null + // This should log a warning but not crash + session->NotifyRelatedWindowsOnDestruction(); + + // Verify sub window with attach but no parent does not crash + EXPECT_EQ(info.windowType_, static_cast(WindowType::WINDOW_TYPE_APP_SUB_WINDOW)); + EXPECT_EQ(session->parentSession_, nullptr); + EXPECT_TRUE(session->GetWindowAnchorInfo().isAnchoredByAttach_); +} + +/** + * @tc.name: NotifyRelatedWindowsOnDestruction08 + * @tc.desc: Test NotifyRelatedWindowsOnDestruction with system modal window + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, NotifyRelatedWindowsOnDestruction08, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "NotifyRelatedWindowsOnDestruction08"; + info.bundleName_ = "NotifyRelatedWindowsOnDestruction08"; + info.windowType_ = static_cast(WindowType::WINDOW_TYPE_FLOAT); + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info for system modal window + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // System modal window is neither main nor sub window, so it should not trigger any notifications + session->NotifyRelatedWindowsOnDestruction(); + + // Verify FLOAT window type and attach state (no notifications sent) + EXPECT_EQ(info.windowType_, static_cast(WindowType::WINDOW_TYPE_FLOAT)); + EXPECT_TRUE(session->GetWindowAnchorInfo().isAnchoredByAttach_); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimits_BaseClass + * @tc.desc: Test SceneSession base class default implementation returns WS_OK + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, RequestUpdateAttachedWindowLimits_BaseClass, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "RequestUpdateAttachedWindowLimits_BaseClass"; + info.bundleName_ = "RequestUpdateAttachedWindowLimits_BaseClass"; + + sptr session = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 1001; + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + bool isIntersectedHeightLimit = true; + bool isIntersectedWidthLimit = true; + + // Base class default implementation should return WS_OK + WSError ret = session->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits_BaseClass + * @tc.desc: Test SceneSession base class default implementation returns WS_OK + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, RequestRemoveAttachedWindowLimits_BaseClass, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "RequestRemoveAttachedWindowLimits_BaseClass"; + info.bundleName_ = "RequestRemoveAttachedWindowLimits_BaseClass"; + + sptr session = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 2001; + + // Base class default implementation should return WS_OK + WSError ret = session->RequestRemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: ShouldProcessAttachStateChange01 + * @tc.desc: Test ShouldProcessAttachStateChange with valid session and attach info + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange01, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange01"; + info.bundleName_ = "ShouldProcessAttachStateChange01"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Test with valid session and attach state change (wasAttached=false, isAttached=true) + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(false, true, false, false, isDetaching); + EXPECT_EQ(true, result); // Should return true as effective limits changed + EXPECT_EQ(false, isDetaching); // Not detaching +} + +/** + * @tc.name: ShouldProcessAttachStateChange02 + * @tc.desc: Test ShouldProcessAttachStateChange returns false when isFromAttachOrDetach_ is false + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange02, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange02"; + info.bundleName_ = "ShouldProcessAttachStateChange02"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with isFromAttachOrDetach_ = false + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = false; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(false, true, false, false, isDetaching); + EXPECT_EQ(false, result); +} + +/** + * @tc.name: ShouldProcessAttachStateChange03 + * @tc.desc: Test ShouldProcessAttachStateChange returns false when effective limits unchanged + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange03, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange03"; + info.bundleName_ = "ShouldProcessAttachStateChange03"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Test with unchanged effective limits (wasAttached=false, isAttached=false) + // Old: false && true = false, New: false && true = false (no change) + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(false, false, true, true, isDetaching); + EXPECT_EQ(false, result); +} + +/** + * @tc.name: ShouldProcessAttachStateChange04 + * @tc.desc: Test ShouldProcessAttachStateChange returns true when effective limits change + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange04, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange04"; + info.bundleName_ = "ShouldProcessAttachStateChange04"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Test with changed effective limits (wasAttached=false, isAttached=true) + // Old: false && false = false, New: true && true = true (changed) + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(false, true, false, false, isDetaching); + EXPECT_EQ(true, result); + EXPECT_EQ(false, isDetaching); // Not detaching (wasAttached=false, isAttached=true) +} + +/** + * @tc.name: ShouldProcessAttachStateChange05 + * @tc.desc: Test ShouldProcessAttachStateChange with attach scenario + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange05, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange05"; + info.bundleName_ = "ShouldProcessAttachStateChange05"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with height limit only + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = false; + + // Test attach: wasAttached=false, isAttached=true + // Old: false && false = false, New: true && true = true (height changed) + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(false, true, false, false, isDetaching); + EXPECT_EQ(true, result); + EXPECT_EQ(false, isDetaching); // Not detaching +} + +/** + * @tc.name: ShouldProcessAttachStateChange06 + * @tc.desc: Test ShouldProcessAttachStateChange with detach scenario + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange06, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange06"; + info.bundleName_ = "ShouldProcessAttachStateChange06"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with both limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Test detach: wasAttached=true, isAttached=false + // Old: true && true = true, New: false && true = false (changed) + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(true, false, true, true, isDetaching); + EXPECT_EQ(true, result); + EXPECT_EQ(true, isDetaching); // Detaching (wasAttached=true, isAttached=false) +} + +/** + * @tc.name: ShouldProcessAttachStateChange07 + * @tc.desc: Test ShouldProcessAttachStateChange returns false when effective limits unchanged (both true) + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange07, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange07"; + info.bundleName_ = "ShouldProcessAttachStateChange07"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Test with both old and new effective limits being true (no change) + // Old: true && true = true, New: true && true = true (no change) + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(true, true, true, true, isDetaching); + EXPECT_EQ(false, result); // No change in effective limits +} + +/** + * @tc.name: ShouldProcessAttachStateChange08 + * @tc.desc: Test ShouldProcessAttachStateChange when attach state unchanged but limits change + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, ShouldProcessAttachStateChange08, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "ShouldProcessAttachStateChange08"; + info.bundleName_ = "ShouldProcessAttachStateChange08"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Set up attach info with intersected limits + session->windowAnchorInfo_.isAnchoredByAttach_ = true; + session->windowAnchorInfo_.isFromAttachOrDetach_ = true; + session->windowAnchorInfo_.attachOptions.isIntersectedHeightLimit = true; // New value + session->windowAnchorInfo_.attachOptions.isIntersectedWidthLimit = true; + + // Test with wasAttached=true, isAttached=true, but old limits changed + // Old: true && false = false (height), true && true = true (width) + // New: true && true = true (height), true && true = true (width) + // Height limit changed from false to true + bool isDetaching = false; + bool result = session->ShouldProcessAttachStateChange(true, true, true, false, isDetaching); + EXPECT_EQ(true, result); // Height limit changed + EXPECT_EQ(false, isDetaching); // Not detaching (wasAttached=true, isAttached=true) +} + /** * @tc.name: HandleMoveDragEnd * @tc.desc: HandleMoveDragEnd @@ -1417,65 +2623,6 @@ HWTEST_F(SceneSessionLayoutTest, SetMoveAvailableArea02, TestSize.Level1) EXPECT_EQ(res, WSError::WS_OK); } -/** - * @tc.name: GetAppHookWindowInfoFromServer - * @tc.desc: GetAppHookWindowInfoFromServer - * @tc.type: FUNC - */ -HWTEST_F(SceneSessionLayoutTest, GetAppHookWindowInfoFromServer, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "GetAppHookWindowInfoFromServer"; - info.bundleName_ = "GetAppHookWindowInfoFromServer"; - sptr sceneSession = sptr::MakeSptr(info, nullptr); - - sceneSession->getHookWindowInfoFunc_ = nullptr; - HookWindowInfo hookWindowInfo; - WMError errCode = sceneSession->GetAppHookWindowInfoFromServer(hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_ERROR_NULLPTR); - EXPECT_EQ(hookWindowInfo.enableHookWindow, false); - - sceneSession->getHookWindowInfoFunc_ = [](const std::string& bundleName) -> HookWindowInfo { - HookWindowInfo hookInfo; - hookInfo.enableHookWindow = true; - return hookInfo; - }; - HookWindowInfo hookWindowInfo2; - errCode = sceneSession->GetAppHookWindowInfoFromServer(hookWindowInfo2); - EXPECT_EQ(errCode, WMError::WM_OK); - EXPECT_EQ(hookWindowInfo2.enableHookWindow, true); -} - -/** - * @tc.name: RegisterAppHookWindowInfoFunc - * @tc.desc: RegisterAppHookWindowInfoFunc - * @tc.type: FUNC - */ -HWTEST_F(SceneSessionLayoutTest, RegisterAppHookWindowInfoFunc, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "RegisterAppHookWindowInfoFunc"; - info.bundleName_ = "RegisterAppHookWindowInfoFunc"; - sptr sceneSession = sptr::MakeSptr(info, nullptr); - sceneSession->getHookWindowInfoFunc_ = nullptr; - - // Case 1: func is not nullptr - sceneSession->RegisterAppHookWindowInfoFunc([](const std::string& bundleName) -> HookWindowInfo { - HookWindowInfo hookInfo; - hookInfo.enableHookWindow = true; - return hookInfo; - }); - ASSERT_NE(sceneSession->getHookWindowInfoFunc_, nullptr); - HookWindowInfo hookWindowInfo; - WMError errCode = sceneSession->GetAppHookWindowInfoFromServer(hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_OK); - EXPECT_EQ(hookWindowInfo.enableHookWindow, true); - - // Case 2: func is nullptr - sceneSession->RegisterAppHookWindowInfoFunc(nullptr); - ASSERT_NE(sceneSession->getHookWindowInfoFunc_, nullptr); -} - /** * @tc.name: GetWindowDragMoveMountedNode01 * @tc.desc: GetWindowDragMoveMountedNode @@ -1582,6 +2729,151 @@ HWTEST_F(SceneSessionLayoutTest, ShouldSkipUpdateRectNotify, TestSize.Level0) session->SetSessionRect({ 0, 0, 800, 800}); EXPECT_EQ(true, session->ShouldSkipUpdateRectNotify(rect)); } +/** + * @tc.name: SyncAllAttachedLimitsToAttachingChild01 + * @tc.desc: Test SyncAllAttachedLimitsToAttachingChild with null parentSession + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, SyncAllAttachedLimitsToAttachingChild01, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "SyncAllAttachedLimitsToAttachingChild01"; + info.bundleName_ = "SyncAllAttachedLimitsToAttachingChild01"; + + sptr session = sptr::MakeSptr(info, nullptr); + session->sessionStage_ = mockSessionStage_; + + // Call with null parent - should return early + session->SyncAllAttachedLimitsToAttachingChild(nullptr); + + // Verify no IPC call was made + auto limitsList = session->GetSessionProperty()->GetAttachedWindowLimitsList(); + EXPECT_TRUE(limitsList.empty()); +} + +/** + * @tc.name: SyncAllAttachedLimitsToAttachingChild02 + * @tc.desc: Test SyncAllAttachedLimitsToAttachingChild with null sessionStage + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, SyncAllAttachedLimitsToAttachingChild02, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "SyncAllAttachedLimitsToAttachingChild02"; + info.bundleName_ = "SyncAllAttachedLimitsToAttachingChild02"; + + sptr parentSession = sptr::MakeSptr(info, nullptr); + sptr childSession = sptr::MakeSptr(info, nullptr); + // Don't set sessionStage_ - it stays null + + // Call with null sessionStage - should return early + childSession->SyncAllAttachedLimitsToAttachingChild(parentSession); + + // Verify no crash + auto limitsList = childSession->GetSessionProperty()->GetAttachedWindowLimitsList(); + EXPECT_TRUE(limitsList.empty()); +} + +/** + * @tc.name: SyncAllAttachedLimitsToAttachingChild03 + * @tc.desc: Test SyncAllAttachedLimitsToAttachingChild with parent that has no attached limits + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, SyncAllAttachedLimitsToAttachingChild03, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "SyncAllAttachedLimitsToAttachingChild03"; + info.bundleName_ = "SyncAllAttachedLimitsToAttachingChild03"; + + // Create parent with limits for attached windows + sptr parentSession = sptr::MakeSptr(info, nullptr); + WindowLimits parentLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + parentSession->GetSessionProperty()->SetLimitsForAttachedWindows(parentLimits); + + // Create child with mock sessionStage + sptr childSession = sptr::MakeSptr(info, nullptr); + sptr childMockStage = sptr::MakeSptr(); + childSession->sessionStage_ = childMockStage; + + // Expect IPC call with only parent's own limits (no other attached windows) + EXPECT_CALL(*childMockStage, SyncAllAttachedLimitsToChild( + testing::SizeIs(1), testing::SizeIs(1))) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + childSession->SyncAllAttachedLimitsToAttachingChild(parentSession); +} + +/** + * @tc.name: SyncAllAttachedLimitsToAttachingChild04 + * @tc.desc: Test SyncAllAttachedLimitsToAttachingChild with parent that has existing attached limits + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, SyncAllAttachedLimitsToAttachingChild04, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "SyncAllAttachedLimitsToAttachingChild04"; + info.bundleName_ = "SyncAllAttachedLimitsToAttachingChild04"; + + // Create parent with limits for attached windows + sptr parentSession = sptr::MakeSptr(info, nullptr); + WindowLimits parentLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + parentSession->GetSessionProperty()->SetLimitsForAttachedWindows(parentLimits); + + // Simulate parent already has an attached sub-window's limits + WindowLimits subLimits = { 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + parentSession->GetSessionProperty()->SetAttachedWindowLimits(500, subLimits); + parentSession->GetSessionProperty()->SetAttachedLimitOptions(500, AttachLimitOptions{ true, false }); + + // Create child with mock sessionStage + sptr childSession = sptr::MakeSptr(info, nullptr); + sptr childMockStage = sptr::MakeSptr(); + childSession->sessionStage_ = childMockStage; + + // Expect IPC call with parent's limits + 1 other attached window = 2 entries + EXPECT_CALL(*childMockStage, SyncAllAttachedLimitsToChild( + testing::SizeIs(2), testing::SizeIs(2))) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + childSession->SyncAllAttachedLimitsToAttachingChild(parentSession); +} + +/** + * @tc.name: SyncAllAttachedLimitsToAttachingChild05 + * @tc.desc: Test SyncAllAttachedLimitsToAttachingChild with multiple attached sub-windows + * @tc.type: FUNC + */ +HWTEST_F(SceneSessionLayoutTest, SyncAllAttachedLimitsToAttachingChild05, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "SyncAllAttachedLimitsToAttachingChild05"; + info.bundleName_ = "SyncAllAttachedLimitsToAttachingChild05"; + + // Create parent with limits for attached windows + sptr parentSession = sptr::MakeSptr(info, nullptr); + WindowLimits parentLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + parentSession->GetSessionProperty()->SetLimitsForAttachedWindows(parentLimits); + + // Simulate parent already has 2 attached sub-windows + WindowLimits subLimits1 = { 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits subLimits2 = { 1800, 900, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + parentSession->GetSessionProperty()->SetAttachedWindowLimits(501, subLimits1); + parentSession->GetSessionProperty()->SetAttachedLimitOptions(501, AttachLimitOptions{ true, false }); + parentSession->GetSessionProperty()->SetAttachedWindowLimits(502, subLimits2); + parentSession->GetSessionProperty()->SetAttachedLimitOptions(502, AttachLimitOptions{ false, true }); + + // Create child with mock sessionStage + sptr childSession = sptr::MakeSptr(info, nullptr); + sptr childMockStage = sptr::MakeSptr(); + childSession->sessionStage_ = childMockStage; + + // Expect IPC call with parent's limits + 2 other attached windows = 3 entries + EXPECT_CALL(*childMockStage, SyncAllAttachedLimitsToChild( + testing::SizeIs(3), testing::SizeIs(3))) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + childSession->SyncAllAttachedLimitsToAttachingChild(parentSession); +} + } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/layout/scene_session_manager_layout_test.cpp b/window_scene/test/unittest/layout/scene_session_manager_layout_test.cpp index 05e6722b65..4653360bbc 100644 --- a/window_scene/test/unittest/layout/scene_session_manager_layout_test.cpp +++ b/window_scene/test/unittest/layout/scene_session_manager_layout_test.cpp @@ -347,85 +347,6 @@ HWTEST_F(SceneSessionManagerLayoutTest, UpdateWindowModeByIdForUITest01, TestSiz EXPECT_EQ(ssm_->UpdateWindowModeByIdForUITest(windowId, updateMode), WMError::WM_ERROR_INVALID_PERMISSION); } -/** - * @tc.name: GetAppHookWindowInfo - * @tc.desc: test function : GetAppHookWindowInfo - * @tc.type: FUNC - */ -HWTEST_F(SceneSessionManagerLayoutTest, GetAppHookWindowInfo, TestSize.Level1) -{ - ASSERT_TRUE(ssm_ != nullptr); - - // Case 1: empty bundleName - std::string bundleName = ""; - HookWindowInfo hookWindowInfo = ssm_->GetAppHookWindowInfo(bundleName); - EXPECT_EQ(hookWindowInfo.enableHookWindow, false); - - // Case 2: bundleName not found - bundleName = "GetAppHookWindowInfo_Test"; - hookWindowInfo = ssm_->GetAppHookWindowInfo(bundleName); - EXPECT_EQ(hookWindowInfo.enableHookWindow, false); - - // Case 3: success - HookWindowInfo hookWindowInfo2; - hookWindowInfo2.enableHookWindow = true; - hookWindowInfo2.widthHookRatio = 0.5f; - ssm_->appHookWindowInfoMap_[bundleName] = hookWindowInfo2; - hookWindowInfo = ssm_->GetAppHookWindowInfo(bundleName); - EXPECT_EQ(hookWindowInfo.enableHookWindow, true); -} - -/** - * @tc.name: UpdateAppHookWindowInfo - * @tc.desc: test function : UpdateAppHookWindowInfo - * @tc.type: FUNC - */ -HWTEST_F(SceneSessionManagerLayoutTest, UpdateAppHookWindowInfo, TestSize.Level1) -{ - ASSERT_TRUE(ssm_ != nullptr); - - // Case 1: empty bundleName - std::string bundleName = ""; - HookWindowInfo hookWindowInfo; - WMError errCode = ssm_->UpdateAppHookWindowInfo(bundleName, hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_ERROR_NULLPTR); - - // Case 2: Invalid hook window parameters - bundleName = "UpdateAppHookWindowInfo_Test"; - hookWindowInfo.widthHookRatio = -0.5f; - errCode = ssm_->UpdateAppHookWindowInfo(bundleName, hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_ERROR_INVALID_PARAM); - - // Case 3: not found session - hookWindowInfo.enableHookWindow = true; - hookWindowInfo.widthHookRatio = 0.5f; - ssm_->sceneSessionMap_.insert({ 999, nullptr }); - errCode = ssm_->UpdateAppHookWindowInfo(bundleName, hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_OK); - ssm_->appHookWindowInfoMap_.clear(); - - // Case 4: bundleName not found - SessionInfo sessionInfo; - sessionInfo.bundleName_ = bundleName; - sessionInfo.abilityName_ = bundleName; - sptr sceneSession = ssm_->CreateSceneSession(sessionInfo, nullptr); - sceneSession->GetSessionProperty()->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); - ssm_->sceneSessionMap_.insert({ sceneSession->GetPersistentId(), sceneSession }); - errCode = ssm_->UpdateAppHookWindowInfo("randomBundleName", hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_OK); - ssm_->appHookWindowInfoMap_.clear(); - - // Case 5: success - errCode = ssm_->UpdateAppHookWindowInfo(bundleName, hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_OK); - EXPECT_NE(0, ssm_->appHookWindowInfoMap_.count(bundleName)); - - // Case 6: Repeat update - errCode = ssm_->UpdateAppHookWindowInfo(bundleName, hookWindowInfo); - EXPECT_EQ(errCode, WMError::WM_OK); - EXPECT_NE(0, ssm_->appHookWindowInfoMap_.count(bundleName)); -} - /** * @tc.name: UpdateAppHookWindowInfoWhenSwitchFreeMultiWindow * @tc.desc: test function : UpdateAppHookWindowInfoWhenSwitchFreeMultiWindow @@ -445,15 +366,15 @@ HWTEST_F(SceneSessionManagerLayoutTest, UpdateAppHookWindowInfoWhenSwitchFreeMul HookWindowInfo hookWindowInfo; hookWindowInfo.enableHookWindow = true; hookWindowInfo.widthHookRatio = 0.5f; - ssm_->appHookWindowInfoMap_[bundleName] = hookWindowInfo; + sceneSession->GetSessionProperty()->SetHookWindowInfo(hookWindowInfo); // Case 1: open freeMultiWindow ssm_->UpdateAppHookWindowInfoWhenSwitchFreeMultiWindow(true); - EXPECT_EQ(ssm_->appHookWindowInfoMap_[bundleName].enableHookWindow, false); + EXPECT_EQ(sceneSession->GetSessionProperty()->GetHookWindowInfo().enableHookWindow, false); // Case 2: close freeMultiWindow ssm_->UpdateAppHookWindowInfoWhenSwitchFreeMultiWindow(false); - EXPECT_EQ(ssm_->appHookWindowInfoMap_[bundleName].enableHookWindow, true); + EXPECT_EQ(sceneSession->GetSessionProperty()->GetHookWindowInfo().enableHookWindow, true); } /** @@ -550,7 +471,7 @@ HWTEST_F(SceneSessionManagerLayoutTest, GetAllWindowLayoutInfo, TestSize.Level1) HookWindowInfo hookWindowInfo; hookWindowInfo.enableHookWindow = true; hookWindowInfo.widthHookRatio = 1.0f; - ssm_->appHookWindowInfoMap_[bundleName] = hookWindowInfo; + sceneSession->GetSessionProperty()->SetHookWindowInfo(hookWindowInfo); std::vector> info; ssm_->GetAllWindowLayoutInfo(TEST_DISPLAY_ID, info); @@ -558,7 +479,7 @@ HWTEST_F(SceneSessionManagerLayoutTest, GetAllWindowLayoutInfo, TestSize.Level1) EXPECT_EQ(800, info[0]->rect.width_); hookWindowInfo.widthHookRatio = 0.5f; - ssm_->appHookWindowInfoMap_[bundleName] = hookWindowInfo; + sceneSession->GetSessionProperty()->SetHookWindowInfo(hookWindowInfo); info.clear(); ssm_->GetAllWindowLayoutInfo(TEST_DISPLAY_ID, info); ASSERT_NE(info.size(), 0); diff --git a/window_scene/test/unittest/layout/session_layout_test.cpp b/window_scene/test/unittest/layout/session_layout_test.cpp index 441691ac03..7a823af00c 100644 --- a/window_scene/test/unittest/layout/session_layout_test.cpp +++ b/window_scene/test/unittest/layout/session_layout_test.cpp @@ -407,27 +407,6 @@ HWTEST_F(SessionLayoutTest, SetGetRsCmdBlockingCountFunc, TestSize.Level1) }); ASSERT_NE(nullptr, session->getRsCmdBlockingCountFunc_); } - -/** - * @tc.name: NotifyAppHookWindowInfoUpdated - * @tc.desc: NotifyAppHookWindowInfoUpdated - * @tc.type: FUNC - */ -HWTEST_F(SessionLayoutTest, NotifyAppHookWindowInfoUpdated, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "NotifyAppHookWindowInfoUpdated"; - info.bundleName_ = "NotifyAppHookWindowInfoUpdated"; - sptr session = sptr::MakeSptr(info); - - session->sessionStage_ = nullptr; - WSError errCode = session->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(errCode, WSError::WS_ERROR_NULLPTR); - - session->sessionStage_ = sptr::MakeSptr(); - errCode = session->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(errCode, WSError::WS_OK); -} } // namespace } // namespace Rosen } // namespace OHOS diff --git a/window_scene/test/unittest/layout/session_stub_layout_test.cpp b/window_scene/test/unittest/layout/session_stub_layout_test.cpp index 5f48ef5233..dc29974b25 100644 --- a/window_scene/test/unittest/layout/session_stub_layout_test.cpp +++ b/window_scene/test/unittest/layout/session_stub_layout_test.cpp @@ -21,6 +21,7 @@ #include "iremote_object_mocker.h" #include "mock/mock_session_stub.h" #include "parcel/accessibility_event_info_parcel.h" +#include "session/host/include/scene_session.h" #include "session/host/include/zidl/session_ipc_interface_code.h" #include "session/host/include/zidl/session_stub.h" #include "want.h" @@ -136,35 +137,105 @@ HWTEST_F(SessionStubLayoutTest, HandleSetSystemEnableDrag_TestReadBool, TestSize res = session_->HandleSetSystemEnableDrag(data, reply); ASSERT_EQ(ERR_NONE, res); } - /** - * @tc.name: HandleGetAppHookWindowInfoFromServer - * @tc.desc: HandleGetAppHookWindowInfoFromServer01 + * @tc.name: HandleNotifyAttachedWindowsLimitsChanged01 + * @tc.desc: Test HandleNotifyAttachedWindowsLimitsChanged with valid data * @tc.type: FUNC */ -HWTEST_F(SessionStubLayoutTest, HandleGetAppHookWindowInfoFromServer01, TestSize.Level1) +HWTEST_F(SessionStubLayoutTest, HandleNotifyAttachedWindowsLimitsChanged01, TestSize.Level1) { - ASSERT_TRUE(session_ != nullptr); + SessionInfo info; + auto session = sptr::MakeSptr(info, nullptr); MessageParcel data; MessageParcel reply; - MessageOption option = { MessageOption::TF_SYNC }; - uint32_t code = static_cast(SessionInterfaceCode::TRANS_ID_GET_HOOK_WINDOW_INFO); - auto res = session_->ProcessRemoteRequest(code, data, reply, option); + + WindowLimits newLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + newLimits.Marshalling(data); + + auto res = session->HandleNotifyAttachedWindowsLimitsChanged(data, reply); EXPECT_EQ(ERR_NONE, res); } /** - * @tc.name: HandleGetAppHookWindowInfoFromServer - * @tc.desc: HandleGetAppHookWindowInfoFromServer02 + * @tc.name: HandleNotifyAttachedWindowsLimitsChanged02 + * @tc.desc: Test HandleNotifyAttachedWindowsLimitsChanged with read limits failed * @tc.type: FUNC */ -HWTEST_F(SessionStubLayoutTest, HandleGetAppHookWindowInfoFromServer02, TestSize.Level1) +HWTEST_F(SessionStubLayoutTest, HandleNotifyAttachedWindowsLimitsChanged02, TestSize.Level1) { - ASSERT_TRUE(session_ != nullptr); + SessionInfo info; + auto session = sptr::MakeSptr(info, nullptr); MessageParcel data; MessageParcel reply; - auto res = session_->HandleGetAppHookWindowInfoFromServer(data, reply); - EXPECT_EQ(res, ERR_NONE); + // Don't write limits - will fail to read + + auto res = session->HandleNotifyAttachedWindowsLimitsChanged(data, reply); + EXPECT_EQ(ERR_INVALID_DATA, res); +} + +/** + * @tc.name: HandleNotifyAttachedWindowsLimitsChanged03 + * @tc.desc: Test HandleNotifyAttachedWindowsLimitsChanged with height only + * @tc.type: FUNC + */ +HWTEST_F(SessionStubLayoutTest, HandleNotifyAttachedWindowsLimitsChanged03, TestSize.Level1) +{ + SessionInfo info; + auto session = sptr::MakeSptr(info, nullptr); + MessageParcel data; + MessageParcel reply; + + WindowLimits newLimits = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + newLimits.Marshalling(data); + + auto res = session->HandleNotifyAttachedWindowsLimitsChanged(data, reply); + EXPECT_EQ(ERR_NONE, res); +} + +/** + * @tc.name: HandleNotifyAttachedWindowsLimitsChanged04 + * @tc.desc: Test HandleNotifyAttachedWindowsLimitsChanged with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(SessionStubLayoutTest, HandleNotifyAttachedWindowsLimitsChanged04, TestSize.Level1) +{ + SessionInfo info; + auto session = sptr::MakeSptr(info, nullptr); + MessageParcel data; + MessageParcel reply; + + WindowLimits newLimits = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + newLimits.Marshalling(data); + + auto res = session->HandleNotifyAttachedWindowsLimitsChanged(data, reply); + EXPECT_EQ(ERR_NONE, res); +} + +/** + * @tc.name: HandleNotifyAttachedWindowsLimitsChanged05 + * @tc.desc: Test HandleNotifyAttachedWindowsLimitsChanged with invalid pixelUnit value + * @tc.type: FUNC + */ +HWTEST_F(SessionStubLayoutTest, HandleNotifyAttachedWindowsLimitsChanged05, TestSize.Level1) +{ + SessionInfo info; + auto session = sptr::MakeSptr(info, nullptr); + MessageParcel data; + MessageParcel reply; + + // Write WindowLimits data manually with invalid pixelUnit value + data.WriteUint32(1600); // maxWidth_ + data.WriteUint32(800); // maxHeight_ + data.WriteUint32(100); // minWidth_ + data.WriteUint32(200); // minHeight_ + data.WriteFloat(0.0f); // maxRatio_ + data.WriteFloat(0.0f); // minRatio_ + data.WriteFloat(0.0f); // vpRatio_ + data.WriteUint32(999); // Invalid pixelUnit (valid values are 0=PX, 1=VP) + + // WindowLimits::Unmarshalling will fail due to invalid pixelUnit + auto res = session->HandleNotifyAttachedWindowsLimitsChanged(data, reply); + EXPECT_EQ(ERR_INVALID_DATA, res); } } // namespace } // namespace Rosen diff --git a/window_scene/test/unittest/layout/sub_session_layout_test.cpp b/window_scene/test/unittest/layout/sub_session_layout_test.cpp index b3f01e510c..269a263789 100644 --- a/window_scene/test/unittest/layout/sub_session_layout_test.cpp +++ b/window_scene/test/unittest/layout/sub_session_layout_test.cpp @@ -14,12 +14,14 @@ */ #include +#include #include #include "session/host/include/session.h" #include "session/host/include/main_session.h" #include "session/host/include/sub_session.h" #include "session/screen/include/screen_session.h" +#include "test/mock/mock_session_stage.h" #include "window_helper.h" #include "window_manager_hilog.h" #include "window_property.h" @@ -39,6 +41,17 @@ public: private: RSSurfaceNode::SharedPtr CreateRSSurfaceNode(); + +protected: + // Helper function to create SessionInfo with test name + SessionInfo CreateSessionInfo(const std::string& name) const + { + SessionInfo info; + info.bundleName_ = name; + info.moduleName_ = name; + info.abilityName_ = name; + return info; + } }; void SubSessionLayoutTest::SetUpTestCase() {} @@ -93,6 +106,191 @@ HWTEST_F(SubSessionLayoutTest, HandleCrossSurfaceNodeByWindowAnchor, TestSize.Le sceneSession->HandleCrossSurfaceNodeByWindowAnchor(SizeChangeReason::UNDEFINED, 0); EXPECT_EQ(1, sceneSession->cloneNodeCountDuringCross_.load()); } + +/** + * @tc.name: RequestUpdateAttachedWindowLimits01 + * @tc.desc: Test sub window updates own limits only + * @tc.type: FUNC + */ +HWTEST_F(SubSessionLayoutTest, RequestUpdateAttachedWindowLimits01, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestUpdateAttachedWindowLimits01"); + sptr subSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 1001; + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + bool isIntersectedHeightLimit = true; + bool isIntersectedWidthLimit = true; + + // Test with null sessionStage_ + subSession->sessionStage_ = nullptr; + WSError ret = subSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, ret); + + // Test with valid sessionStage_ + sptr sessionStageMock = sptr::MakeSptr(); + subSession->sessionStage_ = sessionStageMock; + + EXPECT_CALL(*sessionStageMock, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, isIntersectedHeightLimit, isIntersectedWidthLimit)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + ret = subSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimits02 + * @tc.desc: Test sub window with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(SubSessionLayoutTest, RequestUpdateAttachedWindowLimits02, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestUpdateAttachedWindowLimits02"); + sptr subSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 1002; + WindowLimits attachedLimits = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + bool isIntersectedHeightLimit = false; + bool isIntersectedWidthLimit = true; + + // Test with null sessionStage_ + subSession->sessionStage_ = nullptr; + WSError ret = subSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, ret); + + // Test with valid sessionStage_ + sptr sessionStageMock = sptr::MakeSptr(); + subSession->sessionStage_ = sessionStageMock; + + EXPECT_CALL(*sessionStageMock, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, isIntersectedHeightLimit, isIntersectedWidthLimit)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + ret = subSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits01 + * @tc.desc: Test sub window removes own limits only + * @tc.type: FUNC + */ +HWTEST_F(SubSessionLayoutTest, RequestRemoveAttachedWindowLimits01, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestRemoveAttachedWindowLimits01"); + sptr subSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 2001; + + // Test with null sessionStage_ + subSession->sessionStage_ = nullptr; + WSError ret = subSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_ERROR_NULLPTR, ret); + + // Test with valid sessionStage_ + sptr sessionStageMock = sptr::MakeSptr(); + subSession->sessionStage_ = sessionStageMock; + + EXPECT_CALL(*sessionStageMock, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + ret = subSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_OK, ret); +} + +/** + * @tc.name: RequestUpdateAttachedWindowLimits03 + * @tc.desc: Test sub window RequestUpdateAttachedWindowLimits with failure + * @tc.type: FUNC + */ +HWTEST_F(SubSessionLayoutTest, RequestUpdateAttachedWindowLimits03, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestUpdateAttachedWindowLimits03"); + sptr subSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 1003; + WindowLimits attachedLimits = { 1600, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + bool isIntersectedHeightLimit = true; + bool isIntersectedWidthLimit = false; + + // Test with valid sessionStage_ but UpdateAttachedWindowLimits fails + sptr sessionStageMock = sptr::MakeSptr(); + subSession->sessionStage_ = sessionStageMock; + + EXPECT_CALL(*sessionStageMock, UpdateAttachedWindowLimits( + sourcePersistentId, testing::_, isIntersectedHeightLimit, isIntersectedWidthLimit)) + .Times(1).WillOnce(testing::Return(WSError::WS_ERROR_IPC_FAILED)); + + WSError ret = subSession->RequestUpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, ret); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits02 + * @tc.desc: Test sub window RequestRemoveAttachedWindowLimits with failure + * @tc.type: FUNC + */ +HWTEST_F(SubSessionLayoutTest, RequestRemoveAttachedWindowLimits02, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestRemoveAttachedWindowLimits02"); + sptr subSession = sptr::MakeSptr(info, nullptr); + + int32_t sourcePersistentId = 2002; + + // Test with valid sessionStage_ but RemoveAttachedWindowLimits fails + sptr sessionStageMock = sptr::MakeSptr(); + subSession->sessionStage_ = sessionStageMock; + + EXPECT_CALL(*sessionStageMock, RemoveAttachedWindowLimits(sourcePersistentId)) + .Times(1).WillOnce(testing::Return(WSError::WS_ERROR_IPC_FAILED)); + + WSError ret = subSession->RequestRemoveAttachedWindowLimits(sourcePersistentId, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, ret); +} + +/** + * @tc.name: RequestRemoveAttachedWindowLimits03 + * @tc.desc: Test sub window RequestRemoveAttachedWindowLimits when sourcePersistentId == winId (detaching) + * @tc.type: FUNC + */ +HWTEST_F(SubSessionLayoutTest, RequestRemoveAttachedWindowLimits03, TestSize.Level1) +{ + auto info = CreateSessionInfo("RequestRemoveAttachedWindowLimits03"); + sptr subSession = sptr::MakeSptr(info, nullptr); + + sptr sessionStageMock = sptr::MakeSptr(); + subSession->sessionStage_ = sessionStageMock; + + // Pre-populate attached limits lists to verify they get cleared + auto property = subSession->GetSessionProperty(); + property->SetAttachedWindowLimits(100, { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + property->SetAttachedWindowLimits(200, { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + AttachLimitOptions options = { true, true }; + property->SetAttachedLimitOptions(100, options); + property->SetAttachedLimitOptions(200, options); + + EXPECT_FALSE(property->GetAttachedWindowLimitsList().empty()); + EXPECT_FALSE(property->GetAttachedLimitOptionsList().empty()); + + // Use subSession's own persistentId as sourcePersistentId (detaching scenario) + int32_t winId = subSession->GetPersistentId(); + + EXPECT_CALL(*sessionStageMock, RemoveAttachedWindowLimits(winId)) + .Times(1).WillOnce(testing::Return(WSError::WS_OK)); + + WSError ret = subSession->RequestRemoveAttachedWindowLimits(winId, INVALID_SESSION_ID); + EXPECT_EQ(WSError::WS_OK, ret); + + // Verify that all attached limits lists were cleared + EXPECT_TRUE(property->GetAttachedWindowLimitsList().empty()); + EXPECT_TRUE(property->GetAttachedLimitOptionsList().empty()); +} } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/main_session_test.cpp b/window_scene/test/unittest/main_session_test.cpp index f14fde2e12..d1a183a48d 100644 --- a/window_scene/test/unittest/main_session_test.cpp +++ b/window_scene/test/unittest/main_session_test.cpp @@ -1129,118 +1129,6 @@ HWTEST_F(MainSessionTest, IsExitSplitOnBackgroundRecover, TestSize.Level1) EXPECT_EQ(session->IsExitSplitOnBackgroundRecover(), true); } -/** - * @tc.name: GetAppForceLandscapeConfigEnable01 - * @tc.desc: Test GetAppForceLandscapeConfigEnable when forceSplitEnableFunc_ is null - * @tc.type: FUNC - */ -HWTEST_F(MainSessionTest, GetAppForceLandscapeConfigEnable01, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "testMainSession1"; - info.moduleName_ = "testMainSession2"; - info.bundleName_ = "testMainSession3"; - sptr session = sptr::MakeSptr(info, nullptr); - ASSERT_NE(session, nullptr); - - bool enableForceSplit = false; - WMError res = session->GetAppForceLandscapeConfigEnable(enableForceSplit); - EXPECT_EQ(res, WMError::WM_ERROR_NULLPTR); -} - -/** - * @tc.name: GetAppForceLandscapeConfigEnable02 - * @tc.desc: Test GetAppForceLandscapeConfigEnable when forceSplitEnableFunc_ is set - * @tc.type: FUNC - */ -HWTEST_F(MainSessionTest, GetAppForceLandscapeConfigEnable02, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "testMainSession1"; - info.moduleName_ = "testMainSession2"; - info.bundleName_ = "testBundle"; - sptr session = sptr::MakeSptr(info, nullptr); - ASSERT_NE(session, nullptr); - - bool expectedEnable = true; - session->RegisterForceSplitEnableListener( - [expectedEnable](const std::string& bundleName) { - return expectedEnable; - }); - - bool enableForceSplit = false; - WMError res = session->GetAppForceLandscapeConfigEnable(enableForceSplit); - EXPECT_EQ(res, WMError::WM_OK); - EXPECT_EQ(enableForceSplit, expectedEnable); -} - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated01 - * @tc.desc: Test NotifyAppForceLandscapeConfigEnableUpdated when sessionStage_ is null - * @tc.type: FUNC - */ -HWTEST_F(MainSessionTest, NotifyAppForceLandscapeConfigEnableUpdated01, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "testMainSession1"; - info.moduleName_ = "testMainSession2"; - info.bundleName_ = "testMainSession3"; - sptr session = sptr::MakeSptr(info, nullptr); - ASSERT_NE(session, nullptr); - session->sessionStage_ = nullptr; - - WSError res = session->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_ERROR_NULLPTR); -} - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated02 - * @tc.desc: Test NotifyAppForceLandscapeConfigEnableUpdated when sessionStage_ is set - * @tc.type: FUNC - */ -HWTEST_F(MainSessionTest, NotifyAppForceLandscapeConfigEnableUpdated02, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "testMainSession1"; - info.moduleName_ = "testMainSession2"; - info.bundleName_ = "testMainSession3"; - sptr session = sptr::MakeSptr(info, nullptr); - ASSERT_NE(session, nullptr); - session->sessionStage_ = sptr::MakeSptr(); - - WSError res = session->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_OK); -} - -/** - * @tc.name: RegisterForceSplitEnableListener - * @tc.desc: Test RegisterForceSplitEnableListener - * @tc.type: FUNC - */ -HWTEST_F(MainSessionTest, RegisterForceSplitEnableListener, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "testMainSession1"; - info.moduleName_ = "testMainSession2"; - info.bundleName_ = "testBundle"; - sptr session = sptr::MakeSptr(info, nullptr); - ASSERT_NE(session, nullptr); - - bool callbackCalled = false; - bool callbackResult = true; - session->RegisterForceSplitEnableListener( - [&callbackCalled, callbackResult](const std::string& bundleName) { - callbackCalled = true; - return callbackResult; - }); - - bool enableForceSplit = false; - WMError res = session->GetAppForceLandscapeConfigEnable(enableForceSplit); - EXPECT_EQ(res, WMError::WM_OK); - EXPECT_TRUE(callbackCalled); - EXPECT_EQ(enableForceSplit, callbackResult); -} - /** * @tc.name: Prelaunch * @tc.desc: Test Prelaunch @@ -1382,6 +1270,266 @@ HWTEST_F(MainSessionTest, NotifyPageEnable01, TestSize.Level1) EXPECT_EQ(std::get<2>(callbackCalls[2]), "enter"); EXPECT_EQ(std::get<3>(callbackCalls[2]), "Page2"); } + +/** + * @tc.name: UpdateHookWindowInfo01 + * @tc.desc: Test UpdateHookWindowInfo with drawableRectHook change + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, UpdateHookWindowInfo01, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "UpdateHookWindowInfo01"; + info.abilityName_ = "UpdateHookWindowInfo01"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + sptr property = sptr::MakeSptr(); + session->SetSessionProperty(property); + session->sessionStage_ = sptr::MakeSptr(); + + HookWindowInfo initialInfo; + initialInfo.enableHookWindow = true; + initialInfo.widthHookRatio = 0.5f; + initialInfo.drawableRectHook = false; + session->GetSessionProperty()->SetHookWindowInfo(initialInfo); + + HookWindowInfo newInfo; + newInfo.enableHookWindow = true; + newInfo.widthHookRatio = 0.5f; + newInfo.drawableRectHook = true; + + auto ret = session->UpdateHookWindowInfo(newInfo); + EXPECT_EQ(ret, WSError::WS_OK); + EXPECT_EQ(session->GetSessionProperty()->GetHookWindowInfo().drawableRectHook, true); +} + +/** + * @tc.name: UpdateHookWindowInfo02 + * @tc.desc: Test UpdateHookWindowInfo with no change (drawableRectHook same) + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, UpdateHookWindowInfo02, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "UpdateHookWindowInfo02"; + info.abilityName_ = "UpdateHookWindowInfo02"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + sptr property = sptr::MakeSptr(); + session->SetSessionProperty(property); + + HookWindowInfo initialInfo; + initialInfo.enableHookWindow = true; + initialInfo.widthHookRatio = 0.5f; + initialInfo.drawableRectHook = true; + session->GetSessionProperty()->SetHookWindowInfo(initialInfo); + + HookWindowInfo newInfo; + newInfo.enableHookWindow = true; + newInfo.widthHookRatio = 0.5f; + newInfo.drawableRectHook = true; + + auto ret = session->UpdateHookWindowInfo(newInfo); + EXPECT_EQ(ret, WSError::WS_OK); +} + +/** + * @tc.name: UpdateHookWindowInfo03 + * @tc.desc: Test UpdateHookWindowInfo with invalid widthHookRatio + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, UpdateHookWindowInfo03, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "UpdateHookWindowInfo03"; + info.abilityName_ = "UpdateHookWindowInfo03"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + HookWindowInfo invalidInfo; + invalidInfo.widthHookRatio = -1.0f; + + auto ret = session->UpdateHookWindowInfo(invalidInfo); + EXPECT_EQ(ret, WSError::WS_ERROR_INVALID_PARAM); +} + +/** + * @tc.name: UpdateHookWindowInfo04 + * @tc.desc: Test UpdateHookWindowInfo when property is nullptr + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, UpdateHookWindowInfo04, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "UpdateHookWindowInfo04"; + info.abilityName_ = "UpdateHookWindowInfo04"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + session->property_ = nullptr; + + HookWindowInfo hookInfo; + hookInfo.widthHookRatio = 0.5f; + + auto ret = session->UpdateHookWindowInfo(hookInfo); + EXPECT_EQ(ret, WSError::WS_ERROR_NULLPTR); +} + +/** + * @tc.name: SetForceSplitEnable01 + * @tc.desc: Test SetForceSplitEnable when setSelectModeCallback_ is nullptr + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, SetForceSplitEnable01, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "SetForceSplitEnable01"; + info.abilityName_ = "SetForceSplitEnable01"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + session->setSelectModeCallback_ = nullptr; + + auto ret = session->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE); + EXPECT_EQ(ret, WSError::WS_ERROR_NULLPTR); +} + +/** + * @tc.name: SetForceSplitEnable02 + * @tc.desc: Test SetForceSplitEnable with callback registered and property nullptr + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, SetForceSplitEnable02, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "SetForceSplitEnable02"; + info.abilityName_ = "SetForceSplitEnable02"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + SelectMode receivedSelectMode = SelectMode::WIDE_MODE; + session->RegisterSetSelectModeCallback([&receivedSelectMode](SelectMode selectMode) { + receivedSelectMode = selectMode; + }); + session->property_ = nullptr; + + auto ret = session->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE); + EXPECT_EQ(receivedSelectMode, SelectMode::WIDE_MODE); + EXPECT_EQ(ret, WSError::WS_ERROR_NULLPTR); +} + +/** + * @tc.name: SetForceSplitEnable03 + * @tc.desc: Test SetForceSplitEnable with callback and sessionStage nullptr + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, SetForceSplitEnable03, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "SetForceSplitEnable03"; + info.abilityName_ = "SetForceSplitEnable03"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + sptr property = sptr::MakeSptr(); + session->SetSessionProperty(property); + + SelectMode receivedSelectMode = SelectMode::WIDE_MODE; + session->RegisterSetSelectModeCallback([&receivedSelectMode](SelectMode selectMode) { + receivedSelectMode = selectMode; + }); + session->sessionStage_ = nullptr; + + auto ret = session->SetForceSplitEnable(true, false, SelectMode::SQUARE_MODE); + EXPECT_EQ(receivedSelectMode, SelectMode::SQUARE_MODE); + EXPECT_EQ(ret, WSError::WS_ERROR_NULLPTR); +} + +/** + * @tc.name: SetForceSplitEnable04 + * @tc.desc: Test SetForceSplitEnable success with callback + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, SetForceSplitEnable04, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "SetForceSplitEnable04"; + info.abilityName_ = "SetForceSplitEnable04"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + sptr property = sptr::MakeSptr(); + session->SetSessionProperty(property); + session->sessionStage_ = sptr::MakeSptr(); + + SelectMode receivedSelectMode = SelectMode::WIDE_MODE; + session->RegisterSetSelectModeCallback([&receivedSelectMode](SelectMode selectMode) { + receivedSelectMode = selectMode; + }); + + auto ret = session->SetForceSplitEnable(true, true, SelectMode::SQUARE_MODE); + EXPECT_EQ(receivedSelectMode, SelectMode::SQUARE_MODE); + EXPECT_EQ(ret, WSError::WS_OK); + EXPECT_EQ(session->GetSessionProperty()->GetForceSplitEnable(), true); +} + +/** + * @tc.name: SetForceSplitEnable05 + * @tc.desc: Test SetForceSplitEnable when sessionStage SetForceSplitEnable fails + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, SetForceSplitEnable05, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "SetForceSplitEnable05"; + info.abilityName_ = "SetForceSplitEnable05"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + sptr property = sptr::MakeSptr(); + session->SetSessionProperty(property); + + auto sessionStageMock = sptr::MakeSptr(); + session->sessionStage_ = sessionStageMock; + + SelectMode receivedSelectMode = SelectMode::WIDE_MODE; + session->RegisterSetSelectModeCallback([&receivedSelectMode](SelectMode selectMode) { + receivedSelectMode = selectMode; + }); + + // Mock sessionStage SetForceSplitEnable to return error + EXPECT_CALL(*sessionStageMock, SetForceSplitEnable(true, false, SelectMode::WIDE_MODE)) + .WillOnce(::testing::Return(WSError::WS_ERROR_IPC_FAILED)); + + auto ret = session->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE); + EXPECT_EQ(receivedSelectMode, SelectMode::WIDE_MODE); + EXPECT_EQ(ret, WSError::WS_ERROR_IPC_FAILED); +} + +/** + * @tc.name: RegisterSetSelectModeCallback01 + * @tc.desc: Test RegisterSetSelectModeCallback + * @tc.type: FUNC + */ +HWTEST_F(MainSessionTest, RegisterSetSelectModeCallback01, TestSize.Level1) +{ + SessionInfo info; + info.bundleName_ = "RegisterSetSelectModeCallback01"; + info.abilityName_ = "RegisterSetSelectModeCallback01"; + sptr session = sptr::MakeSptr(info, nullptr); + ASSERT_NE(session, nullptr); + + EXPECT_EQ(session->setSelectModeCallback_, nullptr); + + bool callbackCalled = false; + session->RegisterSetSelectModeCallback([&callbackCalled](SelectMode selectMode) { + callbackCalled = true; + }); + + EXPECT_NE(session->setSelectModeCallback_, nullptr); + session->setSelectModeCallback_(SelectMode::WIDE_MODE); + EXPECT_TRUE(callbackCalled); +} } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/scene_session_manager_test11.cpp b/window_scene/test/unittest/scene_session_manager_test11.cpp index 7a8093cfec..8cc8e3d10c 100755 --- a/window_scene/test/unittest/scene_session_manager_test11.cpp +++ b/window_scene/test/unittest/scene_session_manager_test11.cpp @@ -1730,6 +1730,7 @@ HWTEST_F(SceneSessionManagerTest11, WindowVisibilityInfoMarshallingDisplayAndGlo info.rect_ = { 1, 2, 300, 400 }; info.globalDisplayRect_ = { 5, 6, 700, 800 }; info.SetDisplayId(66); + info.SetModuleName("entry"); info.SetGlobalRect({ 10, 20, 100, 200 }); MessageParcel parcel; @@ -1738,6 +1739,7 @@ HWTEST_F(SceneSessionManagerTest11, WindowVisibilityInfoMarshallingDisplayAndGlo WindowVisibilityInfo* unmarshalled = WindowVisibilityInfo::Unmarshalling(parcel); ASSERT_NE(unmarshalled, nullptr); EXPECT_EQ(unmarshalled->GetDisplayId(), 66); + EXPECT_EQ(unmarshalled->GetModuleName(), "entry"); EXPECT_EQ(unmarshalled->GetGlobalRect().posX_, 10); EXPECT_EQ(unmarshalled->GetGlobalRect().posY_, 20); EXPECT_EQ(unmarshalled->GetGlobalRect().width_, 100); diff --git a/window_scene/test/unittest/scene_session_manager_test6.cpp b/window_scene/test/unittest/scene_session_manager_test6.cpp index 6ad5799e5f..2812637e22 100755 --- a/window_scene/test/unittest/scene_session_manager_test6.cpp +++ b/window_scene/test/unittest/scene_session_manager_test6.cpp @@ -1549,10 +1549,11 @@ HWTEST_F(SceneSessionManagerTest6, SetSessionVisibilityInfo02, TestSize.Level1) std::vector> windowVisibilityInfos; std::string visibilityInfo = ""; ASSERT_NE(nullptr, ssm_); - SessionInfo sessionInfo; - sessionInfo.bundleName_ = "SceneSessionManagerTest2"; - sessionInfo.abilityName_ = "DumpSessionWithId"; - sessionInfo.callerPersistentId_ = 2; + SessionInfo sessionInfo; + sessionInfo.bundleName_ = "SceneSessionManagerTest2"; + sessionInfo.moduleName_ = "entry"; + sessionInfo.abilityName_ = "DumpSessionWithId"; + sessionInfo.callerPersistentId_ = 2; auto session1 = sptr::MakeSptr(sessionInfo, nullptr); auto session2 = sptr::MakeSptr(sessionInfo, nullptr); session1->persistentId_ = 1; @@ -1562,11 +1563,12 @@ HWTEST_F(SceneSessionManagerTest6, SetSessionVisibilityInfo02, TestSize.Level1) ssm_->sceneSessionMap_.insert({ 2, session2 }); ssm_->windowVisibilityListenerSessionSet_.clear(); ssm_->windowVisibilityListenerSessionSet_.insert(1); - ssm_->occlusionStateListenerSessionSet_.clear(); - ssm_->occlusionStateListenerSessionSet_.insert(1); - ssm_->SetSessionVisibilityInfo(session1, visibleState, windowVisibilityInfos, visibilityInfo); - EXPECT_NE(windowVisibilityInfos.size(), 0); - ssm_->sceneSessionMap_.clear(); + ssm_->occlusionStateListenerSessionSet_.clear(); + ssm_->occlusionStateListenerSessionSet_.insert(1); + ssm_->SetSessionVisibilityInfo(session1, visibleState, windowVisibilityInfos, visibilityInfo); + EXPECT_NE(windowVisibilityInfos.size(), 0); + EXPECT_EQ(windowVisibilityInfos[0]->GetModuleName(), "entry"); + ssm_->sceneSessionMap_.clear(); ssm_->occlusionStateListenerSessionSet_.clear(); } @@ -2925,4 +2927,4 @@ HWTEST_F(SceneSessionManagerTest6, GetApplicationInfo, TestSize.Level1) } } // namespace } // namespace Rosen -} // namespace OHOS \ No newline at end of file +} // namespace OHOS diff --git a/window_scene/test/unittest/scene_session_manager_test7.cpp b/window_scene/test/unittest/scene_session_manager_test7.cpp index 0368ba3b70..c54b09fb82 100644 --- a/window_scene/test/unittest/scene_session_manager_test7.cpp +++ b/window_scene/test/unittest/scene_session_manager_test7.cpp @@ -1324,7 +1324,7 @@ HWTEST_F(SceneSessionManagerTest7, TestReportIncompleteScreenFoldStatusChangeEve HWTEST_F(SceneSessionManagerTest7, SetAppForceLandscapeConfig, TestSize.Level1) { std::string bundleName = "SetAppForceLandscapeConfig"; - AppForceLandscapeConfig config = { 0, false, false, {}, {}, {}, false, false, false, false }; + AppForceLandscapeConfig config = { {}, {}, {}, false, false, false, false }; WSError result = ssm_->SetAppForceLandscapeConfig(bundleName, config); ASSERT_EQ(result, WSError::WS_OK); } @@ -1351,13 +1351,9 @@ HWTEST_F(SceneSessionManagerTest7, SetAppForceLandscapeConfig02, TestSize.Level1 { std::string bundleName = "com.example.app"; AppForceLandscapeConfig config; - config.mode_ = 5; // 5: FORCE_SPLIT_MODE - config.supportSplit_ = 5; WSError result = ssm_->SetAppForceLandscapeConfig(bundleName, config); EXPECT_EQ(result, WSError::WS_OK); - EXPECT_EQ(ssm_->appForceLandscapeMap_[bundleName].mode_, 5); - EXPECT_EQ(ssm_->appForceLandscapeMap_[bundleName].supportSplit_, 5); } /** @@ -1369,31 +1365,12 @@ HWTEST_F(SceneSessionManagerTest7, SetAppForceLandscapeConfig03, TestSize.Level1 { std::string bundleName = "com.example.app"; AppForceLandscapeConfig preConfig; - preConfig.mode_ = 0; - preConfig.supportSplit_ = -1; ssm_->appForceLandscapeMap_[bundleName] = preConfig; AppForceLandscapeConfig config; - config.mode_ = 5; // 5: FORCE_SPLIT_MODE - config.supportSplit_ = 5; WSError result = ssm_->SetAppForceLandscapeConfig(bundleName, config); EXPECT_EQ(result, WSError::WS_OK); - EXPECT_EQ(ssm_->appForceLandscapeMap_[bundleName].mode_, 5); - EXPECT_EQ(ssm_->appForceLandscapeMap_[bundleName].supportSplit_, 5); -} - -/** - * @tc.name: GetAppForceLandscapeConfig - * @tc.desc: SceneSesionManager GetAppForceLandscapeConfig - * @tc.type: FUNC - */ -HWTEST_F(SceneSessionManagerTest7, GetAppForceLandscapeConfig, TestSize.Level1) -{ - std::string bundleName = "GetAppForceLandscapeConfig"; - AppForceLandscapeConfig config = ssm_->GetAppForceLandscapeConfig(bundleName); - EXPECT_EQ(config.mode_, 0); - EXPECT_EQ(config.supportSplit_, -1); } /** diff --git a/window_scene/test/unittest/session_proxy_layout_test.cpp b/window_scene/test/unittest/session_proxy_layout_test.cpp new file mode 100644 index 0000000000..f84a6a7116 --- /dev/null +++ b/window_scene/test/unittest/session_proxy_layout_test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "iremote_object_mocker.h" +#include "mock_message_parcel.h" +#include "session/host/include/zidl/session_ipc_interface_code.h" +#include "session/host/include/zidl/session_proxy.h" +#include "ws_common.h" + +using namespace testing; +using namespace testing::ext; + +namespace OHOS { +namespace Rosen { +class SessionProxyLayoutTest : public testing::Test { +public: + SessionProxyLayoutTest() : iRemoteObjectMocker_(sptr::MakeSptr()), + sessionProxy_(sptr::MakeSptr(iRemoteObjectMocker_)) {} + ~SessionProxyLayoutTest() = default; + + sptr iRemoteObjectMocker_; + sptr sessionProxy_; +}; + +namespace { +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged01 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with valid limits + * @tc.type: FUNC + */ +HWTEST_F(SessionProxyLayoutTest, NotifyAttachedWindowsLimitsChanged01, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged01 start"; + WindowLimits newLimits = { 200, 1000, 300, 2000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + WSError res = sessionProxy_->NotifyAttachedWindowsLimitsChanged(newLimits); + EXPECT_EQ(res, WSError::WS_OK); + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged01 end"; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged02 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with WriteInterfaceToken error + * @tc.type: FUNC + */ +HWTEST_F(SessionProxyLayoutTest, NotifyAttachedWindowsLimitsChanged02, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged02 start"; + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); + + WindowLimits newLimits = { 100, 800, 200, 1600, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionProxy_->NotifyAttachedWindowsLimitsChanged(newLimits); + EXPECT_EQ(res, WSError::WS_ERROR_IPC_FAILED); + MockMessageParcel::ClearAllErrorFlag(); + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged02 end"; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged03 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with Marshalling error (WriteUint32 fails) + * @tc.type: FUNC + */ +HWTEST_F(SessionProxyLayoutTest, NotifyAttachedWindowsLimitsChanged03, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged03 start"; + WindowLimits newLimits = { 150, 900, 250, 1800, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + MockMessageParcel::SetWriteUint32ErrorFlag(true); + WSError res = sessionProxy_->NotifyAttachedWindowsLimitsChanged(newLimits); + EXPECT_EQ(res, WSError::WS_ERROR_IPC_FAILED); + MockMessageParcel::ClearAllErrorFlag(); + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged03 end"; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged04 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(SessionProxyLayoutTest, NotifyAttachedWindowsLimitsChanged04, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged04 start"; + WindowLimits newLimits = { 50, 500, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WSError res = sessionProxy_->NotifyAttachedWindowsLimitsChanged(newLimits); + EXPECT_EQ(res, WSError::WS_OK); + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged04 end"; +} + +/** + * @tc.name: NotifyAttachedWindowsLimitsChanged05 + * @tc.desc: Test NotifyAttachedWindowsLimitsChanged with null remote object + * @tc.type: FUNC + */ +HWTEST_F(SessionProxyLayoutTest, NotifyAttachedWindowsLimitsChanged05, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged05 start"; + // Create proxy with null remote object + auto sessionProxy = sptr::MakeSptr(nullptr); + + WindowLimits newLimits = { 100, 800, 200, 1600, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionProxy->NotifyAttachedWindowsLimitsChanged(newLimits); + EXPECT_EQ(res, WSError::WS_ERROR_IPC_FAILED); + GTEST_LOG_(INFO) << "SessionProxyLayoutTest: NotifyAttachedWindowsLimitsChanged05 end"; +} + +} // namespace +} // namespace Rosen +} // namespace OHOS diff --git a/window_scene/test/unittest/session_proxy_test.cpp b/window_scene/test/unittest/session_proxy_test.cpp index c43cf615d1..08eb885a6e 100755 --- a/window_scene/test/unittest/session_proxy_test.cpp +++ b/window_scene/test/unittest/session_proxy_test.cpp @@ -2306,49 +2306,7 @@ HWTEST_F(SessionProxyTest, TestUpdateGlobalDisplayRectFromClient, Function | Sma } /** - * @tc.name: GetAppHookWindowInfoFromServer - * @tc.desc: normal function - * @tc.type: FUNC - */ -HWTEST_F(SessionProxyTest, GetAppHookWindowInfoFromServer, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "SessionProxyTest: GetAppHookWindowInfoFromServer start"; - auto mockRemote = sptr::MakeSptr(); - auto sProxy = sptr::MakeSptr(mockRemote); - MockMessageParcel::ClearAllErrorFlag(); - HookWindowInfo hookWindowInfo; - - // Case 1: Failed to write interface token - MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); - EXPECT_EQ(WMError::WM_ERROR_IPC_FAILED, sProxy->GetAppHookWindowInfoFromServer(hookWindowInfo)); - MockMessageParcel::SetWriteInterfaceTokenErrorFlag(false); - - // Case 2: remote is nullptr - sptr nullProxy = sptr::MakeSptr(nullptr); - EXPECT_EQ(WMError::WM_ERROR_IPC_FAILED, nullProxy->GetAppHookWindowInfoFromServer(hookWindowInfo)); - - // Case 3: Failed to send request - mockRemote->SetRequestResult(ERR_TRANSACTION_FAILED); - sptr failSendRequestProxy = sptr::MakeSptr(mockRemote); - EXPECT_EQ(WMError::WM_ERROR_IPC_FAILED, failSendRequestProxy->GetAppHookWindowInfoFromServer(hookWindowInfo)); - mockRemote->SetRequestResult(ERR_NONE); - - // Case 4: Failed to read replyInfo and ret - MockMessageParcel::SetReadBoolErrorFlag(true); - MockMessageParcel::SetReadInt32ErrorFlag(true); - EXPECT_EQ(WMError::WM_ERROR_IPC_FAILED, sProxy->GetAppHookWindowInfoFromServer(hookWindowInfo)); - MockMessageParcel::SetReadBoolErrorFlag(false); - MockMessageParcel::SetReadInt32ErrorFlag(false); - - // Case 5: Success - sptr okProxy = sptr::MakeSptr(mockRemote); - EXPECT_EQ(WMError::WM_OK, okProxy->GetAppHookWindowInfoFromServer(hookWindowInfo)); - MockMessageParcel::ClearAllErrorFlag(); - GTEST_LOG_(INFO) << "SessionProxyTest: GetAppHookWindowInfoFromServer end"; -} - -/** - * @tc.name: GetAppHookWindowInfoFromServer + * @tc.name: NotifyWindowStatusDidChangeAfterShowWindow * @tc.desc: normal function * @tc.type: FUNC */ @@ -2564,69 +2522,6 @@ HWTEST_F(SessionProxyTest, RestartApp, TestSize.Level3) MockMessageParcel::ClearAllErrorFlag(); } -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated - * @tc.desc: Test NotifyAppForceLandscapeConfigEnableUpdated normal function - * @tc.type: FUNC - */ -HWTEST_F(SessionProxyTest, NotifyAppForceLandscapeConfigEnableUpdated, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "SessionProxyTest: NotifyAppForceLandscapeConfigEnableUpdated start"; - auto iRemoteObjectMocker = sptr::MakeSptr(); - ASSERT_NE(iRemoteObjectMocker, nullptr); - auto sProxy = sptr::MakeSptr(iRemoteObjectMocker); - ASSERT_NE(sProxy, nullptr); - auto res = sProxy->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - ASSERT_EQ(res, WSError::WS_OK); - GTEST_LOG_(INFO) << "SessionProxyTest: NotifyAppForceLandscapeConfigEnableUpdated end"; -} - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated01 - * @tc.desc: ShouldReturnIpcFailed_WhenWriteInterfaceTokenFails - * @tc.type: FUNC - */ -HWTEST_F(SessionProxyTest, NotifyAppForceLandscapeConfigEnableUpdated01, TestSize.Level1) -{ - auto iRemoteObjectMocker = sptr::MakeSptr(); - ASSERT_NE(iRemoteObjectMocker, nullptr); - auto sProxy = sptr::MakeSptr(iRemoteObjectMocker); - ASSERT_NE(sProxy, nullptr); - - MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); - auto res = sProxy->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_ERROR_IPC_FAILED); - MockMessageParcel::ClearAllErrorFlag(); -} - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated02 - * @tc.desc: NotifyAppForceLandscapeConfigEnableUpdated_ShouldReturnIpcFailed_WhenRemoteIsNull - * @tc.type: FUNC - */ -HWTEST_F(SessionProxyTest, NotifyAppForceLandscapeConfigEnableUpdated02, TestSize.Level1) -{ - auto sProxy = sptr::MakeSptr(nullptr); - ASSERT_NE(sProxy, nullptr); - auto res = sProxy->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_ERROR_IPC_FAILED); -} - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated03 - * @tc.desc: ShouldReturnIpcFailed_WhenSendRequestFails - * @tc.type: FUNC - */ -HWTEST_F(SessionProxyTest, NotifyAppForceLandscapeConfigEnableUpdated03, TestSize.Level1) -{ - auto mockRemote = sptr::MakeSptr(); - mockRemote->sendRequestResult_ = ERR_TRANSACTION_FAILED; - auto sProxy = sptr::MakeSptr(mockRemote); - ASSERT_NE(sProxy, nullptr); - auto res = sProxy->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_ERROR_IPC_FAILED); -} - /** * @tc.name: NotifyPageEnable * @tc.desc: normal function diff --git a/window_scene/test/unittest/session_stage_proxy_layout_test.cpp b/window_scene/test/unittest/session_stage_proxy_layout_test.cpp index eed5cd74f3..3a0430c8e1 100644 --- a/window_scene/test/unittest/session_stage_proxy_layout_test.cpp +++ b/window_scene/test/unittest/session_stage_proxy_layout_test.cpp @@ -284,41 +284,240 @@ HWTEST_F(SessionStageProxyLayoutTest, NotifyGlobalScaledRectChange, TestSize.Lev } /** - * @tc.name: NotifyAppHookWindowInfoUpdated - * @tc.desc: test function : NotifyAppHookWindowInfoUpdated + * @tc.name: UpdateAttachedWindowLimits01 + * @tc.desc: Test UpdateAttachedWindowLimits with valid limits * @tc.type: FUNC */ -HWTEST_F(SessionStageProxyLayoutTest, NotifyAppHookWindowInfoUpdated, TestSize.Level1) +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits01, TestSize.Level1) { - GTEST_LOG_(INFO) << "SessionStageProxyLayoutTest: NotifyAppHookWindowInfoUpdated start"; - MockMessageParcel::ClearAllErrorFlag(); - sptr remoteMocker = sptr::MakeSptr(); - sptr sessionStageProxy = sptr::MakeSptr(remoteMocker); + ASSERT_TRUE((sessionStage_ != nullptr)); + int32_t sourcePersistentId = 1001; + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + bool isIntersectedHeightLimit = true; + bool isIntersectedWidthLimit = true; - // Case 1: Failed to write interface token + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, + isIntersectedHeightLimit, isIntersectedWidthLimit); + EXPECT_EQ(WSError::WS_OK, res); +} + +/** + * @tc.name: UpdateAttachedWindowLimits02 + * @tc.desc: Test UpdateAttachedWindowLimits with WriteInterfaceToken error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits02, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); - WSError errCode = sessionStageProxy->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(errCode, WSError::WS_ERROR_IPC_FAILED); + + int32_t sourcePersistentId = 1002; + WindowLimits attachedLimits = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, false, true); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(false); +} - // Case 2: remote is nullptr - sptr nullProxy = sptr::MakeSptr(nullptr); - errCode = nullProxy->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(errCode, WSError::WS_ERROR_IPC_FAILED); +/** + * @tc.name: UpdateAttachedWindowLimits03 + * @tc.desc: Test UpdateAttachedWindowLimits with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits03, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + int32_t sourcePersistentId = 1003; + WindowLimits attachedLimits = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; - // Case 3: Failed to send request - remoteMocker->SetRequestResult(ERR_TRANSACTION_FAILED); - errCode = sessionStageProxy->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(errCode, WSError::WS_ERROR_IPC_FAILED); - remoteMocker->SetRequestResult(ERR_NONE); + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, true, false); + EXPECT_EQ(WSError::WS_OK, res); +} - // Case 4: Success - errCode = sessionStageProxy->NotifyAppHookWindowInfoUpdated(); - MockMessageParcel::SetReadInt32ErrorFlag(false); - EXPECT_EQ(errCode, WSError::WS_OK); +/** + * @tc.name: UpdateAttachedWindowLimits04 + * @tc.desc: Test UpdateAttachedWindowLimits with null remote object + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits04, TestSize.Level1) +{ + // Create proxy with null remote object + auto sessionStage = sptr::MakeSptr(nullptr); + + int32_t sourcePersistentId = 1004; + WindowLimits attachedLimits = { 1600, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionStage->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, true, true); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} + +/** + * @tc.name: UpdateAttachedWindowLimits05 + * @tc.desc: Test UpdateAttachedWindowLimits with WriteInt32 error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits05, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteInt32ErrorFlag(true); + + int32_t sourcePersistentId = 1005; + WindowLimits attachedLimits = { 1700, 900, 120, 220, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, true, false); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteInt32ErrorFlag(false); +} + +/** + * @tc.name: UpdateAttachedWindowLimits06 + * @tc.desc: Test UpdateAttachedWindowLimits with Marshalling error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits06, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteUint32ErrorFlag(true); + + int32_t sourcePersistentId = 1006; + WindowLimits attachedLimits = { 1750, 950, 130, 230, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, false, true); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteUint32ErrorFlag(false); +} + +/** + * @tc.name: UpdateAttachedWindowLimits07 + * @tc.desc: Test UpdateAttachedWindowLimits with WriteBool(isIntersectedHeightLimit) error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits07, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteBoolErrorFlag(true); + MockMessageParcel::SetWriteBoolErrorCount(0); + + int32_t sourcePersistentId = 1007; + WindowLimits attachedLimits = { 1800, 980, 140, 240, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, true, true); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); MockMessageParcel::ClearAllErrorFlag(); - GTEST_LOG_(INFO) << "SessionStageProxyLayoutTest: NotifyAppHookWindowInfoUpdated end"; +} + +/** + * @tc.name: UpdateAttachedWindowLimits08 + * @tc.desc: Test UpdateAttachedWindowLimits with WriteBool(isIntersectedWidthLimit) error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits08, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteBoolErrorFlag(true); + MockMessageParcel::SetWriteBoolErrorCount(1); + + int32_t sourcePersistentId = 1008; + WindowLimits attachedLimits = { 1850, 970, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = sessionStage_->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, true, true); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::ClearAllErrorFlag(); +} + +/** + * @tc.name: UpdateAttachedWindowLimits09 + * @tc.desc: Test UpdateAttachedWindowLimits with SendRequest error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, UpdateAttachedWindowLimits09, TestSize.Level1) +{ + auto remoteMock = sptr::MakeSptr(); + remoteMock->sendRequestResult_ = ERR_TRANSACTION_FAILED; + sptr failSendProxy = sptr::MakeSptr(remoteMock); + + int32_t sourcePersistentId = 1009; + WindowLimits attachedLimits = { 1900, 990, 160, 260, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = failSendProxy->UpdateAttachedWindowLimits(sourcePersistentId, attachedLimits, true, true); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} + +/** + * @tc.name: RemoveAttachedWindowLimits01 + * @tc.desc: Test RemoveAttachedWindowLimits with valid sourceId + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, RemoveAttachedWindowLimits01, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + int32_t sourcePersistentId = 2001; + + WSError res = sessionStage_->RemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_OK, res); +} + +/** + * @tc.name: RemoveAttachedWindowLimits02 + * @tc.desc: Test RemoveAttachedWindowLimits with WriteInterfaceToken error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, RemoveAttachedWindowLimits02, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); + + int32_t sourcePersistentId = 2002; + WSError res = sessionStage_->RemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(false); +} + +/** + * @tc.name: RemoveAttachedWindowLimits03 + * @tc.desc: Test RemoveAttachedWindowLimits with null remote object + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, RemoveAttachedWindowLimits03, TestSize.Level1) +{ + // Create proxy with null remote object + auto sessionStage = sptr::MakeSptr(nullptr); + + int32_t sourcePersistentId = 2003; + WSError res = sessionStage->RemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} + +/** + * @tc.name: RemoveAttachedWindowLimits04 + * @tc.desc: Test RemoveAttachedWindowLimits with WriteInt32 error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, RemoveAttachedWindowLimits04, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteInt32ErrorFlag(true); + + int32_t sourcePersistentId = 2004; + WSError res = sessionStage_->RemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteInt32ErrorFlag(false); +} + +/** + * @tc.name: RemoveAttachedWindowLimits05 + * @tc.desc: Test RemoveAttachedWindowLimits with SendRequest error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, RemoveAttachedWindowLimits05, TestSize.Level1) +{ + auto remoteMock = sptr::MakeSptr(); + remoteMock->sendRequestResult_ = ERR_TRANSACTION_FAILED; + sptr failSendProxy = sptr::MakeSptr(remoteMock); + + int32_t sourcePersistentId = 2005; + WSError res = failSendProxy->RemoveAttachedWindowLimits(sourcePersistentId); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); } /** @@ -365,6 +564,165 @@ HWTEST_F(SessionStageProxyLayoutTest, UpdateAppHookWindowInfo, TestSize.Level1) MockMessageParcel::ClearAllErrorFlag(); GTEST_LOG_(INFO) << "SessionStageProxyLayoutTest: UpdateAppHookWindowInfo end"; } +/** + * @tc.name: SyncAllAttachedLimitsToChild01 + * @tc.desc: Test SyncAllAttachedLimitsToChild with valid lists + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild01, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + std::vector> limitsList; + std::vector> optionsList; + + limitsList.emplace_back(1001, WindowLimits{ 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1001, AttachLimitOptions{ true, true }); + + limitsList.emplace_back(1002, WindowLimits{ 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1002, AttachLimitOptions{ true, false }); + + WSError res = sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_OK, res); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild02 + * @tc.desc: Test SyncAllAttachedLimitsToChild with empty lists + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild02, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + std::vector> limitsList; + std::vector> optionsList; + + WSError res = sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_OK, res); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild03 + * @tc.desc: Test SyncAllAttachedLimitsToChild with WriteInterfaceToken error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild03, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); + + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(1003, WindowLimits{ 1600, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1003, AttachLimitOptions{ false, true }); + + WSError res = sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(false); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild04 + * @tc.desc: Test SyncAllAttachedLimitsToChild with null remote object + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild04, TestSize.Level1) +{ + auto sessionStage = sptr::MakeSptr(nullptr); + + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(1004, WindowLimits{ 1700, 850, 120, 220, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1004, AttachLimitOptions{ true, true }); + + WSError res = sessionStage->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild05 + * @tc.desc: Test SyncAllAttachedLimitsToChild with WriteUint32 error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild05, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteUint32ErrorFlag(true); + + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(1005, WindowLimits{ 1750, 950, 130, 230, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1005, AttachLimitOptions{ true, false }); + + WSError res = sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteUint32ErrorFlag(false); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild06 + * @tc.desc: Test SyncAllAttachedLimitsToChild with WriteInt32 error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild06, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteInt32ErrorFlag(true); + + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(1006, WindowLimits{ 1800, 980, 140, 240, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1006, AttachLimitOptions{ false, true }); + + WSError res = sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::SetWriteInt32ErrorFlag(false); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild07 + * @tc.desc: Test SyncAllAttachedLimitsToChild with WriteBool error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild07, TestSize.Level1) +{ + ASSERT_TRUE((sessionStage_ != nullptr)); + MockMessageParcel::SetWriteBoolErrorFlag(true); + MockMessageParcel::SetWriteBoolErrorCount(0); + + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(1007, WindowLimits{ 1900, 990, 160, 260, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1007, AttachLimitOptions{ true, true }); + + WSError res = sessionStage_->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); + + MockMessageParcel::ClearAllErrorFlag(); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild08 + * @tc.desc: Test SyncAllAttachedLimitsToChild with SendRequest error + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyLayoutTest, SyncAllAttachedLimitsToChild08, TestSize.Level1) +{ + auto remoteMock = sptr::MakeSptr(); + remoteMock->sendRequestResult_ = ERR_TRANSACTION_FAILED; + sptr failSendProxy = sptr::MakeSptr(remoteMock); + + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(1008, WindowLimits{ 1950, 995, 170, 270, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(1008, AttachLimitOptions{ true, true }); + + WSError res = failSendProxy->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(WSError::WS_ERROR_IPC_FAILED, res); +} + } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/session_stage_proxy_test.cpp b/window_scene/test/unittest/session_stage_proxy_test.cpp index 1717bdb15e..1740323a4e 100755 --- a/window_scene/test/unittest/session_stage_proxy_test.cpp +++ b/window_scene/test/unittest/session_stage_proxy_test.cpp @@ -1640,6 +1640,42 @@ HWTEST_F(SessionStageProxyTest, SyncFvLimits, TestSize.Level1) MockMessageParcel::ClearAllErrorFlag(); } + +/** + * @tc.name: SetForceSplitEnable01 + * @tc.desc: test function : SetForceSplitEnable + * @tc.type: FUNC + */ +HWTEST_F(SessionStageProxyTest, SetForceSplitEnable01, TestSize.Level1) +{ + ASSERT_TRUE(sessionStage_ != nullptr); + + // Case 1: Success + MockMessageParcel::ClearAllErrorFlag(); + ASSERT_EQ(WSError::WS_OK, sessionStage_->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE)); + + // Case 2: Failed to write interface token + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(true); + ASSERT_EQ(WSError::WS_ERROR_IPC_FAILED, sessionStage_->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE)); + MockMessageParcel::SetWriteInterfaceTokenErrorFlag(false); + + // Case 3: Failed to write isForceSplitEnabled + MockMessageParcel::SetWriteBoolErrorFlag(true); + ASSERT_EQ(WSError::WS_ERROR_IPC_FAILED, sessionStage_->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE)); + MockMessageParcel::SetWriteBoolErrorFlag(false); + + // Case 4: remote is nullptr + sptr nullProxy = sptr::MakeSptr(nullptr); + ASSERT_EQ(WSError::WS_ERROR_IPC_FAILED, nullProxy->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE)); + + // Case 5: Failed to send request + auto remoteMock = sptr::MakeSptr(); + remoteMock->sendRequestResult_ = ERR_TRANSACTION_FAILED; + sptr failSendProxy = sptr::MakeSptr(remoteMock); + ASSERT_EQ(WSError::WS_ERROR_IPC_FAILED, failSendProxy->SetForceSplitEnable(true, false, SelectMode::WIDE_MODE)); + + MockMessageParcel::ClearAllErrorFlag(); +} } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/session_stage_stub_layout_test.cpp b/window_scene/test/unittest/session_stage_stub_layout_test.cpp new file mode 100644 index 0000000000..04dc666a91 --- /dev/null +++ b/window_scene/test/unittest/session_stage_stub_layout_test.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "session/container/include/zidl/session_stage_stub.h" +#include "session/container/include/zidl/session_stage_ipc_interface_code.h" +#include +#include +#include +#include +#include +#include + +#include "iremote_object_mocker.h" +#include "mock/mock_session_stage.h" +#include "session_manager/include/scene_session_manager.h" +#include "session_manager/include/zidl/scene_session_manager_interface.h" +#include "window_manager.h" +#include "window_manager_agent.h" +#include "window_manager_hilog.h" +#include "ws_common.h" +#include "zidl/window_manager_agent_interface.h" + +using namespace testing; +using namespace testing::ext; + +namespace OHOS { +namespace Rosen { +class SessionStageStubLayoutTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; + sptr sessionStageStub_ = sptr::MakeSptr(); +}; + +void SessionStageStubLayoutTest::SetUpTestCase() {} + +void SessionStageStubLayoutTest::TearDownTestCase() {} + +void SessionStageStubLayoutTest::SetUp() {} + +void SessionStageStubLayoutTest::TearDown() {} + +namespace { + +/** + * @tc.name: HandleUpdateAttachedWindowLimits01 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with valid data + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits01, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 3001; + data.WriteInt32(sourcePersistentId); + + WindowLimits attachedLimits = { 200, 1000, 300, 2000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + attachedLimits.Marshalling(data); + + data.WriteBool(true); // isIntersectedHeightLimit + data.WriteBool(true); // isIntersectedWidthLimit + + EXPECT_EQ(ERR_NONE, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits02 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with read sourceId failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits02, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + // Don't write sourcePersistentId - will fail to read + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits03 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits03, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 3003; + data.WriteInt32(sourcePersistentId); + + WindowLimits attachedLimits = { 50, 500, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + attachedLimits.Marshalling(data); + + data.WriteBool(false); // isIntersectedHeightLimit + data.WriteBool(true); // isIntersectedWidthLimit + + EXPECT_EQ(ERR_NONE, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits04 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with height only + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits04, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 3004; + data.WriteInt32(sourcePersistentId); + + WindowLimits attachedLimits = { 150, 900, 250, 1800, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + attachedLimits.Marshalling(data); + + data.WriteBool(true); // isIntersectedHeightLimit + data.WriteBool(false); // isIntersectedWidthLimit + + EXPECT_EQ(ERR_NONE, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleRemoveAttachedWindowLimits01 + * @tc.desc: Test HandleRemoveAttachedWindowLimits with valid sourceId + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleRemoveAttachedWindowLimits01, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 4001; + data.WriteInt32(sourcePersistentId); + + EXPECT_EQ(ERR_NONE, sessionStageStub_->HandleRemoveAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleRemoveAttachedWindowLimits02 + * @tc.desc: Test HandleRemoveAttachedWindowLimits with read sourceId failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleRemoveAttachedWindowLimits02, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + // Don't write sourcePersistentId - will fail to read + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleRemoveAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits05 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with Unmarshalling failed (null WindowLimits) + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits05, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + // Write sourcePersistentId but don't write WindowLimits + int32_t sourcePersistentId = 3005; + data.WriteInt32(sourcePersistentId); + // WindowLimits::Unmarshalling will fail and return nullptr + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits06 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with read isIntersectedHeightLimit failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits06, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 3006; + data.WriteInt32(sourcePersistentId); + + WindowLimits attachedLimits = { 200, 1000, 300, 2000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + attachedLimits.Marshalling(data); + + // Don't write isIntersectedHeightLimit - will fail to read + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits07 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with read isIntersectedWidthLimit failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits07, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 3007; + data.WriteInt32(sourcePersistentId); + + WindowLimits attachedLimits = { 200, 1000, 300, 2000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + attachedLimits.Marshalling(data); + + data.WriteBool(true); // isIntersectedHeightLimit + // Don't write isIntersectedWidthLimit - will fail to read + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleUpdateAttachedWindowLimits08 + * @tc.desc: Test HandleUpdateAttachedWindowLimits with invalid pixelUnit value + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleUpdateAttachedWindowLimits08, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + int32_t sourcePersistentId = 3008; + data.WriteInt32(sourcePersistentId); + + // Write WindowLimits data manually with invalid pixelUnit value + data.WriteUint32(200); // maxWidth_ + data.WriteUint32(1000); // maxHeight_ + data.WriteUint32(300); // minWidth_ + data.WriteUint32(2000); // minHeight_ + data.WriteFloat(0.0f); // maxRatio_ + data.WriteFloat(0.0f); // minRatio_ + data.WriteFloat(0.0f); // vpRatio_ + data.WriteUint32(999); // Invalid pixelUnit (valid values are 0=PX, 1=VP) + + // WindowLimits::Unmarshalling will fail due to invalid pixelUnit + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleUpdateAttachedWindowLimits(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild01 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with valid data + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild01, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + // Write limits list: 2 entries + data.WriteUint32(2); + // Entry 1: parent limits + data.WriteInt32(100); // sourceId + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + limits1.Marshalling(data); + // Entry 2: sub window limits + data.WriteInt32(200); // sourceId + WindowLimits limits2 = { 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + limits2.Marshalling(data); + + // Write options list: 2 entries + data.WriteUint32(2); + data.WriteInt32(100); // sourceId + data.WriteBool(true); // isIntersectedHeightLimit + data.WriteBool(true); // isIntersectedWidthLimit + data.WriteInt32(200); // sourceId + data.WriteBool(true); // isIntersectedHeightLimit + data.WriteBool(false); // isIntersectedWidthLimit + + EXPECT_EQ(ERR_NONE, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild02 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with empty lists + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild02, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + data.WriteUint32(0); // empty limits list + data.WriteUint32(0); // empty options list + + EXPECT_EQ(ERR_NONE, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild03 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with read limitsCount failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild03, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + // Don't write anything - will fail to read limitsCount + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild04 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with read sourceId failed in limits + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild04, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + data.WriteUint32(1); // 1 entry in limits list + // Don't write sourceId - will fail + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild05 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with WindowLimits Unmarshalling failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild05, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + data.WriteUint32(1); // 1 entry in limits list + data.WriteInt32(300); // sourceId + // Don't write WindowLimits data - Unmarshalling will return nullptr + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild06 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with read optionsCount failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild06, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + // Write valid limits list + data.WriteUint32(1); + data.WriteInt32(400); + WindowLimits limits = { 1600, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + limits.Marshalling(data); + // Don't write optionsCount - will fail + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild07 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with read opt sourceId failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild07, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + // Write valid limits list + data.WriteUint32(1); + data.WriteInt32(500); + WindowLimits limits = { 1700, 900, 120, 220, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + limits.Marshalling(data); + + // Write options list with missing sourceId + data.WriteUint32(1); + // Don't write sourceId + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild08 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with read isIntersectedHeightLimit failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild08, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + // Write valid limits list + data.WriteUint32(1); + data.WriteInt32(600); + WindowLimits limits = { 1800, 950, 130, 230, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + limits.Marshalling(data); + + // Write options list with missing heightLimit + data.WriteUint32(1); + data.WriteInt32(600); + // Don't write isIntersectedHeightLimit + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +/** + * @tc.name: HandleSyncAllAttachedLimitsToChild09 + * @tc.desc: Test HandleSyncAllAttachedLimitsToChild with read isIntersectedWidthLimit failed + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubLayoutTest, HandleSyncAllAttachedLimitsToChild09, TestSize.Level1) +{ + ASSERT_TRUE((sessionStageStub_ != nullptr)); + MessageParcel data; + MessageParcel reply; + + // Write valid limits list + data.WriteUint32(1); + data.WriteInt32(700); + WindowLimits limits = { 1900, 980, 140, 240, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + limits.Marshalling(data); + + // Write options list with missing widthLimit + data.WriteUint32(1); + data.WriteInt32(700); + data.WriteBool(true); // isIntersectedHeightLimit + // Don't write isIntersectedWidthLimit + + EXPECT_EQ(ERR_INVALID_DATA, sessionStageStub_->HandleSyncAllAttachedLimitsToChild(data, reply)); +} + +} // namespace +} // namespace Rosen +} // namespace OHOS diff --git a/window_scene/test/unittest/session_stage_stub_test.cpp b/window_scene/test/unittest/session_stage_stub_test.cpp index cdfccf01cf..b90309d8fd 100644 --- a/window_scene/test/unittest/session_stage_stub_test.cpp +++ b/window_scene/test/unittest/session_stage_stub_test.cpp @@ -1641,22 +1641,6 @@ HWTEST_F(SessionStageStubTest, HandleUpdateGlobalDisplayRectFromServerSuccess, T EXPECT_EQ(result, ERR_NONE); } -/** - * @tc.name: HandleNotifyAppHookWindowInfoUpdated - * @tc.desc: test function : HandleNotifyAppHookWindowInfoUpdated - * @tc.type: FUNC - */ -HWTEST_F(SessionStageStubTest, HandleNotifyAppHookWindowInfoUpdated, TestSize.Level1) -{ - MessageParcel data; - MessageParcel reply; - MessageOption option; - data.WriteInterfaceToken(SessionStageStub::GetDescriptor()); - uint32_t code = static_cast(SessionStageInterfaceCode::TRANS_ID_NOTIFY_APP_HOOK_WINDOW_INFO_UPDATED); - ASSERT_TRUE(sessionStageStub_ != nullptr); - EXPECT_EQ(ERR_NONE, sessionStageStub_->OnRemoteRequest(code, data, reply, option)); -} - /** * @tc.name: HandleUpdateAppHookWindowInfo * @tc.desc: test function : HandleUpdateAppHookWindowInfo @@ -1968,6 +1952,63 @@ HWTEST_F(SessionStageStubTest, HandleSyncFvLimits, TestSize.Level1) data2.WriteInterfaceToken(SessionStageStub::GetDescriptor()); ASSERT_EQ(ERR_INVALID_VALUE, sessionStageStub_->OnRemoteRequest(code, data2, reply2, option)); } + +/** + * @tc.name: HandleSetForceSplitEnable01 + * @tc.desc: test function : HandleSetForceSplitEnable + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubTest, HandleSetForceSplitEnable01, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + // Case 1: Success + data.WriteInterfaceToken(SessionStageStub::GetDescriptor()); + bool isForceSplitEnabled = true; + bool needUpdateViewport = false; + uint32_t selectModeValue = static_cast(SelectMode::WIDE_MODE); + data.WriteBool(isForceSplitEnabled); + data.WriteBool(needUpdateViewport); + data.WriteUint32(selectModeValue); + uint32_t code = static_cast(SessionStageInterfaceCode::TRANS_ID_SET_FORCE_SPLIT_ENABLE); + ASSERT_TRUE(sessionStageStub_ != nullptr); + ASSERT_EQ(ERR_NONE, sessionStageStub_->OnRemoteRequest(code, data, reply, option)); +} + +/** + * @tc.name: HandleSetForceSplitEnable02 + * @tc.desc: test function : HandleSetForceSplitEnable with read failure + * @tc.type: FUNC + */ +HWTEST_F(SessionStageStubTest, HandleSetForceSplitEnable02, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + // Case 1: Failed to read isForceSplitEnabled + data.WriteInterfaceToken(SessionStageStub::GetDescriptor()); + uint32_t code = static_cast(SessionStageInterfaceCode::TRANS_ID_SET_FORCE_SPLIT_ENABLE); + ASSERT_TRUE(sessionStageStub_ != nullptr); + ASSERT_EQ(ERR_INVALID_DATA, sessionStageStub_->OnRemoteRequest(code, data, reply, option)); + + // Case 2: Failed to read needUpdateViewport + MessageParcel data2; + MessageParcel reply2; + data2.WriteInterfaceToken(SessionStageStub::GetDescriptor()); + data2.WriteBool(true); + ASSERT_EQ(ERR_INVALID_DATA, sessionStageStub_->OnRemoteRequest(code, data2, reply2, option)); + + // Case 3: Failed to read selectMode + MessageParcel data3; + MessageParcel reply3; + data3.WriteInterfaceToken(SessionStageStub::GetDescriptor()); + data3.WriteBool(true); + data3.WriteBool(false); + ASSERT_EQ(ERR_INVALID_DATA, sessionStageStub_->OnRemoteRequest(code, data3, reply3, option)); +} } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/session_test2.cpp b/window_scene/test/unittest/session_test2.cpp index 6f2177150f..10fefd98b0 100644 --- a/window_scene/test/unittest/session_test2.cpp +++ b/window_scene/test/unittest/session_test2.cpp @@ -85,6 +85,18 @@ private: sptr mockEventChannel_ = nullptr; }; +class SurfaceNodeChangedSession : public Session { +public: + explicit SurfaceNodeChangedSession(const SessionInfo& info) : Session(info) {} + bool isOnSurfaceNodeChangedCalled_ = false; + +protected: + void OnSurfaceNodeChanged() override + { + isOnSurfaceNodeChangedCalled_ = true; + } +}; + void WindowSessionTest2::SetUpTestCase() {} void WindowSessionTest2::TearDownTestCase() {} @@ -803,6 +815,25 @@ HWTEST_F(WindowSessionTest2, SetAndGetShadowSurfaceNode, TestSize.Level1) EXPECT_NE(session_->GetShadowSurfaceNode(), nullptr); } +/** + * @tc.name: SetSurfaceNodeTriggerOnSurfaceNodeChanged + * @tc.desc: SetSurfaceNode should trigger OnSurfaceNodeChanged callback + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionTest2, SetSurfaceNodeTriggerOnSurfaceNodeChanged, TestSize.Level1) +{ + SessionInfo info; + info.abilityName_ = "SetSurfaceNodeTriggerOnSurfaceNodeChanged"; + info.bundleName_ = "SetSurfaceNodeTriggerOnSurfaceNodeChanged"; + sptr session = sptr::MakeSptr(info); + ASSERT_NE(session, nullptr); + + std::shared_ptr surfaceNode = WindowSessionTest2::CreateRSSurfaceNode(); + ASSERT_NE(surfaceNode, nullptr); + session->SetSurfaceNode(surfaceNode); + EXPECT_TRUE(session->isOnSurfaceNodeChangedCalled_); +} + /** * @tc.name: SetAndGetLeashWinShadowSurfaceNode * @tc.desc: SetAndGetLeashWinShadowSurfaceNode @@ -1765,4 +1796,4 @@ HWTEST_F(WindowSessionTest2, TestGetMoveDragTargetShadowSurfaceNode, TestSize.Le } } // namespace } // namespace Rosen -} // namespace OHOS \ No newline at end of file +} // namespace OHOS diff --git a/window_scene/test/unittest/sub_session_test.cpp b/window_scene/test/unittest/sub_session_test.cpp index 3b624498fb..6096dc4c8a 100644 --- a/window_scene/test/unittest/sub_session_test.cpp +++ b/window_scene/test/unittest/sub_session_test.cpp @@ -835,139 +835,6 @@ HWTEST_F(SubSessionTest, ProcessPointDownSession02, TestSize.Level1) ret = subSession->ProcessPointDownSession(100, 200); EXPECT_EQ(WSError::WS_OK, ret); } - -/** - * @tc.name: IsVisibleForeground_LoosenedWithFreeMultiMode - * @tc.desc: test IsVisibleForeground when IsLoosenedWithFreeMultiMode is enabled - * @tc.type: FUNC - */ -HWTEST_F(SubSessionTest, IsVisibleForeground_LoosenedWithFreeMultiMode, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "TestSubSession"; - info.bundleName_ = "TestBundle"; - sptr subSession = sptr::MakeSptr(info, nullptr); - ASSERT_NE(subSession, nullptr); - - // Enable ZLevelAboveParentLoosened and PC mode - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(true); - subSession->systemConfig_.windowUIType_ = WindowUIType::PC_WINDOW; - - // Set session to foreground state - subSession->SetSessionState(SessionState::STATE_FOREGROUND); - - // Should return Session::IsVisibleForeground() when loosened - bool result = subSession->IsVisibleForeground(); - EXPECT_EQ(true, result); -} - -/** - * @tc.name: IsVisibleForeground_NotLoosened - * @tc.desc: test IsVisibleForeground when not loosened - * @tc.type: FUNC - */ -HWTEST_F(SubSessionTest, IsVisibleForeground_NotLoosened, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "TestSubSession"; - info.bundleName_ = "TestBundle"; - sptr subSession = sptr::MakeSptr(info, nullptr); - ASSERT_NE(subSession, nullptr); - - // Don't enable ZLevelAboveParentLoosened - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(false); - - // Set session to foreground state - subSession->SetSessionState(SessionState::STATE_FOREGROUND); - - // Should check parent session when not loosened - // Without parent session, this should return false or default behavior - bool result = subSession->IsVisibleForeground(); - // Result depends on implementation, test verifies no crash -} - -/** - * @tc.name: IsVisibleForeground_LoosenedWithFreeMultiMode_False - * @tc.desc: test IsVisibleForeground when loosened but session not foreground - * @tc.type: FUNC - */ -HWTEST_F(SubSessionTest, IsVisibleForeground_LoosenedWithFreeMultiMode_False, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "TestSubSession"; - info.bundleName_ = "TestBundle"; - sptr subSession = sptr::MakeSptr(info, nullptr); - ASSERT_NE(subSession, nullptr); - - // Enable ZLevelAboveParentLoosened and FreeMulti mode - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(true); - subSession->systemConfig_.freeMultiWindowEnable_ = true; - subSession->systemConfig_.freeMultiWindowSupport_ = true; - - // Set session to background state - subSession->SetSessionState(SessionState::STATE_BACKGROUND); - - // Should return Session::IsVisibleForeground() which should be false - bool result = subSession->IsVisibleForeground(); - EXPECT_EQ(false, result); -} - -/** - * @tc.name: IsSubWindowZLevelAboveParentLoosened_SubSession - * @tc.desc: test IsSubWindowZLevelAboveParentLoosened for SubSession - * @tc.type: FUNC - */ -HWTEST_F(SubSessionTest, IsSubWindowZLevelAboveParentLoosened_SubSession, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "TestSubSession"; - info.bundleName_ = "TestBundle"; - sptr subSession = sptr::MakeSptr(info, nullptr); - ASSERT_NE(subSession, nullptr); - - // Test default value - ASSERT_EQ(false, subSession->IsSubWindowZLevelAboveParentLoosened()); - - // Test enabled value - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(true); - ASSERT_EQ(true, subSession->IsSubWindowZLevelAboveParentLoosened()); - - // Test disabled value - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(false); - ASSERT_EQ(false, subSession->IsSubWindowZLevelAboveParentLoosened()); -} - -/** - * @tc.name: IsLoosenedWithFreeMultiMode_SubSession - * @tc.desc: test IsLoosenedWithFreeMultiMode for SubSession - * @tc.type: FUNC - */ -HWTEST_F(SubSessionTest, IsLoosenedWithFreeMultiMode_SubSession, TestSize.Level1) -{ - SessionInfo info; - info.abilityName_ = "TestSubSession"; - info.bundleName_ = "TestBundle"; - sptr subSession = sptr::MakeSptr(info, nullptr); - ASSERT_NE(subSession, nullptr); - - // Test default value - ASSERT_EQ(false, subSession->IsLoosenedWithFreeMultiMode()); - - // Test with PC mode enabled - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(true); - subSession->systemConfig_.windowUIType_ = WindowUIType::PC_WINDOW; - ASSERT_EQ(true, subSession->IsLoosenedWithFreeMultiMode()); - - // Test with FreeMulti mode enabled - subSession->systemConfig_.windowUIType_ = WindowUIType::PHONE_WINDOW; - subSession->systemConfig_.freeMultiWindowEnable_ = true; - subSession->systemConfig_.freeMultiWindowSupport_ = true; - ASSERT_EQ(true, subSession->IsLoosenedWithFreeMultiMode()); - - // Test with zLevel not loosened - subSession->GetSessionProperty()->SetZLevelAboveParentLoosened(false); - ASSERT_EQ(false, subSession->IsLoosenedWithFreeMultiMode()); -} } // namespace } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/window_scene/test/unittest/window_session_property_test.cpp b/window_scene/test/unittest/window_session_property_test.cpp index 352c0249b8..51c2a832a7 100755 --- a/window_scene/test/unittest/window_session_property_test.cpp +++ b/window_scene/test/unittest/window_session_property_test.cpp @@ -412,6 +412,445 @@ HWTEST_F(WindowSessionPropertyTest, IsDecorEnable, TestSize.Level1) ASSERT_EQ(false, result); } +/** + * @tc.name: SetAttachedWindowLimits01 + * @tc.desc: Test SetAttachedWindowLimits with new sourceId + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedWindowLimits01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + int32_t sourcePersistentId = 1001; + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(sourcePersistentId, attachedLimits); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 1u); + EXPECT_EQ(attachedList[0].first, sourcePersistentId); + EXPECT_EQ(attachedList[0].second.minWidth_, 200); +} + +/** + * @tc.name: SetAttachedWindowLimits02 + * @tc.desc: Test SetAttachedWindowLimits with existing sourceId (update) + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedWindowLimits02, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + int32_t sourcePersistentId = 1002; + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(sourcePersistentId, limits1); + + WindowLimits limits2 = { 2200, 1100, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(sourcePersistentId, limits2); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 1u); + EXPECT_EQ(attachedList[0].second.minWidth_, 250); +} + +/** + * @tc.name: SetAttachedWindowLimits03 + * @tc.desc: Test SetAttachedWindowLimits with multiple sourceIds (insertion order) + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedWindowLimits03, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 2200, 1100, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits3 = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + property->SetAttachedWindowLimits(1, limits1); + property->SetAttachedWindowLimits(2, limits2); + property->SetAttachedWindowLimits(3, limits3); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 3u); + EXPECT_EQ(attachedList[0].first, 1); + EXPECT_EQ(attachedList[1].first, 2); + EXPECT_EQ(attachedList[2].first, 3); +} + +/** + * @tc.name: SetAttachedWindowLimits04 + * @tc.desc: Test SetAttachedWindowLimits with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedWindowLimits04, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + int32_t sourcePersistentId = 1004; + WindowLimits attachedLimits = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + property->SetAttachedWindowLimits(sourcePersistentId, attachedLimits); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 1u); + EXPECT_EQ(attachedList[0].second.minWidth_, 50); +} + +/** + * @tc.name: RemoveAttachedWindowLimits01 + * @tc.desc: Test RemoveAttachedWindowLimits with existing sourceId + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, RemoveAttachedWindowLimits01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + int32_t sourcePersistentId = 2001; + WindowLimits attachedLimits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(sourcePersistentId, attachedLimits); + + property->RemoveAttachedWindowLimits(sourcePersistentId); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 0u); +} + +/** + * @tc.name: RemoveAttachedWindowLimits02 + * @tc.desc: Test RemoveAttachedWindowLimits with non-existent sourceId + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, RemoveAttachedWindowLimits02, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(1, limits); + property->SetAttachedWindowLimits(2, limits); + + property->RemoveAttachedWindowLimits(999); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 2u); +} + +/** + * @tc.name: RemoveAttachedWindowLimits03 + * @tc.desc: Test RemoveAttachedWindowLimits with multiple sourceIds + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, RemoveAttachedWindowLimits03, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 2200, 1100, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits3 = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + property->SetAttachedWindowLimits(1, limits1); + property->SetAttachedWindowLimits(2, limits2); + property->SetAttachedWindowLimits(3, limits3); + + property->RemoveAttachedWindowLimits(2); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 2u); + EXPECT_EQ(attachedList[0].first, 1); + EXPECT_EQ(attachedList[1].first, 3); +} + +/** + * @tc.name: GetAttachedWindowLimitsList01 + * @tc.desc: Test GetAttachedWindowLimitsList with empty list + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, GetAttachedWindowLimitsList01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 0u); +} + +/** + * @tc.name: GetAttachedWindowLimitsList02 + * @tc.desc: Test GetAttachedWindowLimitsList returns copy + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, GetAttachedWindowLimitsList02, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(1, limits); + + auto attachedList1 = property->GetAttachedWindowLimitsList(); + auto attachedList2 = property->GetAttachedWindowLimitsList(); + + EXPECT_EQ(attachedList1.size(), attachedList2.size()); +} + +/** + * @tc.name: ClearAttachedWindowLimitsList01 + * @tc.desc: Test ClearAttachedWindowLimitsList + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, ClearAttachedWindowLimitsList01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetAttachedWindowLimits(1, limits); + property->SetAttachedWindowLimits(2, limits); + property->SetAttachedWindowLimits(3, limits); + + property->ClearAttachedWindowLimitsList(); + + auto attachedList = property->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 0u); +} + +/** + * @tc.name: SetLimitsForAttachedWindows01 + * @tc.desc: Test SetLimitsForAttachedWindows and GetLimitsForAttachedWindows with PX unit + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetLimitsForAttachedWindows01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits = { 1000, 2000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetLimitsForAttachedWindows(limits); + + WindowLimits result = property->GetLimitsForAttachedWindows(); + EXPECT_EQ(result.minWidth_, 200); + EXPECT_EQ(result.maxWidth_, 1000); + EXPECT_EQ(result.minHeight_, 300); + EXPECT_EQ(result.maxHeight_, 2000); + EXPECT_EQ(result.pixelUnit_, PixelUnit::PX); +} + +/** + * @tc.name: SetLimitsForAttachedWindows02 + * @tc.desc: Test SetLimitsForAttachedWindows and GetLimitsForAttachedWindows with VP unit + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetLimitsForAttachedWindows02, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits = { 500, 1000, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + property->SetLimitsForAttachedWindows(limits); + + WindowLimits result = property->GetLimitsForAttachedWindows(); + EXPECT_EQ(result.minWidth_, 50); + EXPECT_EQ(result.maxWidth_, 500); + EXPECT_EQ(result.minHeight_, 100); + EXPECT_EQ(result.maxHeight_, 1000); + EXPECT_EQ(result.pixelUnit_, PixelUnit::VP); +} + +/** + * @tc.name: SetLimitsForAttachedWindows03 + * @tc.desc: Test SetLimitsForAttachedWindows with multiple updates + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetLimitsForAttachedWindows03, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + WindowLimits limits1 = { 1000, 2000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetLimitsForAttachedWindows(limits1); + + WindowLimits result1 = property->GetLimitsForAttachedWindows(); + EXPECT_EQ(result1.minWidth_, 200); + + WindowLimits limits2 = { 1100, 2200, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + property->SetLimitsForAttachedWindows(limits2); + + WindowLimits result2 = property->GetLimitsForAttachedWindows(); + EXPECT_EQ(result2.minWidth_, 250); + EXPECT_EQ(result2.maxWidth_, 1100); +} + +/** + * @tc.name: SetAttachedLimitOptions01 + * @tc.desc: Test SetAttachedLimitOptions and GetAttachedLimitOptions + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedLimitOptions01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + AttachLimitOptions options1{ true, false }; // height=true, width=false + property->SetAttachedLimitOptions(100, options1); + + AttachLimitOptions result1 = property->GetAttachedLimitOptions(100); + EXPECT_TRUE(result1.isIntersectedHeightLimit); + EXPECT_FALSE(result1.isIntersectedWidthLimit); + + AttachLimitOptions options2{ false, true }; // height=false, width=true + property->SetAttachedLimitOptions(200, options2); + + AttachLimitOptions result2 = property->GetAttachedLimitOptions(200); + EXPECT_FALSE(result2.isIntersectedHeightLimit); + EXPECT_TRUE(result2.isIntersectedWidthLimit); +} + +/** + * @tc.name: SetAttachedLimitOptions02 + * @tc.desc: Test GetAttachedLimitOptions with non-existent window ID + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedLimitOptions02, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + AttachLimitOptions result = property->GetAttachedLimitOptions(999); + // Should return default options (false, false) + EXPECT_FALSE(result.isIntersectedHeightLimit); + EXPECT_FALSE(result.isIntersectedWidthLimit); +} + +/** + * @tc.name: SetAttachedLimitOptions03 + * @tc.desc: Test SetAttachedLimitOptions with update + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedLimitOptions03, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + AttachLimitOptions options1{ true, true }; + property->SetAttachedLimitOptions(100, options1); + + AttachLimitOptions result1 = property->GetAttachedLimitOptions(100); + EXPECT_TRUE(result1.isIntersectedHeightLimit); + EXPECT_TRUE(result1.isIntersectedWidthLimit); + + // Update the same window ID + AttachLimitOptions options2{ false, false }; + property->SetAttachedLimitOptions(100, options2); + + AttachLimitOptions result2 = property->GetAttachedLimitOptions(100); + EXPECT_FALSE(result2.isIntersectedHeightLimit); + EXPECT_FALSE(result2.isIntersectedWidthLimit); +} + +/** + * @tc.name: RemoveAttachedLimitOptions01 + * @tc.desc: Test RemoveAttachedLimitOptions + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, RemoveAttachedLimitOptions01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + AttachLimitOptions options{ true, true }; + property->SetAttachedLimitOptions(100, options); + property->SetAttachedLimitOptions(200, options); + + EXPECT_TRUE(property->GetAttachedLimitOptions(100).isIntersectedHeightLimit); + EXPECT_TRUE(property->GetAttachedLimitOptions(200).isIntersectedHeightLimit); + + property->RemoveAttachedLimitOptions(100); + + AttachLimitOptions result = property->GetAttachedLimitOptions(100); + EXPECT_FALSE(result.isIntersectedHeightLimit); + EXPECT_FALSE(result.isIntersectedWidthLimit); + + // Window ID 200 should still exist + AttachLimitOptions result2 = property->GetAttachedLimitOptions(200); + EXPECT_TRUE(result2.isIntersectedHeightLimit); + EXPECT_TRUE(result2.isIntersectedWidthLimit); +} + +/** + * @tc.name: GetAttachedLimitOptionsList01 + * @tc.desc: Test GetAttachedLimitOptionsList with multiple windows + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, GetAttachedLimitOptionsList01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + property->SetAttachedLimitOptions(100, AttachLimitOptions{ true, false }); + property->SetAttachedLimitOptions(200, AttachLimitOptions{ false, true }); + property->SetAttachedLimitOptions(300, AttachLimitOptions{ true, true }); + + auto optionsList = property->GetAttachedLimitOptionsList(); + EXPECT_EQ(optionsList.size(), 3u); + + EXPECT_EQ(optionsList[0].first, 100); + EXPECT_TRUE(optionsList[0].second.isIntersectedHeightLimit); + EXPECT_FALSE(optionsList[0].second.isIntersectedWidthLimit); + + EXPECT_EQ(optionsList[1].first, 200); + EXPECT_FALSE(optionsList[1].second.isIntersectedHeightLimit); + EXPECT_TRUE(optionsList[1].second.isIntersectedWidthLimit); + + EXPECT_EQ(optionsList[2].first, 300); + EXPECT_TRUE(optionsList[2].second.isIntersectedHeightLimit); + EXPECT_TRUE(optionsList[2].second.isIntersectedWidthLimit); +} + +/** + * @tc.name: ClearAttachedLimitOptionsList01 + * @tc.desc: Test ClearAttachedLimitOptionsList + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, ClearAttachedLimitOptionsList01, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + property->SetAttachedLimitOptions(100, AttachLimitOptions{ true, false }); + property->SetAttachedLimitOptions(200, AttachLimitOptions{ false, true }); + + EXPECT_EQ(property->GetAttachedLimitOptionsList().size(), 2u); + + property->ClearAttachedLimitOptionsList(); + + EXPECT_EQ(property->GetAttachedLimitOptionsList().size(), 0u); +} + +/** + * @tc.name: SetAttachedLimitOptions04 + * @tc.desc: Test SetAttachedLimitOptions preserves order when updating existing entry + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetAttachedLimitOptions04, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + + // Add three entries in order: 100, 200, 300 + property->SetAttachedLimitOptions(100, AttachLimitOptions{ true, false }); + property->SetAttachedLimitOptions(200, AttachLimitOptions{ false, true }); + property->SetAttachedLimitOptions(300, AttachLimitOptions{ true, true }); + + auto optionsList1 = property->GetAttachedLimitOptionsList(); + EXPECT_EQ(optionsList1.size(), 3u); + EXPECT_EQ(optionsList1[0].first, 100); + EXPECT_EQ(optionsList1[1].first, 200); + EXPECT_EQ(optionsList1[2].first, 300); + + // Update entry 200 (middle element) + property->SetAttachedLimitOptions(200, AttachLimitOptions{ false, false }); + + auto optionsList2 = property->GetAttachedLimitOptionsList(); + EXPECT_EQ(optionsList2.size(), 3u); + + // Verify order is preserved: 100, 200, 300 + EXPECT_EQ(optionsList2[0].first, 100); + EXPECT_TRUE(optionsList2[0].second.isIntersectedHeightLimit); + EXPECT_FALSE(optionsList2[0].second.isIntersectedWidthLimit); + + EXPECT_EQ(optionsList2[1].first, 200); + EXPECT_FALSE(optionsList2[1].second.isIntersectedHeightLimit); // Updated to false + EXPECT_FALSE(optionsList2[1].second.isIntersectedWidthLimit); // Updated to false + + EXPECT_EQ(optionsList2[2].first, 300); + EXPECT_TRUE(optionsList2[2].second.isIntersectedHeightLimit); + EXPECT_TRUE(optionsList2[2].second.isIntersectedWidthLimit); +} + /** * @tc.name: SetWindowModeSupportType * @tc.desc: SetWindowModeSupportType test @@ -1898,16 +2337,10 @@ HWTEST_F(WindowSessionPropertyTest, GetKeyboardLayoutParamsByScreenId, TestSize. HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig01, TestSize.Level1) { AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; preconfig.containsSysConfig_ = false; preconfig.containsAppConfig_ = false; AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; config.containsSysConfig_ = false; config.containsAppConfig_ = false; @@ -1917,65 +2350,12 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig01, TestSize.Level1) /** * @tc.name: IsSameForceSplitConfig02 - * @tc.desc: Test IsSameForceSplitConfig when mode differs + * @tc.desc: Test IsSameForceSplitConfig when containsSysConfig is true and sys configs match * @tc.type: FUNC */ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig02, TestSize.Level1) { AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; - preconfig.containsSysConfig_ = false; - preconfig.containsAppConfig_ = false; - - AppForceLandscapeConfig config; - config.mode_ = 6; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; - config.containsSysConfig_ = false; - config.containsAppConfig_ = false; - - bool result = AppForceLandscapeConfig::IsSameForceSplitConfig(preconfig, config); - EXPECT_EQ(result, false); -} - -/** - * @tc.name: IsSameForceSplitConfig03 - * @tc.desc: Test IsSameForceSplitConfig when supportSplit differs - * @tc.type: FUNC - */ -HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig03, TestSize.Level1) -{ - AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; - preconfig.containsSysConfig_ = false; - preconfig.containsAppConfig_ = false; - - AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 2; - config.ignoreOrientation_ = false; - config.containsSysConfig_ = false; - config.containsAppConfig_ = false; - - bool result = AppForceLandscapeConfig::IsSameForceSplitConfig(preconfig, config); - EXPECT_EQ(result, false); -} - -/** - * @tc.name: IsSameForceSplitConfig04 - * @tc.desc: Test IsSameForceSplitConfig when containsSysConfig is true and sys configs match - * @tc.type: FUNC - */ -HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig04, TestSize.Level1) -{ - AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; preconfig.containsSysConfig_ = true; preconfig.isSysRouter_ = true; preconfig.sysHomePage_ = "home"; @@ -1983,9 +2363,6 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig04, TestSize.Level1) preconfig.containsAppConfig_ = false; AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; config.containsSysConfig_ = true; config.isSysRouter_ = true; config.sysHomePage_ = "home"; @@ -1997,16 +2374,13 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig04, TestSize.Level1) } /** - * @tc.name: IsSameForceSplitConfig05 + * @tc.name: IsSameForceSplitConfig03 * @tc.desc: Test IsSameForceSplitConfig when containsSysConfig is true and sysHomePage differs * @tc.type: FUNC */ -HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig05, TestSize.Level1) +HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig03, TestSize.Level1) { AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; preconfig.containsSysConfig_ = true; preconfig.isSysRouter_ = true; preconfig.sysHomePage_ = "home1"; @@ -2014,9 +2388,6 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig05, TestSize.Level1) preconfig.containsAppConfig_ = false; AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; config.containsSysConfig_ = true; config.isSysRouter_ = true; config.sysHomePage_ = "home2"; @@ -2028,25 +2399,19 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig05, TestSize.Level1) } /** - * @tc.name: IsSameForceSplitConfig06 + * @tc.name: IsSameForceSplitConfig04 * @tc.desc: Test IsSameForceSplitConfig when containsAppConfig is true and app configs match * @tc.type: FUNC */ -HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig06, TestSize.Level1) +HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig04, TestSize.Level1) { AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; preconfig.containsSysConfig_ = false; preconfig.containsAppConfig_ = true; preconfig.isAppRouter_ = true; preconfig.appConfigJsonStr_ = "appConfig"; AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; config.containsSysConfig_ = false; config.containsAppConfig_ = true; config.isAppRouter_ = true; @@ -2057,25 +2422,19 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig06, TestSize.Level1) } /** - * @tc.name: IsSameForceSplitConfig07 + * @tc.name: IsSameForceSplitConfig05 * @tc.desc: Test IsSameForceSplitConfig when containsAppConfig is true and appConfigJsonStr differs * @tc.type: FUNC */ -HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig07, TestSize.Level1) +HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig05, TestSize.Level1) { AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; preconfig.containsSysConfig_ = false; preconfig.containsAppConfig_ = true; preconfig.isAppRouter_ = true; preconfig.appConfigJsonStr_ = "appConfig1"; AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; config.containsSysConfig_ = false; config.containsAppConfig_ = true; config.isAppRouter_ = true; @@ -2086,16 +2445,13 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig07, TestSize.Level1) } /** - * @tc.name: IsSameForceSplitConfig08 + * @tc.name: IsSameForceSplitConfig06 * @tc.desc: Test IsSameForceSplitConfig when containsSysConfig and containsAppConfig are both true * @tc.type: FUNC */ -HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig08, TestSize.Level1) +HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig06, TestSize.Level1) { AppForceLandscapeConfig preconfig; - preconfig.mode_ = 5; - preconfig.supportSplit_ = 1; - preconfig.ignoreOrientation_ = false; preconfig.containsSysConfig_ = true; preconfig.isSysRouter_ = true; preconfig.sysHomePage_ = "home"; @@ -2105,9 +2461,6 @@ HWTEST_F(WindowSessionPropertyTest, IsSameForceSplitConfig08, TestSize.Level1) preconfig.appConfigJsonStr_ = "appConfig"; AppForceLandscapeConfig config; - config.mode_ = 5; - config.supportSplit_ = 1; - config.ignoreOrientation_ = false; config.containsSysConfig_ = true; config.isSysRouter_ = true; config.sysHomePage_ = "home"; @@ -2217,6 +2570,145 @@ HWTEST_F(WindowSessionPropertyTest, UnmarshallingFvTemplateInfo, TestSize.Level1 property->UnmarshallingFvTemplateInfo(parcel, property); EXPECT_EQ(property->GetFvTemplateInfo().bindWindowId_, fvTemplateInfo.bindWindowId_); } + +/** + * @tc.name: SetForceSplitEnable001 + * @tc.desc: SetForceSplitEnable and GetForceSplitEnable test + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetForceSplitEnable001, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + ASSERT_NE(nullptr, property); + + property->SetForceSplitEnable(true); + ASSERT_EQ(property->GetForceSplitEnable(), true); + + property->SetForceSplitEnable(false); + ASSERT_EQ(property->GetForceSplitEnable(), false); +} + +/** + * @tc.name: SetHookWindowInfo001 + * @tc.desc: SetHookWindowInfo and GetHookWindowInfo test + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, SetHookWindowInfo001, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + ASSERT_NE(nullptr, property); + + HookWindowInfo hookInfo; + hookInfo.enableHookWindow = true; + hookInfo.widthHookRatio = 0.5f; + hookInfo.drawableRectHook = true; + + property->SetHookWindowInfo(hookInfo); + auto retInfo = property->GetHookWindowInfo(); + ASSERT_EQ(retInfo.enableHookWindow, true); + ASSERT_EQ(retInfo.widthHookRatio, 0.5f); + ASSERT_EQ(retInfo.drawableRectHook, true); +} + +/** + * @tc.name: MarshallingHookWindowInfo001 + * @tc.desc: test MarshallingHookWindowInfo + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, MarshallingHookWindowInfo001, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + ASSERT_NE(nullptr, property); + + HookWindowInfo hookInfo; + hookInfo.enableHookWindow = true; + hookInfo.widthHookRatio = 0.6f; + property->SetHookWindowInfo(hookInfo); + + Parcel parcel; + bool ret = property->MarshallingHookWindowInfo(parcel); + ASSERT_EQ(true, ret); +} + +/** + * @tc.name: UnmarshallingHookWindowInfo001 + * @tc.desc: test UnmarshallingHookWindowInfo + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, UnmarshallingHookWindowInfo001, TestSize.Level1) +{ + Parcel parcel; + HookWindowInfo hookInfo; + hookInfo.enableHookWindow = true; + hookInfo.widthHookRatio = 0.7f; + parcel.WriteParcelable(&hookInfo); + + sptr property = sptr::MakeSptr(); + ASSERT_NE(nullptr, property); + + WindowSessionProperty::UnmarshallingHookWindowInfo(parcel, property); + auto retInfo = property->GetHookWindowInfo(); + ASSERT_EQ(retInfo.enableHookWindow, true); + ASSERT_EQ(retInfo.widthHookRatio, 0.7f); +} + +/** + * @tc.name: UnmarshallingHookWindowInfo002 + * @tc.desc: test UnmarshallingHookWindowInfo with nullptr hookWindowInfo + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, UnmarshallingHookWindowInfo002, TestSize.Level1) +{ + Parcel parcel; + // Do not write any HookWindowInfo, ReadParcelable will return nullptr + + sptr property = sptr::MakeSptr(); + ASSERT_NE(nullptr, property); + + // Set initial value + HookWindowInfo initialInfo; + initialInfo.enableHookWindow = true; + initialInfo.widthHookRatio = 0.5f; + property->SetHookWindowInfo(initialInfo); + + // Call UnmarshallingHookWindowInfo with empty parcel + WindowSessionProperty::UnmarshallingHookWindowInfo(parcel, property); + + // Property should remain unchanged when ReadParcelable returns nullptr + auto retInfo = property->GetHookWindowInfo(); + ASSERT_EQ(retInfo.enableHookWindow, true); + ASSERT_EQ(retInfo.widthHookRatio, 0.5f); +} + +/** + * @tc.name: MarshallingUnmarshallingWithHookWindowInfo + * @tc.desc: test Marshalling and Unmarshalling with HookWindowInfo + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionPropertyTest, MarshallingUnmarshallingWithHookWindowInfo, TestSize.Level1) +{ + sptr property = sptr::MakeSptr(); + ASSERT_NE(nullptr, property); + property->SetPersistentId(100); + + HookWindowInfo hookInfo; + hookInfo.enableHookWindow = true; + hookInfo.widthHookRatio = 0.8f; + hookInfo.drawableRectHook = true; + property->SetHookWindowInfo(hookInfo); + property->SetForceSplitEnable(true); + + Parcel parcel; + bool ret = property->Marshalling(parcel); + ASSERT_EQ(true, ret); + + sptr targetProperty = property->Unmarshalling(parcel); + ASSERT_NE(targetProperty, nullptr); + ASSERT_EQ(targetProperty->GetHookWindowInfo().enableHookWindow, true); + ASSERT_EQ(targetProperty->GetHookWindowInfo().widthHookRatio, 0.8f); + ASSERT_EQ(targetProperty->GetHookWindowInfo().drawableRectHook, true); + ASSERT_EQ(targetProperty->GetForceSplitEnable(), true); +} } // namespace } // namespace Rosen -} // namespace OHOS +} // namespace OHOS \ No newline at end of file diff --git a/wm/BUILD.gn b/wm/BUILD.gn index fd371f0154..890f1c0efd 100644 --- a/wm/BUILD.gn +++ b/wm/BUILD.gn @@ -328,6 +328,7 @@ ohos_shared_library("libwm") { ] defines = [] + ldflags = [ "-Wl,-Bsymbolic-functions" ] if (defined(global_parts_info) && defined(global_parts_info.barrierfree_accessibility)) { @@ -363,7 +364,7 @@ ohos_shared_library("libwm") { } if (is_ohos && is_clang && target_cpu == "arm64") { - ldflags = [ + ldflags += [ "-Wl,--emit-relocs", "-Wl,--no-relax", "-mno-fix-cortex-a53-843419" @@ -453,6 +454,7 @@ ohos_shared_library("libwm_lite") { if (build_variant == "user") { defines += [ "IS_RELEASE_VERSION" ] } + ldflags = [ "-Wl,-Bsymbolic-functions" ] } group("test") { @@ -476,7 +478,7 @@ ohos_shared_library("libwm_ndk") { include_dirs = [ "${window_base_path}/interfaces/kits/ndk/wm", - "${window_base_path}/interfaces/inner_kits/wm", + "${window_base_path}/interfaces/innerkits/wm", ] sources = [ @@ -596,6 +598,7 @@ ohos_shared_library("libpip_web") { "samgr:samgr_proxy", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] innerapi_tags = [ "platformsdk_indirect" ] part_name = "window_manager" subsystem_name = "window" @@ -630,7 +633,7 @@ ohos_shared_library("libpip_ndk") { include_dirs = [ "//foundation/window/window_manager", "//foundation/window/window_manager/interfaces/innerkits", - "//foundation/window/window_manager/interfaces/inner_kits/wm", + "//foundation/window/window_manager/interfaces/innerkits/wm", "//foundation/window/window_manager/interfaces/kits/ndk/wm", "//foundation/window/window_manager/wm/include", ] diff --git a/wm/include/window_scene_session_impl.h b/wm/include/window_scene_session_impl.h index c5f49fd005..9a6bdc1f7b 100644 --- a/wm/include/window_scene_session_impl.h +++ b/wm/include/window_scene_session_impl.h @@ -80,8 +80,15 @@ public: bool isLayoutFullScreen) override; WMError SetFrameRectForPartialZoomIn(const Rect& frameRect) override; WMError UpdateWindowModeForUITest(int32_t updateMode) override; - WSError NotifyAppHookWindowInfoUpdated() override; WSError UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) override; + WSError SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) override; + WSError UpdateAttachedWindowLimits(int32_t sourcePersistentId, + const WindowLimits& attachedWindowLimits, bool isIntersectedHeightLimit, + bool isIntersectedWidthLimit) override; + WSError RemoveAttachedWindowLimits(int32_t sourcePersistentId) override; + WSError SyncAllAttachedLimitsToChild( + const std::vector>& limitsList, + const std::vector>& optionsList) override; /* * Window Hierarchy @@ -185,10 +192,7 @@ public: WMError AdjustKeyboardLayout(const KeyboardLayoutParams params) override; WMError CheckAndModifyWindowRect(uint32_t& width, uint32_t& height) override; WMError GetAppForceLandscapeConfig(AppForceLandscapeConfig& config) override; - WMError GetAppForceLandscapeConfigEnable(bool& enableForceSplit) override; WSError NotifyAppForceLandscapeConfigUpdated() override; - WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) override; /* * Sub Window @@ -377,7 +381,7 @@ public: * Window LifeCycle */ void Resume(bool isGamePreLaunch = false) override; - void Pause() override; + void Pause(bool isGamePreLaunch = false) override; WSError CloseSpecificScene() override; WMError SetSubWindowSource(SubWindowSource source) override; @@ -431,7 +435,45 @@ protected: * may exceed system limits and is clamped to min(system limits, 40vp); * other cases remain constrained by system limits. */ - void UpdateWindowSizeLimits(); + void UpdateWindowSizeLimits(bool needNotifySession = false); + + /** + * @brief Calculate window limits intersection with attached windows. + * @param newLimits Reference to WindowLimits (PX unit) to be updated with intersected values. + * @param newLimitsVP Reference to WindowLimits (VP unit) to be updated with intersected values. + * @param virtualPixelRatio Virtual pixel ratio for unit conversion. + */ + void CalculateAttachedWindowLimitsIntersection(WindowLimits& newLimits, WindowLimits& newLimitsVP, + float virtualPixelRatio); + + /** + * @brief Result of calculating intersection with a single attached window. + */ + struct WinIntersectResult { + bool pxValid; // PX intersection is valid + bool vpValid; // VP intersection is valid + WindowLimits pxLimits; // PX intersection result + WindowLimits vpLimits; // VP intersection result + }; + + /** + * @brief Calculate intersection with a single attached window. + * @param currentLimits Current PX limits. + * @param currentLimitsVP Current VP limits. + * @param attachedLimits Attached window limits (may be PX or VP). + * @param limitOptions Options for which limits (height/width) to intersect. + * @param virtualPixelRatio Virtual pixel ratio for conversion. + * @return Intersection result containing validity and calculated limits. + */ + WinIntersectResult CalcSingleWinIntersect( + const WindowLimits& currentLimits, const WindowLimits& currentLimitsVP, const WindowLimits& attachedLimits, + const AttachLimitOptions& limitOptions, float virtualPixelRatio); + + /** + * @brief Notify session side about window limits change. + * @param limitsToNotify The window limits to notify (already selected based on pixelUnit). + */ + void NotifySessionSideLimitsChanged(const WindowLimits& limitsToNotify); // Checker to determine whether the caller has system permission. using SystemPermissionChecker = std::function; @@ -528,7 +570,6 @@ private: */ void CheckMoveConfiguration(MoveConfiguration& moveConfiguration); void UpdateEnableDragWhenSwitchMultiWindow(bool enable); - WMError GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) override; WMError GetSelectMode(SelectMode& selectMode) override; bool ShouldSkipSupportWindowModeCheck(uint32_t windowModeSupportType, WindowMode mode); uint32_t UpdateConfigVal(uint32_t minVal, uint32_t maxVal, uint32_t configVal, uint32_t defaultVal, float vpr); diff --git a/wm/include/window_session_impl.h b/wm/include/window_session_impl.h index 217fce6de0..36ee91cdb5 100644 --- a/wm/include/window_session_impl.h +++ b/wm/include/window_session_impl.h @@ -563,8 +563,9 @@ public: WSError NotifySubWindowAfterParentWindowStatusChange(WindowMode mode, MaximizeMode maximizeMode, bool isLayoutFullScreen) override { return WSError::WS_OK; } WMError UpdateWindowModeForUITest(int32_t updateMode) override { return WMError::WM_OK; } - WSError NotifyAppHookWindowInfoUpdated() override { return WSError::WS_DO_NOTHING; } WSError UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) override { return WSError::WS_DO_NOTHING; } + WSError SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, SelectMode selectMode) override + { return WSError::WS_DO_NOTHING; } void SetNotifySizeChangeFlag(bool flag); Rect GetGlobalScaledRectLocal() const; @@ -610,6 +611,8 @@ public: bool IsDeviceFeatureCapableFor(const std::string& feature) const override; bool IsDeviceFeatureCapableForFreeMultiWindow() const override; bool IsAnco() const override; + bool GetIsAtomicService() const override; + int32_t GetWindowPersistentId() const override; /* * Window Input Event @@ -746,6 +749,10 @@ protected: WMError UnregisterListenerInMap(std::unordered_map>>& listenerMap, int32_t persistentId, const sptr& listener) { + if (listener == nullptr) { + TLOGE(WmsLogTag::DEFAULT, "listener could not be null"); + return WMError::WM_ERROR_NULLPTR; + } auto it = listenerMap.find(persistentId); if (it == listenerMap.end()) { return WMError::WM_OK; @@ -980,8 +987,6 @@ protected: std::atomic_bool hasSetEnableDrag_ = false; void HookWindowSizeByHookWindowInfo(Rect& rect); void SetAppHookWindowInfo(const HookWindowInfo& hookWindowInfo); - HookWindowInfo GetAppHookWindowInfo(); - virtual WMError GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) { return WMError::WM_OK; } virtual WMError GetSelectMode(SelectMode& selectMode) { return WMError::WM_OK; } /* @@ -1211,10 +1216,7 @@ private: const std::map& avoidAreas = {}); void SubmitNoInteractionMonitorTask(int32_t eventId, const IWindowNoInteractionListenerSptr& listener); virtual WMError GetAppForceLandscapeConfig(AppForceLandscapeConfig& config) { return WMError::WM_OK; }; - virtual WMError GetAppForceLandscapeConfigEnable(bool& enableForceSplit) { return WMError::WM_OK; }; WSError NotifyAppForceLandscapeConfigUpdated() override; - WSError NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) override; void SetFrameLayoutCallbackEnable(bool enable); void UpdateFrameLayoutCallbackIfNeeded(WindowSizeChangeReason wmReason); void SetUniqueVirtualPixelRatioForSub(bool useUniqueDensity, float virtualPixelRatio); @@ -1353,8 +1355,6 @@ private: std::atomic lastStatusWhenNotifyWindowStatusDidChange_ = WindowStatus::WINDOW_STATUS_UNDEFINED; std::atomic lastStatusWhenNotifyParentStatusChange_ = WindowStatus::WINDOW_STATUS_UNDEFINED; SizeChangeReason globalDisplayRectSizeChangeReason_ = SizeChangeReason::END; - std::shared_mutex hookWindowInfoMutex_; - HookWindowInfo hookWindowInfo_; std::atomic_bool notifySizeChangeFlag_ = false; std::atomic isFirstValidLayoutUpdate_ = true; mutable std::mutex globalScaledRectMutex_; diff --git a/wm/src/window_scene.cpp b/wm/src/window_scene.cpp index ff5d37ec21..e4cec16c5e 100644 --- a/wm/src/window_scene.cpp +++ b/wm/src/window_scene.cpp @@ -186,15 +186,15 @@ WMError WindowScene::GoResume(bool isGamePreLaunch) return WMError::WM_OK; } -WMError WindowScene::GoPause() +WMError WindowScene::GoPause(bool isGamePreLaunch) { - TLOGI(WmsLogTag::WMS_LIFE, "in"); + TLOGI(WmsLogTag::WMS_LIFE, "in isGamePreLaunch: %{public}d", isGamePreLaunch); auto mainWindow = GetMainWindow(); if (mainWindow == nullptr) { TLOGE(WmsLogTag::WMS_LIFE, "failed, because main window is null"); return WMError::WM_ERROR_NULLPTR; } - mainWindow->Pause(); + mainWindow->Pause(isGamePreLaunch); return WMError::WM_OK; } diff --git a/wm/src/window_scene_session_impl.cpp b/wm/src/window_scene_session_impl.cpp index 76375098f7..daf0a9eaf8 100644 --- a/wm/src/window_scene_session_impl.cpp +++ b/wm/src/window_scene_session_impl.cpp @@ -214,6 +214,47 @@ void RecalculateLimits(double maxRatio, double minRatio, WindowLimits& limits) limits.minHeight_ = std::max(newMinHeight, limits.minHeight_); } +/** + * @brief Calculate intersection between current limits and attached limits. + * @param currentLimits Current window limits. + * @param attachedLimits Attached window limits. + * @param intersectHeight Whether to intersect height limits. + * @param intersectWidth Whether to intersect width limits. + * @return Intersected limits. + */ +WindowLimits CalculateLimitsIntersection(const WindowLimits& currentLimits, + const WindowLimits& attachedLimits, bool intersectHeight, bool intersectWidth) +{ + WindowLimits result = currentLimits; + if (intersectHeight) { + result.minHeight_ = std::max(currentLimits.minHeight_, attachedLimits.minHeight_); + result.maxHeight_ = std::min(currentLimits.maxHeight_, attachedLimits.maxHeight_); + } + if (intersectWidth) { + result.minWidth_ = std::max(currentLimits.minWidth_, attachedLimits.minWidth_); + result.maxWidth_ = std::min(currentLimits.maxWidth_, attachedLimits.maxWidth_); + } + return result; +} + +/** + * @brief Check if limits intersection is valid (min <= max). + * @param limits Limits to validate. + * @param checkHeight Whether to check height limits. + * @param checkWidth Whether to check width limits. + * @return true if intersection is valid, false otherwise. + */ +bool IsLimitsIntersectionValid(const WindowLimits& limits, bool checkHeight, bool checkWidth) +{ + if (checkWidth && limits.minWidth_ > limits.maxWidth_) { + return false; + } + if (checkHeight && limits.minHeight_ > limits.maxHeight_) { + return false; + } + return true; +} + /** * @brief Returns candidate if it lies within the inclusive range; otherwise returns fallback. * @@ -1820,7 +1861,7 @@ void WindowSceneSessionImpl::RecalculateSizeLimitsWithRatios(WindowLimits& limit } /** @note @window.layout */ -void WindowSceneSessionImpl::UpdateWindowSizeLimits() +void WindowSceneSessionImpl::UpdateWindowSizeLimits(bool needNotifySession) { WindowLimits customizedLimits; WindowLimits newLimits; @@ -1846,11 +1887,152 @@ void WindowSceneSessionImpl::UpdateWindowSizeLimits() newLimits.minHeight_ = 1; } + // Notify session side about window limits change before calculating attached windows intersection + // Save the limits regardless of needNotifySession flag + // Determine which limits to notify based on user's pixelUnit setting + const WindowLimits& limitsToNotify = + (property_->GetUserWindowLimits().pixelUnit_ == PixelUnit::VP) ? newLimitsVP : newLimits; + property_->SetLimitsForAttachedWindows(limitsToNotify); + + // Only notify session side when triggered by SetWindowLimits + if (needNotifySession) { + NotifySessionSideLimitsChanged(limitsToNotify); + } + + // Calculate intersection with attached windows' limits if configured + CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + property_->SetWindowLimits(newLimits); property_->SetWindowLimitsVP(newLimitsVP); property_->SetLastLimitsVpr(virtualPixelRatio); } +/** @note @window.layout */ +void WindowSceneSessionImpl::CalculateAttachedWindowLimitsIntersection( + WindowLimits& newLimits, WindowLimits& newLimitsVP, float virtualPixelRatio) +{ + if (!IsPcOrPadFreeMultiWindowMode()) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, not in PC or free multi-window mode", + GetPersistentId()); + return; + } + if (MathHelper::NearZero(virtualPixelRatio)) { + TLOGE(WmsLogTag::WMS_LAYOUT, "windowId:%{public}u, virtual pixel ratio is zero", GetWindowId()); + return; + } + + auto attachedLimitsList = property_->GetAttachedWindowLimitsList(); + if (attachedLimitsList.empty()) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, no attached limits", GetPersistentId()); + return; + } + + const bool isMainWindow = WindowHelper::IsMainWindow(GetType()); + TLOGI(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, calc with %{public}zu wins, type=%{public}u", + GetPersistentId(), attachedLimitsList.size(), static_cast(GetType())); + + for (const auto& [sourceId, attachedLimits] : attachedLimitsList) { + AttachLimitOptions limitOptions = isMainWindow ? property_->GetAttachedLimitOptions(sourceId) : + AttachLimitOptions { property_->GetWindowAnchorInfo().attachOptions.isIntersectedHeightLimit, + property_->GetWindowAnchorInfo().attachOptions.isIntersectedWidthLimit }; + if (!limitOptions.isIntersectedHeightLimit && !limitOptions.isIntersectedWidthLimit) { + continue; + } + auto result = CalcSingleWinIntersect( + newLimits, newLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + if (!result.pxValid || !result.vpValid) { + TLOGW(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, no intersect srcId=%{public}d (%{public}s)", + GetPersistentId(), sourceId, !result.pxValid ? "PX" : "VP"); + continue; + } + + newLimits = result.pxLimits; + newLimitsVP = result.vpLimits; + TLOGI(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, intersect srcId=%{public}d " + "PX[%{public}u,%{public}u,%{public}u,%{public}u] VP[%{public}u,%{public}u,%{public}u,%{public}u]", + GetPersistentId(), sourceId, newLimits.minWidth_, newLimits.maxWidth_, + newLimits.minHeight_, newLimits.maxHeight_, newLimitsVP.minWidth_, + newLimitsVP.maxWidth_, newLimitsVP.minHeight_, newLimitsVP.maxHeight_); + } +} + +/** @note @window.layout */ +WindowSceneSessionImpl::WinIntersectResult WindowSceneSessionImpl::CalcSingleWinIntersect( + const WindowLimits& currentLimits, + const WindowLimits& currentLimitsVP, + const WindowLimits& attachedLimits, + const AttachLimitOptions& limitOptions, + float virtualPixelRatio) +{ + WinIntersectResult result; + const bool intersectHeight = limitOptions.isIntersectedHeightLimit; + const bool intersectWidth = limitOptions.isIntersectedWidthLimit; + + // Convert to PX and calculate intersection + WindowLimits attachedLimitsPX; + if (attachedLimits.pixelUnit_ == PixelUnit::VP) { + RecalculatePxLimitsByVp(attachedLimits, attachedLimitsPX, virtualPixelRatio); + } else { + attachedLimitsPX = attachedLimits; + } + result.pxLimits = CalculateLimitsIntersection(currentLimits, attachedLimitsPX, intersectHeight, + intersectWidth); + result.pxValid = IsLimitsIntersectionValid(result.pxLimits, intersectHeight, intersectWidth); + + // Convert to VP and calculate intersection + WindowLimits attachedLimitsVP; + if (attachedLimits.pixelUnit_ == PixelUnit::PX) { + RecalculateVpLimitsByPx(attachedLimits, attachedLimitsVP, virtualPixelRatio); + } else { + attachedLimitsVP = attachedLimits; + } + result.vpLimits = CalculateLimitsIntersection(currentLimitsVP, attachedLimitsVP, intersectHeight, + intersectWidth); + result.vpValid = IsLimitsIntersectionValid(result.vpLimits, intersectHeight, intersectWidth); + + return result; +} + +/** @note @window.layout */ +void WindowSceneSessionImpl::NotifySessionSideLimitsChanged(const WindowLimits& limitsToNotify) +{ + if (GetHostSession() == nullptr) { + return; + } + + WindowType windowType = GetType(); + bool shouldNotify = false; + + if (WindowHelper::IsMainWindow(windowType)) { + // Main window: check if any sub window attached with intersected limits + auto attachedLimitOptionsList = property_->GetAttachedLimitOptionsList(); + shouldNotify = !attachedLimitOptionsList.empty(); + TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d is main window, has %{public}zu attached windows with limits", + GetWindowId(), attachedLimitOptionsList.size()); + } else if (WindowHelper::IsSubWindow(windowType)) { + // Sub window: check if attached with intersected limits via windowAnchorInfo + WindowAnchorInfo anchorInfo = property_->GetWindowAnchorInfo(); + shouldNotify = anchorInfo.isAnchoredByAttach_ && + (anchorInfo.attachOptions.isIntersectedWidthLimit || + anchorInfo.attachOptions.isIntersectedHeightLimit); + TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d is sub window, isAnchoredByAttach=%{public}d, " + "isIntersectedWidthLimit=%{public}d, isIntersectedHeightLimit=%{public}d", + GetWindowId(), anchorInfo.isAnchoredByAttach_, + anchorInfo.attachOptions.isIntersectedWidthLimit, + anchorInfo.attachOptions.isIntersectedHeightLimit); + } + + if (!shouldNotify) { + TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}d, no need to notify session side", GetWindowId()); + return; + } + + GetHostSession()->NotifyAttachedWindowsLimitsChanged(limitsToNotify); + TLOGI(WmsLogTag::WMS_LAYOUT, "Notified session side about window limits change for window " + "id=%{public}u with attach relationship and intersected limits, pixelUnit=%{public}u", + GetWindowId(), static_cast(property_->GetUserWindowLimits().pixelUnit_)); +} + void WindowSceneSessionImpl::PreLayoutOnShow(WindowType type, const sptr& info) { std::shared_ptr uiContent = GetUIContentSharedPtr(); @@ -2068,20 +2250,18 @@ void WindowSceneSessionImpl::Resume(bool isGamePreLaunch) isColdStart_, isDidForeground_, isGamePreLaunch); isDidForeground_ = true; isColdStart_ = false; - SetIsGamePreLaunch(isGamePreLaunch); NotifyAfterLifecycleResumed(isGamePreLaunch); } -void WindowSceneSessionImpl::Pause() +void WindowSceneSessionImpl::Pause(bool isGamePreLaunch) { - TLOGI(WmsLogTag::WMS_LIFE, "in, isColdStart: %{public}d isGamePreLaunch_: %{public}d", - isColdStart_, isGamePreLaunch_); + TLOGI(WmsLogTag::WMS_LIFE, "in, isColdStart: %{public}d isGamePreLaunch: %{public}d", + isColdStart_, isGamePreLaunch); isColdStart_ = false; NotifyAfterLifecyclePaused(); auto hostSession = GetHostSession(); - if (isGamePreLaunch_ && hostSession) { + if (isGamePreLaunch && hostSession) { hostSession->OnSessionEvent(SessionEvent::EVENT_CLEAR_GAME_PRELAUNCH_FLAG); - ClearIsGamePreLaunch(); } } @@ -6704,7 +6884,8 @@ WMError WindowSceneSessionImpl::SetWindowLimits(WindowLimits& windowLimits, bool customizedLimits.vpRatio_, windowLimits.pixelUnit_ }); - UpdateWindowSizeLimits(); + // Pass true to notify session side before calculating attached windows intersection + UpdateWindowSizeLimits(true); WMError ret = UpdateProperty(WSPropertyChangeAction::ACTION_UPDATE_WINDOW_LIMITS); if (ret != WMError::WM_OK) { TLOGE(WmsLogTag::WMS_LAYOUT, "update window proeprty failed! id: %{public}u.", GetWindowId()); @@ -8108,7 +8289,7 @@ WMError WindowSceneSessionImpl::GetWindowPropertyInfo(WindowPropertyInfo& window windowPropertyInfo.globalDisplayRect.ToString().c_str()); HookWindowSizeByHookWindowInfo(windowPropertyInfo.windowRect); HookWindowSizeByHookWindowInfo(windowPropertyInfo.globalDisplayRect); - auto hookWindowInfo = GetAppHookWindowInfo(); + auto hookWindowInfo = GetProperty()->GetHookWindowInfo(); if (hookWindowInfo.drawableRectHook) { HookWindowSizeByHookWindowInfo(windowPropertyInfo.drawableRect); } @@ -8185,6 +8366,97 @@ WSError WindowSceneSessionImpl::UpdatePropertyWhenTriggerMode(const sptrSetAttachedWindowLimits(sourcePersistentId, attachedWindowLimits); + + // 2. Store limit options for this specific attached window + AttachLimitOptions limitOptions{ isIntersectedHeightLimit, isIntersectedWidthLimit }; + property->SetAttachedLimitOptions(sourcePersistentId, limitOptions); + + // 3. Trigger UpdateWindowSizeLimits logic to recalculate with all attached limits + UpdateWindowSizeLimits(); + UpdateProperty(WSPropertyChangeAction::ACTION_UPDATE_WINDOW_LIMITS); + UpdateNewSize(); + + TLOGI(WmsLogTag::WMS_LAYOUT, "completed for window id=%{public}u", GetWindowId()); + return WSError::WS_OK; +} + +/** @note @window.layout */ +WSError WindowSceneSessionImpl::SyncAllAttachedLimitsToChild( + const std::vector>& limitsList, + const std::vector>& optionsList) +{ + TLOGI(WmsLogTag::WMS_LAYOUT, "called for window id=%{public}u, limitsList size=%{public}zu, " + "optionsList size=%{public}zu", GetWindowId(), limitsList.size(), optionsList.size()); + + const auto& property = GetProperty(); + + // 1. Clear existing attached limits first, then store all entries from the parent + property->ClearAttachedWindowLimitsList(); + property->ClearAttachedLimitOptionsList(); + + for (const auto& [sourceId, limits] : limitsList) { + property->SetAttachedWindowLimits(sourceId, limits); + } + + for (const auto& [sourceId, options] : optionsList) { + property->SetAttachedLimitOptions(sourceId, options); + } + + // 2. Trigger UpdateWindowSizeLimits logic to recalculate with all attached limits + UpdateWindowSizeLimits(); + UpdateProperty(WSPropertyChangeAction::ACTION_UPDATE_WINDOW_LIMITS); + UpdateNewSize(); + + TLOGI(WmsLogTag::WMS_LAYOUT, "completed for window id=%{public}u", GetWindowId()); + return WSError::WS_OK; +} + +/** @note @window.layout */ +WSError WindowSceneSessionImpl::RemoveAttachedWindowLimits(int32_t sourcePersistentId) +{ + TLOGI(WmsLogTag::WMS_LAYOUT, "called for window id=%{public}u, " + "sourcePersistentId=%{public}d", GetWindowId(), sourcePersistentId); + + const auto& property = GetProperty(); + + // Check if the source is this window itself (detaching from all attached windows) + if (sourcePersistentId == GetPersistentId()) { + // This window is detaching - clear all attached limits lists + TLOGI(WmsLogTag::WMS_LAYOUT, "Window id=%{public}u is detaching, clearing all attached limits", + GetWindowId()); + property->ClearAttachedWindowLimitsList(); + property->ClearAttachedLimitOptionsList(); + } else { + // Another window is detaching - remove that window's limits from the map + // 1. Remove the source window's limits from the map + property->RemoveAttachedWindowLimits(sourcePersistentId); + + // 2. Remove the source window's limit options + property->RemoveAttachedLimitOptions(sourcePersistentId); + } + + // 3. Trigger UpdateWindowSizeLimits logic to recalculate with remaining attached limits + UpdateWindowSizeLimits(); + UpdateProperty(WSPropertyChangeAction::ACTION_UPDATE_WINDOW_LIMITS); + UpdateNewSize(); + + TLOGI(WmsLogTag::WMS_LAYOUT, "completed for window id=%{public}u", GetWindowId()); + return WSError::WS_OK; +} + WMError WindowSceneSessionImpl::SetHookTargetElementInfo(const AppExecFwk::ElementName& elementName) { auto context = GetContext(); @@ -8212,44 +8484,19 @@ WMError WindowSceneSessionImpl::GetAppForceLandscapeConfig(AppForceLandscapeConf return hostSession->GetAppForceLandscapeConfig(config); } -WMError WindowSceneSessionImpl::GetAppForceLandscapeConfigEnable(bool& enableForceSplit) -{ - if (IsWindowSessionInvalid()) { - TLOGE(WmsLogTag::DEFAULT, "HostSession is invalid"); - return WMError::WM_ERROR_INVALID_WINDOW; - } - auto hostSession = GetHostSession(); - CHECK_HOST_SESSION_RETURN_ERROR_IF_NULL(hostSession, WMError::WM_ERROR_NULLPTR); - return hostSession->GetAppForceLandscapeConfigEnable(enableForceSplit); -} - WSError WindowSceneSessionImpl::NotifyAppForceLandscapeConfigUpdated() { TLOGI(WmsLogTag::DEFAULT, "in"); WindowType winType = GetType(); AppForceLandscapeConfig config = {}; if (WindowHelper::IsMainWindow(winType) && GetAppForceLandscapeConfig(config) == WMError::WM_OK && - config.supportSplit_ > 0) { + (config.containsSysConfig_ || config.containsAppConfig_)) { SetForceSplitConfig(config); return WSError::WS_OK; } return WSError::WS_DO_NOTHING; } -WSError WindowSceneSessionImpl::NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, - SelectMode selectMode) -{ - TLOGI(WmsLogTag::DEFAULT, "in"); - WindowType winType = GetType(); - bool enableForceSplit = false; - if (WindowHelper::IsMainWindow(winType) && - GetAppForceLandscapeConfigEnable(enableForceSplit) == WMError::WM_OK) { - SetForceSplitConfigEnable(enableForceSplit, needUpdateViewport, selectMode); - return WSError::WS_OK; - } - return WSError::WS_DO_NOTHING; -} - void WindowSceneSessionImpl::SetForceSplitConfigEnable(bool enableForceSplit, bool needUpdateViewport, SelectMode selectMode) { @@ -8308,13 +8555,6 @@ void WindowSceneSessionImpl::SendCombinedCompatibleConfigToArkUI() TLOGI(WmsLogTag::WMS_COMPAT, "Send combined compatible config to arkui successfully!"); } -WMError WindowSceneSessionImpl::GetAppHookWindowInfoFromServer(HookWindowInfo& hookWindowInfo) -{ - auto hostSession = GetHostSession(); - CHECK_HOST_SESSION_RETURN_ERROR_IF_NULL(hostSession, WMError::WM_ERROR_NULLPTR); - return hostSession->GetAppHookWindowInfoFromServer(hookWindowInfo); -} - WMError WindowSceneSessionImpl::GetSelectMode(SelectMode& selectMode) { auto hostSession = GetHostSession(); @@ -8322,23 +8562,6 @@ WMError WindowSceneSessionImpl::GetSelectMode(SelectMode& selectMode) return hostSession->GetSelectMode(selectMode); } -WSError WindowSceneSessionImpl::NotifyAppHookWindowInfoUpdated() -{ - TLOGI(WmsLogTag::WMS_LAYOUT, "in"); - const WindowType windowType = GetType(); - if (!WindowHelper::IsMainWindow(windowType)) { - return WSError::WS_DO_NOTHING; - } - - HookWindowInfo hookWindowInfo{}; - if (GetAppHookWindowInfoFromServer(hookWindowInfo) != WMError::WM_OK) { - return WSError::WS_DO_NOTHING; - } - - SetAppHookWindowInfo(hookWindowInfo); - return WSError::WS_OK; -} - WSError WindowSceneSessionImpl::UpdateAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) { TLOGI(WmsLogTag::WMS_LAYOUT, "in"); @@ -8350,6 +8573,18 @@ WSError WindowSceneSessionImpl::UpdateAppHookWindowInfo(const HookWindowInfo& ho return WSError::WS_OK; } +WSError WindowSceneSessionImpl::SetForceSplitEnable(bool isForceSplitEnabled, bool needUpdateViewport, + SelectMode selectMode) +{ + TLOGI(WmsLogTag::WMS_COMPAT, "in"); + const WindowType windowType = GetType(); + if (!WindowHelper::IsMainWindow(windowType)) { + return WSError::WS_DO_NOTHING; + } + SetForceSplitConfigEnable(isForceSplitEnabled, needUpdateViewport, selectMode); + return WSError::WS_OK; +} + WMError WindowSceneSessionImpl::SetSubWindowSource(SubWindowSource source) { if (IsWindowSessionInvalid()) { diff --git a/wm/src/window_session_impl.cpp b/wm/src/window_session_impl.cpp index 66e8833a33..e8b18a97f7 100644 --- a/wm/src/window_session_impl.cpp +++ b/wm/src/window_session_impl.cpp @@ -77,8 +77,6 @@ namespace OHOS { namespace Rosen { namespace { constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "WindowSessionImpl"}; -constexpr int32_t FORCE_SPLIT_MODE = 5; -constexpr int32_t NAV_FORCE_SPLIT_MODE = 6; constexpr int32_t API_VERSION_18 = 18; constexpr uint32_t API_VERSION_MOD = 1000; constexpr int32_t WINDOW_ROTATION_CHANGE = 50; @@ -829,6 +827,7 @@ WMError WindowSessionImpl::Connect() RegisterWindowScaleCallback(); } FloatViewManager::isSupportFloatView_ = windowSystemConfig_.supportCreateFloatView_; + SetAppHookWindowInfo(property_->GetHookWindowInfo()); return static_cast(ret); } @@ -2611,12 +2610,9 @@ void WindowSessionImpl::SetForceSplitConfig(const AppForceLandscapeConfig& confi void WindowSessionImpl::SetAppHookWindowInfo(const HookWindowInfo& hookWindowInfo) { bool notifyWindowChange = hookWindowInfo.notifyWindowChange; - { - std::unique_lock lock(hookWindowInfoMutex_); - TLOGI(WmsLogTag::WMS_COMPAT, "Id:%{public}u, preHookWindowInfo:[%{public}s], newHookWindowInfo:[%{public}s]", - GetWindowId(), hookWindowInfo_.ToString().c_str(), hookWindowInfo.ToString().c_str()); - hookWindowInfo_ = hookWindowInfo; - } + TLOGI(WmsLogTag::WMS_COMPAT, "Id:%{public}u, preHookWindowInfo:[%{public}s], newHookWindowInfo:[%{public}s]", + GetWindowId(), GetProperty()->GetHookWindowInfo().ToString().c_str(), hookWindowInfo.ToString().c_str()); + property_->SetHookWindowInfo(hookWindowInfo); if (notifyWindowChange) { if (state_ == WindowState::STATE_SHOWN) { const auto& windowRect = GetRect(); @@ -2629,15 +2625,9 @@ void WindowSessionImpl::SetAppHookWindowInfo(const HookWindowInfo& hookWindowInf } } -HookWindowInfo WindowSessionImpl::GetAppHookWindowInfo() -{ - std::shared_lock lock(hookWindowInfoMutex_); - return hookWindowInfo_; -} - void WindowSessionImpl::HookWindowSizeByHookWindowInfo(Rect& rect) { - auto hookWindowInfo = GetAppHookWindowInfo(); + auto hookWindowInfo = GetProperty()->GetHookWindowInfo(); if (!hookWindowInfo.enableHookWindow || !WindowHelper::IsMainWindow(GetType()) || isFullScreenInForceSplit_.load()) { TLOGD(WmsLogTag::WMS_LAYOUT, "Id:%{public}u, do not need hook window info.", GetWindowId()); @@ -2696,24 +2686,18 @@ WMError WindowSessionImpl::SetUIContentInner(const std::string& contentInfo, voi AppForceLandscapeConfig config = {}; if (WindowHelper::IsMainWindow(winType) && GetAppForceLandscapeConfig(config) == WMError::WM_OK && - config.supportSplit_ > 0) { + (config.containsSysConfig_ || config.containsAppConfig_)) { SetForceSplitConfig(config); - bool enableForceSplit = false; - if ((config.mode_ == FORCE_SPLIT_MODE || config.mode_ == NAV_FORCE_SPLIT_MODE) && - GetAppForceLandscapeConfigEnable(enableForceSplit) == WMError::WM_OK) { - // try to fetch selectMode - SelectMode finalSelectMode = SelectMode::INVALID_MODE; - if (GetSelectMode(finalSelectMode) != WMError::WM_OK) { - TLOGI(WmsLogTag::WMS_LAYOUT, "get selectMode fail, id:%{public}d", GetPersistentId()); - finalSelectMode = SelectMode::INVALID_MODE; - } else { - TLOGI(WmsLogTag::WMS_LAYOUT, "get selectMode success, id:%{public}d, selectMode: %{public}u", - GetPersistentId(), static_cast(finalSelectMode)); - } - SetForceSplitConfigEnable(enableForceSplit, false, finalSelectMode); + // try to fetch selectMode + SelectMode finalSelectMode = SelectMode::INVALID_MODE; + if (GetSelectMode(finalSelectMode) != WMError::WM_OK) { + TLOGE(WmsLogTag::WMS_COMPAT, "get selectMode fail, id: %{public}d", GetPersistentId()); + finalSelectMode = SelectMode::INVALID_MODE; } else { - SetForceSplitConfigEnable(false); + TLOGI(WmsLogTag::WMS_COMPAT, "get selectMode success, id: %{public}d, selectMode: %{public}u", + GetPersistentId(), static_cast(finalSelectMode)); } + SetForceSplitConfigEnable(property_->GetForceSplitEnable(), false, finalSelectMode); } uint32_t version = 0; @@ -9037,11 +9021,6 @@ WSError WindowSessionImpl::NotifyAppForceLandscapeConfigUpdated() return WSError::WS_DO_NOTHING; } -WSError WindowSessionImpl::NotifyAppForceLandscapeConfigEnableUpdated(bool needUpdateViewport, SelectMode selectMode) -{ - return WSError::WS_DO_NOTHING; -} - void WindowSessionImpl::SetFrameLayoutCallbackEnable(bool enable) { enableFrameLayoutFinishCb_ = enable; @@ -9779,6 +9758,16 @@ bool WindowSessionImpl::IsAnco() const return property_->GetCollaboratorType() == static_cast(CollaboratorType::RESERVE_TYPE); } +bool WindowSessionImpl::GetIsAtomicService() const +{ + return property_->GetIsAtomicService(); +} + +int32_t WindowSessionImpl::GetWindowPersistentId() const +{ + return GetPersistentId(); +} + bool WindowSessionImpl::OnPointDown(int32_t eventId, int32_t posX, int32_t posY) { auto hostSession = GetHostSession(); diff --git a/wm/test/unittest/float_window_manager_test.cpp b/wm/test/unittest/float_window_manager_test.cpp index 1e10babf96..2c6fa554f9 100644 --- a/wm/test/unittest/float_window_manager_test.cpp +++ b/wm/test/unittest/float_window_manager_test.cpp @@ -350,6 +350,7 @@ HWTEST_F(FloatWindowManagerTest, StopBindFloatView, TestSize.Level1) EXPECT_CALL(*mockFvController, IsBind()).WillRepeatedly(Return(false)); FloatWindowManager::Bind(mockFvController, fbController_, *fbOption_); + mockFvController->curState_ = FvWindowState::FV_STATE_STARTED; EXPECT_EQ(WMError::WM_ERROR_INVALID_WINDOW, FloatWindowManager::StopBindFloatView(mockFvController)); EXPECT_CALL(*mw_, Destroy(_)).WillRepeatedly(Return(WMError::WM_OK)); diff --git a/wm/test/unittest/layout/window_scene_session_impl_layout_test.cpp b/wm/test/unittest/layout/window_scene_session_impl_layout_test.cpp index 2ea4d9c980..05be4a9a08 100644 --- a/wm/test/unittest/layout/window_scene_session_impl_layout_test.cpp +++ b/wm/test/unittest/layout/window_scene_session_impl_layout_test.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include "ability_context_impl.h" @@ -61,6 +62,17 @@ public: std::shared_ptr abilityContext_; +protected: + // Helper function to create WindowSceneSessionImpl with option + sptr CreateWindowSession(const std::string& name, WindowType windowType) + { + sptr option = sptr::MakeSptr(); + option->SetWindowName(name); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(windowType); + return window; + } + private: RSSurfaceNode::SharedPtr CreateRSSurfaceNode(); static constexpr uint32_t WAIT_SYNC_IN_NS = 200000; @@ -921,57 +933,1154 @@ HWTEST_F(WindowSceneSessionImplLayoutTest, FillWindowLimits_By_PixelUnit, TestSi } /** - * @tc.name: GetAppHookWindowInfoFromServer - * @tc.desc: GetAppHookWindowInfoFromServer + * @tc.name: UpdateAttachedWindowLimits01 + * @tc.desc: Test UpdateAttachedWindowLimits with null sessionStage * @tc.type: FUNC */ -HWTEST_F(WindowSceneSessionImplLayoutTest, GetAppHookWindowInfoFromServer, TestSize.Level1) +HWTEST_F(WindowSceneSessionImplLayoutTest, UpdateAttachedWindowLimits01, Function | SmallTest | Level2) { sptr option = sptr::MakeSptr(); - option->SetWindowName("GetAppHookWindowInfoFromServer"); - option->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + option->SetWindowName("UpdateAttachedWindowLimits01"); sptr window = sptr::MakeSptr(option); - const int32_t windowId = 2025; - window->property_->SetPersistentId(windowId); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); - SessionInfo sessionInfo = { "CreateTestBundle", "CreateTestModule", "CreateTestAbility" }; - sptr session = sptr::MakeSptr(sessionInfo); - window->hostSession_ = session; - HookWindowInfo hookWindowInfo; - WMError res = window->GetAppHookWindowInfoFromServer(hookWindowInfo); - EXPECT_NE(res, WMError::WM_ERROR_INVALID_WINDOW); + WindowLimits limits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = window->UpdateAttachedWindowLimits(999, limits, false, false); + EXPECT_EQ(res, WSError::WS_OK); } /** - * @tc.name: NotifyAppHookWindowInfoUpdated - * @tc.desc: NotifyAppHookWindowInfoUpdated + * @tc.name: UpdateAttachedWindowLimits02 + * @tc.desc: Test UpdateAttachedWindowLimits with valid sessionStage * @tc.type: FUNC */ -HWTEST_F(WindowSceneSessionImplLayoutTest, NotifyAppHookWindowInfoUpdated, TestSize.Level1) +HWTEST_F(WindowSceneSessionImplLayoutTest, UpdateAttachedWindowLimits02, Function | SmallTest | Level2) { sptr option = sptr::MakeSptr(); - option->SetWindowName("NotifyAppHookWindowInfoUpdated"); - option->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + option->SetWindowName("UpdateAttachedWindowLimits02"); sptr window = sptr::MakeSptr(option); - const int32_t windowId = 2025; - window->property_->SetPersistentId(windowId); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); - // Case 1: GetAppHookWindowInfoFromServer failed - window->hostSession_ = nullptr; - WSError res = window->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(res, WSError::WS_DO_NOTHING); - - // Case 2: success - SessionInfo sessionInfo = { "CreateTestBundle", "CreateTestModule", "CreateTestAbility" }; + // Create and attach sessionStage mock + SessionInfo sessionInfo = { "UpdateAttachedWindowLimits02", "Module", "Ability" }; sptr session = sptr::MakeSptr(sessionInfo); window->hostSession_ = session; - res = window->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(res, WSError::WS_OK); - // Case 3: not mainWindow + WindowLimits limits = { 1800, 1200, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WSError res = window->UpdateAttachedWindowLimits(888, limits, true, true); + EXPECT_EQ(res, WSError::WS_OK); +} + +/** + * @tc.name: RemoveAttachedWindowLimits01 + * @tc.desc: Test RemoveAttachedWindowLimits functionality + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, RemoveAttachedWindowLimits01, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("RemoveAttachedWindowLimits01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + SessionInfo sessionInfo = { "RemoveAttachedWindowLimits01", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // First add limits + WindowLimits limits = { 2000, 1000, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(777, limits, true, true); + + // Then remove + WSError res = window->RemoveAttachedWindowLimits(777); + EXPECT_EQ(res, WSError::WS_OK); +} + +/** + * @tc.name: CalcSingleWinIntersect01 + * @tc.desc: Test PX limits intersection calculation + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect01, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WindowLimits attachedLimits = { 800, 1500, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_TRUE(result.pxValid); + EXPECT_TRUE(result.vpValid); + EXPECT_EQ(result.pxLimits.minWidth_, 150); // max(100, 150) + EXPECT_EQ(result.pxLimits.maxWidth_, 800); // min(2000, 800) + EXPECT_EQ(result.pxLimits.minHeight_, 1000); // max(1000, 250) + EXPECT_EQ(result.pxLimits.maxHeight_, 1500); // min(2000, 1500) +} + +/** + * @tc.name: CalcSingleWinIntersect02 + * @tc.desc: Test VP limits intersection calculation + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect02, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WindowLimits attachedLimits = { 1200, 600, 80, 120, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_TRUE(result.pxValid); + EXPECT_TRUE(result.vpValid); + EXPECT_EQ(result.vpLimits.minWidth_, 80); // max(50, 80) + EXPECT_EQ(result.vpLimits.maxWidth_, 1000); // min(1000, 1200) +} + +/** + * @tc.name: CalcSingleWinIntersect03 + * @tc.desc: Test invalid intersection (min > max) + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect03, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect03"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + // Attached limits have minWidth > current maxWidth (no intersection) + WindowLimits attachedLimits = { 2200, 3000, 2500, 500, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_FALSE(result.pxValid); +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection01 + * @tc.desc: Test multiple attached windows with priority order + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection01, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // Simulate attached windows list + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 2200, 1200, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + window->property_->SetAttachedWindowLimits(2, limits2); + + // Set limit options for each window + AttachLimitOptions limitOptions{ true, true }; + window->property_->SetAttachedLimitOptions(1, limitOptions); + window->property_->SetAttachedLimitOptions(2, limitOptions); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // First attached window: minWidth=max(100,200)=200, maxWidth=min(2500,2000)=2000 + // Second window applied: minWidth=max(200,150)=200, maxWidth=min(2000,2200)=2000 + EXPECT_EQ(newLimits.minWidth_, 200); + EXPECT_EQ(newLimits.maxWidth_, 2000); +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection02 + * @tc.desc: Test with empty attached limits list + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection02, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Limits should remain unchanged + EXPECT_EQ(newLimits.minWidth_, 100); + EXPECT_EQ(newLimits.maxWidth_, 2500); +} + +/** + * @tc.name: UpdateAttachedWindowLimits03 + * @tc.desc: Test UpdateAttachedWindowLimits updates existing sourceId + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, UpdateAttachedWindowLimits03, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("UpdateAttachedWindowLimits03"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "UpdateAttachedWindowLimits03", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // Add limits first time + WindowLimits limits1 = { 1000, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(111, limits1, true, false); + + // Update with new limits for same sourceId + WindowLimits limits2 = { 1200, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(111, limits2, true, true); + + // Verify the limits were updated, not duplicated + auto attachedList = window->property_->GetAttachedWindowLimitsList(); + bool found = false; + for (const auto& [id, limits] : attachedList) { + if (id == 111) { + EXPECT_EQ(limits.minWidth_, 150); + EXPECT_EQ(limits.maxWidth_, 1200); + found = true; + break; + } + } + EXPECT_TRUE(found); +} + +/** + * @tc.name: UpdateAttachedWindowLimits04 + * @tc.desc: Test UpdateAttachedWindowLimits with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, UpdateAttachedWindowLimits04, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("UpdateAttachedWindowLimits04"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "UpdateAttachedWindowLimits04", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limits = { 800, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WSError res = window->UpdateAttachedWindowLimits(222, limits, false, true); + EXPECT_EQ(res, WSError::WS_OK); +} + +/** + * @tc.name: RemoveAttachedWindowLimits02 + * @tc.desc: Test RemoveAttachedWindowLimits with non-existent sourceId + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, RemoveAttachedWindowLimits02, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("RemoveAttachedWindowLimits02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "RemoveAttachedWindowLimits02", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // Try to remove non-existent sourceId + WSError res = window->RemoveAttachedWindowLimits(99999); + EXPECT_EQ(res, WSError::WS_OK); +} + +/** + * @tc.name: RemoveAttachedWindowLimits03 + * @tc.desc: Test RemoveAttachedWindowLimits with multiple sources + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, RemoveAttachedWindowLimits03, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("RemoveAttachedWindowLimits03"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "RemoveAttachedWindowLimits03", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // Add multiple limits + WindowLimits limits1 = { 1000, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 1200, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits3 = { 1500, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(1, limits1, true, true); + window->UpdateAttachedWindowLimits(2, limits2, true, true); + window->UpdateAttachedWindowLimits(3, limits3, true, true); + + // Remove middle one + window->RemoveAttachedWindowLimits(2); + + // Verify only 2 remain and sourceId 2 is removed + auto attachedList = window->property_->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 2u); + for (const auto& [id, limits] : attachedList) { + EXPECT_NE(id, 2); + } +} + +/** + * @tc.name: RemoveAttachedWindowLimits04 + * @tc.desc: Test RemoveAttachedWindowLimits when sourceId matches current window (detaching) + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, RemoveAttachedWindowLimits04, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("RemoveAttachedWindowLimits04"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "RemoveAttachedWindowLimits04", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // Add multiple limits + WindowLimits limits1 = { 1000, 800, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 1200, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits3 = { 1500, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(1, limits1, true, true); + window->UpdateAttachedWindowLimits(2, limits2, true, true); + window->UpdateAttachedWindowLimits(3, limits3, true, true); + + // Verify limits are added + auto attachedList = window->property_->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 3u); + + // Call RemoveAttachedWindowLimits with current window's persistentId + // This simulates the window detaching from all attached windows + int32_t currentPersistentId = window->GetPersistentId(); + window->RemoveAttachedWindowLimits(currentPersistentId); + + // Verify all limits are cleared + attachedList = window->property_->GetAttachedWindowLimitsList(); + EXPECT_EQ(attachedList.size(), 0u); + + // Verify limit options are also cleared + auto limitOptionsList = window->property_->GetAttachedLimitOptionsList(); + EXPECT_EQ(limitOptionsList.size(), 0u); +} + +/** + * @tc.name: CalcSingleWinIntersect04 + * @tc.desc: Test intersection with height only + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect04, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect04"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WindowLimits attachedLimits = { 1100, 1500, 80, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + // Only intersect height + AttachLimitOptions limitOptions{ true, false }; // height=true, width=false + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_TRUE(result.pxValid); + EXPECT_TRUE(result.vpValid); + // Width should remain unchanged + EXPECT_EQ(result.pxLimits.minWidth_, 100); + EXPECT_EQ(result.pxLimits.maxWidth_, 2000); + // Height should be intersected + EXPECT_EQ(result.pxLimits.minHeight_, 1000); // max(1000, 250) + EXPECT_EQ(result.pxLimits.maxHeight_, 1500); // min(2000, 1500) +} + +/** + * @tc.name: CalcSingleWinIntersect05 + * @tc.desc: Test intersection with width only + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect05, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect05"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WindowLimits attachedLimits = { 800, 2200, 150, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + // Only intersect width + AttachLimitOptions limitOptions{ false, true }; // height=false, width=true + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_TRUE(result.pxValid); + EXPECT_TRUE(result.vpValid); + // Width should be intersected + EXPECT_EQ(result.pxLimits.minWidth_, 150); // max(100, 150) + EXPECT_EQ(result.pxLimits.maxWidth_, 800); // min(2000, 800) + // Height should remain unchanged + EXPECT_EQ(result.pxLimits.minHeight_, 1000); + EXPECT_EQ(result.pxLimits.maxHeight_, 2000); +} + +/** + * @tc.name: CalcSingleWinIntersect06 + * @tc.desc: Test intersection with both PX and VP invalid + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect06, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect06"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 1000, 500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 500, 250, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + // Attached limits have no intersection with current + WindowLimits attachedLimits = { 1500, 1500, 150, 800, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_FALSE(result.pxValid); + EXPECT_FALSE(result.vpValid); +} + +/** + * @tc.name: CalcSingleWinIntersect07 + * @tc.desc: Test VP to PX conversion in intersection + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect07, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect07"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + // Attached limits in VP, should be converted to PX + WindowLimits attachedLimits = { 900, 600, 60, 110, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_TRUE(result.pxValid); + EXPECT_TRUE(result.vpValid); + // VP->PX conversion: {maxWidth=1800, maxHeight=1200, minWidth=120, minHeight=220} + // Intersection PX: max(100,120)=120, min(2000,1800)=1800, max(1000,220)=1000, min(2000,1200)=1200 + EXPECT_EQ(result.pxLimits.minWidth_, 120); + EXPECT_EQ(result.pxLimits.maxWidth_, 1800); + EXPECT_EQ(result.pxLimits.minHeight_, 1000); + EXPECT_EQ(result.pxLimits.maxHeight_, 1200); +} + +/** + * @tc.name: CalcSingleWinIntersect08 + * @tc.desc: Test PX to VP conversion in intersection + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect08, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect08"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 1000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + // Attached limits in PX, should be converted to VP + // maxHeight_=1200 so PX intersection is valid: min(2000,1200)=1200 >= max(1000,220)=1000 + WindowLimits attachedLimits = { 1800, 1200, 120, 220, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 2.0f; + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + EXPECT_TRUE(result.pxValid); + EXPECT_TRUE(result.vpValid); + // PX intersection: maxWidth_=min(2000,1800)=1800, maxHeight_=min(2000,1200)=1200 + // minWidth_=max(100,120)=120, minHeight_=max(1000,220)=1000 + // VP->PX conversion of attached: {900, 600, 60, 110} + // VP intersection: maxWidth_=min(1000,900)=900, minWidth_=max(50,60)=60 + EXPECT_EQ(result.vpLimits.minWidth_, 60); + EXPECT_EQ(result.vpLimits.maxWidth_, 900); +} + +/** + * @tc.name: CalcSingleWinIntersect09 + * @tc.desc: Test intersection with zero virtual pixel ratio + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalcSingleWinIntersect09, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalcSingleWinIntersect09"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + WindowLimits currentLimits = { 2000, 2000, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits currentLimitsVP = { 1000, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + WindowLimits attachedLimits = { 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + AttachLimitOptions limitOptions{ true, true }; // Enable both height and width limits intersection + float virtualPixelRatio = 0.0f; // Invalid ratio + + auto result = window->CalcSingleWinIntersect( + currentLimits, currentLimitsVP, attachedLimits, limitOptions, virtualPixelRatio); + + // With zero ratio, conversion fails but direct PX intersection should still work + EXPECT_TRUE(result.pxValid); +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection03 + * @tc.desc: Test with three attached windows (priority order) + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection03, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection03"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // Simulate three attached windows with different limits + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 2200, 1200, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits3 = { 1800, 900, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + window->property_->SetAttachedWindowLimits(2, limits2); + window->property_->SetAttachedWindowLimits(3, limits3); + + // Set limit options for each window + AttachLimitOptions limitOptions{ true, true }; + window->property_->SetAttachedLimitOptions(1, limitOptions); + window->property_->SetAttachedLimitOptions(2, limitOptions); + window->property_->SetAttachedLimitOptions(3, limitOptions); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // First window: minWidth=max(100,200)=200, maxWidth=min(2500,2000)=2000 + // Second window: minWidth=max(200,250)=250, maxWidth=min(2000,2200)=2000 + // Third window: minWidth=max(250,150)=250, maxWidth=min(2000,1800)=1800 + EXPECT_EQ(newLimits.minWidth_, 250); + EXPECT_EQ(newLimits.maxWidth_, 1800); +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection04 + * @tc.desc: Test with no intersect flags set + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection04, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection04"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + + // No intersect flags set + AttachLimitOptions limitOptions{ false, false }; + window->property_->SetAttachedLimitOptions(1, limitOptions); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Limits should remain unchanged since no intersect flags + EXPECT_EQ(newLimits.minWidth_, 100); + EXPECT_EQ(newLimits.maxWidth_, 2500); // maxWidth_ unchanged, was maxHeight_ value 1500 +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection05 + * @tc.desc: Test with window that has no intersection (skipped) + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection05, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection05"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // First window has valid intersection + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + // Second window has no intersection + WindowLimits limits2 = { 4000, 3000, 2000, 3000, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + window->property_->SetAttachedWindowLimits(2, limits2); + + // Set limit options for both windows + AttachLimitOptions limitOptions{ true, true }; + window->property_->SetAttachedLimitOptions(1, limitOptions); + window->property_->SetAttachedLimitOptions(2, limitOptions); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Should only apply first window's limits (second skipped due to invalid intersection) + EXPECT_EQ(newLimits.minWidth_, 200); + EXPECT_EQ(newLimits.maxWidth_, 2000); +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection06 + * @tc.desc: Test with mixed PX and VP attached limits + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection06, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection06"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // First window in PX + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + // Second window in VP + WindowLimits limits2 = { 800, 400, 80, 120, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + window->property_->SetAttachedWindowLimits(1, limits1); + window->property_->SetAttachedWindowLimits(2, limits2); + + // Set limit options for both windows + AttachLimitOptions limitOptions{ true, true }; + window->property_->SetAttachedLimitOptions(1, limitOptions); + window->property_->SetAttachedLimitOptions(2, limitOptions); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.5f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // First window PX: minWidth=max(100,200)=200, maxWidth=min(2500,2000)=2000 + // Second window VP->PX: {maxWidth=2000,maxHeight=1000,minWidth=200,minHeight=300} + // PX: minWidth=max(200,200)=200, maxWidth=min(2000,2000)=2000 + EXPECT_EQ(newLimits.minWidth_, 200); + EXPECT_EQ(newLimits.maxWidth_, 2000); +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection07 + * @tc.desc: Test with zero virtualPixelRatio + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection07, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection07"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // Add attached limits + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 0.0f; // Zero ratio - function should return early + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Limits should remain unchanged due to zero ratio + EXPECT_EQ(newLimits.minWidth_, 100); + EXPECT_EQ(newLimits.maxWidth_, 2500); // maxWidth_ unchanged, was maxHeight_ value 1500 + EXPECT_EQ(newLimitsVP.minWidth_, 50); + EXPECT_EQ(newLimitsVP.maxWidth_, 1250); // maxWidthVP_ unchanged, was maxHeightVP_ value 750 +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection08 + * @tc.desc: Test when not in PC or pad free multi-window mode + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection08, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection08"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + // Set freeMultiWindowEnable to false - not in free multi-window mode + window->windowSystemConfig_.freeMultiWindowEnable_ = false; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // Add attached limits + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Limits should remain unchanged since not in free multi-window mode + EXPECT_EQ(newLimits.minWidth_, 100); + EXPECT_EQ(newLimits.maxWidth_, 2500); // maxWidth_ unchanged, was maxHeight_ value 1500 + EXPECT_EQ(newLimitsVP.minWidth_, 50); + EXPECT_EQ(newLimitsVP.maxWidth_, 1250); // maxWidthVP_ unchanged, was maxHeightVP_ value 750 +} + +/** + * @tc.name: CalculateAttachedWindowLimitsIntersection09 + * @tc.desc: Test with sub window (uses windowAnchorInfo instead of property limitOptions) + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, CalculateAttachedWindowLimitsIntersection09, + Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("CalculateAttachedWindowLimitsIntersection09"); + sptr window = sptr::MakeSptr(option); + // Sub window type window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); - res = window->NotifyAppHookWindowInfoUpdated(); - EXPECT_EQ(res, WSError::WS_DO_NOTHING); + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // Set up windowAnchorInfo for sub window + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.isFromAttachOrDetach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = true; + anchorInfo.attachOptions.isIntersectedWidthLimit = false; // Only limit height + window->property_->SetWindowAnchorInfo(anchorInfo); + + // Add attached limits (sub window doesn't use per-source limitOptions) + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + + WindowLimits newLimits = { 2500, 1500, 100, 200, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Height should be intersected, width should remain unchanged + EXPECT_EQ(newLimits.minWidth_, 100); // Width not intersected + EXPECT_EQ(newLimits.maxWidth_, 2500); // Width not intersected + EXPECT_EQ(newLimits.minHeight_, 300); // Height intersected: max(200, 300) + EXPECT_EQ(newLimits.maxHeight_, 1000); // Height intersected: min(1500, 1000) +} + +/** + * @tc.name: UpdateWindowSizeLimits01 + * @tc.desc: Test UpdateWindowSizeLimits with needNotifySession=false + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, UpdateWindowSizeLimits01, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("UpdateWindowSizeLimits01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + WindowLimits limits = { 2000, 1200, 300, 400, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetWindowLimits(limits); + + // Call without notification + window->UpdateWindowSizeLimits(false); + + // Verify limits were updated + auto updatedLimits = window->property_->GetWindowLimits(); + EXPECT_EQ(updatedLimits.minWidth_, 300); +} + +/** + * @tc.name: UpdateWindowSizeLimits02 + * @tc.desc: Test UpdateWindowSizeLimits with needNotifySession=true and attached windows + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, UpdateWindowSizeLimits02, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("UpdateWindowSizeLimits02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + SessionInfo sessionInfo = { "UpdateWindowSizeLimits02", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + window->windowSystemConfig_.freeMultiWindowEnable_ = true; + window->windowSystemConfig_.freeMultiWindowSupport_ = true; + + // Set up attached window scenario + WindowLimits attachedLimits = { 1800, 1000, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, attachedLimits); + AttachLimitOptions limitOptions{ true, true }; // isIntersectedHeightLimit, isIntersectedWidthLimit + window->property_->SetAttachedLimitOptions(1, limitOptions); + + // Test the intersection logic directly (UpdateWindowSizeLimits requires display mock) + WindowLimits newLimits = { 2500, 1500, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits newLimitsVP = { 1250, 750, 100, 150, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + float virtualPixelRatio = 2.0f; + + window->CalculateAttachedWindowLimitsIntersection(newLimits, newLimitsVP, virtualPixelRatio); + + // Verify limits were intersected with attached window + EXPECT_EQ(newLimits.minWidth_, 250); // max(200, 250) +} + +/** + * @tc.name: NotifySessionSideLimitsChanged01 + * @tc.desc: Test notification for sub window without attach relationship + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged01, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + SessionInfo sessionInfo = { "NotifySessionSideLimitsChanged01", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limitsToNotify = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // No attach relationship set for sub window + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = false; + window->property_->SetWindowAnchorInfo(anchorInfo); + + // Should not call NotifyAttachedWindowsLimitsChanged since no attach relationship + EXPECT_CALL(*session, NotifyAttachedWindowsLimitsChanged(testing::_)).Times(0); + window->NotifySessionSideLimitsChanged(limitsToNotify); +} + +/** + * @tc.name: NotifySessionSideLimitsChanged02 + * @tc.desc: Test notification for sub window with attach relationship + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged02, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + SessionInfo sessionInfo = { "NotifySessionSideLimitsChanged02", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limitsToNotify = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Set up attach relationship for sub window + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = true; + anchorInfo.attachOptions.isIntersectedWidthLimit = true; + window->property_->SetWindowAnchorInfo(anchorInfo); + + // User limits in PX + WindowLimits userLimits = { 2200, 1200, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetUserWindowLimits(userLimits); + + // Should notify with PX limits + EXPECT_CALL(*session, NotifyAttachedWindowsLimitsChanged(testing::_)) + .Times(1) + .WillOnce(testing::Return(WSError::WS_OK)); + window->NotifySessionSideLimitsChanged(limitsToNotify); +} + +/** + * @tc.name: NotifySessionSideLimitsChanged03 + * @tc.desc: Test notification for sub window with VP unit user limits + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged03, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged03"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + SessionInfo sessionInfo = { "NotifySessionSideLimitsChanged03", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limitsToNotify = { 1000, 500, 100, 150, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + + // Set up attach relationship for sub window + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = true; + anchorInfo.attachOptions.isIntersectedWidthLimit = true; + window->property_->SetWindowAnchorInfo(anchorInfo); + + // User limits in VP + WindowLimits userLimits = { 1000, 500, 100, 150, 0.0f, 0.0f, 0.0f, PixelUnit::VP }; + window->property_->SetUserWindowLimits(userLimits); + + // Should notify with VP limits + EXPECT_CALL(*session, NotifyAttachedWindowsLimitsChanged(testing::_)) + .Times(1) + .WillOnce(testing::Return(WSError::WS_OK)); + window->NotifySessionSideLimitsChanged(limitsToNotify); +} + +/** + * @tc.name: NotifySessionSideLimitsChanged04 + * @tc.desc: Test notification for sub window without intersected limits + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged04, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged04"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_SUB_WINDOW); + + SessionInfo sessionInfo = { "NotifySessionSideLimitsChanged04", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limitsToNotify = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Attach relationship but no intersected limits for sub window + WindowAnchorInfo anchorInfo; + anchorInfo.isAnchoredByAttach_ = true; + anchorInfo.attachOptions.isIntersectedHeightLimit = false; + anchorInfo.attachOptions.isIntersectedWidthLimit = false; + window->property_->SetWindowAnchorInfo(anchorInfo); + + // Should return early without notification + EXPECT_CALL(*session, NotifyAttachedWindowsLimitsChanged(testing::_)).Times(0); + window->NotifySessionSideLimitsChanged(limitsToNotify); +} + +/** + * @tc.name: NotifySessionSideLimitsChanged05 + * @tc.desc: Test notification for main window with attached sub windows + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged05, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged05"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "NotifySessionSideLimitsChanged05", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limitsToNotify = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Add sub window with limit options + WindowLimits subLimits = { 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(100, subLimits); + AttachLimitOptions limitOptions{ true, true }; // sub window attaches with both height and width limits + window->property_->SetAttachedLimitOptions(100, limitOptions); + + // Should notify session side + EXPECT_CALL(*session, NotifyAttachedWindowsLimitsChanged(testing::_)) + .Times(1) + .WillOnce(testing::Return(WSError::WS_OK)); + window->NotifySessionSideLimitsChanged(limitsToNotify); +} + +/** + * @tc.name: NotifySessionSideLimitsChanged06 + * @tc.desc: Test notification for main window without attached sub windows + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged06, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged06"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + SessionInfo sessionInfo = { "NotifySessionSideLimitsChanged06", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + WindowLimits limitsToNotify = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // No attached sub windows (attachedLimitOptionsList_ is empty) + // Should not notify session side + EXPECT_CALL(*session, NotifyAttachedWindowsLimitsChanged(testing::_)).Times(0); + window->NotifySessionSideLimitsChanged(limitsToNotify); +} + +/** + * @tc.name: NotifySessionSideLimitsChanged07 + * @tc.desc: Test notification when hostSession is null + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, NotifySessionSideLimitsChanged07, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("NotifySessionSideLimitsChanged07"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Do NOT set hostSession_ - leave it as nullptr + + WindowLimits limitsToNotify = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + // Add sub window with limit options + WindowLimits subLimits = { 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(100, subLimits); + AttachLimitOptions limitOptions{ true, true }; + window->property_->SetAttachedLimitOptions(100, limitOptions); + + // Should return early without crashing when hostSession_ is null + window->NotifySessionSideLimitsChanged(limitsToNotify); + + // Verify early return when hostSession is null + EXPECT_EQ(window->GetHostSession(), nullptr); +} + +/** + * @tc.name: AttachedWindowLimitsPriority01 + * @tc.desc: Test priority order with vector insertion order + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, AttachedWindowLimitsPriority01, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("AttachedWindowLimitsPriority01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Add limits in specific order + WindowLimits limits1 = { 2400, 1200, 300, 400, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits3 = { 2200, 1100, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + + window->property_->SetAttachedWindowLimits(1, limits1); + window->property_->SetAttachedWindowLimits(2, limits2); + window->property_->SetAttachedWindowLimits(3, limits3); + + auto attachedList = window->property_->GetAttachedWindowLimitsList(); + + // Verify insertion order is preserved + EXPECT_EQ(attachedList.size(), 3u); + EXPECT_EQ(attachedList[0].first, 1); + EXPECT_EQ(attachedList[1].first, 2); + EXPECT_EQ(attachedList[2].first, 3); +} + +/** + * @tc.name: AttachedWindowLimitsPriority02 + * @tc.desc: Test priority with existing sourceId update + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, AttachedWindowLimitsPriority02, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("AttachedWindowLimitsPriority02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + + // Add limits + WindowLimits limits1 = { 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + WindowLimits limits2 = { 2200, 1100, 250, 350, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1); + window->property_->SetAttachedWindowLimits(2, limits2); + + // Update existing sourceId 1 + WindowLimits limits1Updated = { 2100, 1050, 220, 330, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->property_->SetAttachedWindowLimits(1, limits1Updated); + + auto attachedList = window->property_->GetAttachedWindowLimitsList(); + + // Should still be 2 entries, with sourceId 1 at the beginning + EXPECT_EQ(attachedList.size(), 2u); + EXPECT_EQ(attachedList[0].first, 1); + EXPECT_EQ(attachedList[0].second.minWidth_, 220); // Updated value } /** @@ -1000,6 +2109,157 @@ HWTEST_F(WindowSceneSessionImplLayoutTest, GetGlobalScaledRect, TestSize.Level1) EXPECT_EQ(res, WMError::WM_OK); EXPECT_NE(globalScaledRect.width_, 800); } +/** + * @tc.name: SyncAllAttachedLimitsToChild01 + * @tc.desc: Test SyncAllAttachedLimitsToChild stores all limits and options + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, SyncAllAttachedLimitsToChild01, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("SyncAllAttachedLimitsToChild01"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + SessionInfo sessionInfo = { "SyncAllAttachedLimitsToChild01", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + std::vector> limitsList; + std::vector> optionsList; + + // Parent's limits (sourceId=100) + limitsList.emplace_back(100, WindowLimits{ 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(100, AttachLimitOptions{ true, true }); + + // Sub-window's limits (sourceId=200) + limitsList.emplace_back(200, WindowLimits{ 1500, 800, 150, 250, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(200, AttachLimitOptions{ true, false }); + + WSError res = window->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(res, WSError::WS_OK); + + // Verify limits were stored + auto storedLimits = window->property_->GetAttachedWindowLimitsList(); + EXPECT_EQ(storedLimits.size(), 2u); + + // Verify options were stored + auto storedOptions = window->property_->GetAttachedLimitOptionsList(); + EXPECT_EQ(storedOptions.size(), 2u); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild02 + * @tc.desc: Test SyncAllAttachedLimitsToChild clears existing limits first + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, SyncAllAttachedLimitsToChild02, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("SyncAllAttachedLimitsToChild02"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + SessionInfo sessionInfo = { "SyncAllAttachedLimitsToChild02", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // Pre-populate with existing limits + WindowLimits oldLimits = { 3000, 2000, 500, 600, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(999, oldLimits, true, true); + EXPECT_EQ(window->property_->GetAttachedWindowLimitsList().size(), 1u); + + // Sync new limits - should clear old and store new + std::vector> limitsList; + std::vector> optionsList; + limitsList.emplace_back(100, WindowLimits{ 2000, 1000, 200, 300, 0.0f, 0.0f, 0.0f, PixelUnit::PX }); + optionsList.emplace_back(100, AttachLimitOptions{ true, true }); + + WSError res = window->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(res, WSError::WS_OK); + + // Verify old limits were replaced, not accumulated + auto storedLimits = window->property_->GetAttachedWindowLimitsList(); + EXPECT_EQ(storedLimits.size(), 1u); + + // Verify old sourceId 999 is gone + bool foundOld = false; + for (const auto& [id, limits] : storedLimits) { + if (id == 999) foundOld = true; + } + EXPECT_FALSE(foundOld); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild03 + * @tc.desc: Test SyncAllAttachedLimitsToChild with empty lists clears all + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, SyncAllAttachedLimitsToChild03, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("SyncAllAttachedLimitsToChild03"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + SessionInfo sessionInfo = { "SyncAllAttachedLimitsToChild03", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + // Pre-populate with existing limits + WindowLimits oldLimits = { 3000, 2000, 500, 600, 0.0f, 0.0f, 0.0f, PixelUnit::PX }; + window->UpdateAttachedWindowLimits(888, oldLimits, true, true); + EXPECT_EQ(window->property_->GetAttachedWindowLimitsList().size(), 1u); + + // Sync with empty lists - should clear everything + std::vector> limitsList; + std::vector> optionsList; + + WSError res = window->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(res, WSError::WS_OK); + + EXPECT_EQ(window->property_->GetAttachedWindowLimitsList().size(), 0u); + EXPECT_EQ(window->property_->GetAttachedLimitOptionsList().size(), 0u); +} + +/** + * @tc.name: SyncAllAttachedLimitsToChild04 + * @tc.desc: Test SyncAllAttachedLimitsToChild with VP unit limits + * @tc.type: FUNC + */ +HWTEST_F(WindowSceneSessionImplLayoutTest, SyncAllAttachedLimitsToChild04, Function | SmallTest | Level2) +{ + sptr option = sptr::MakeSptr(); + option->SetWindowName("SyncAllAttachedLimitsToChild04"); + sptr window = sptr::MakeSptr(option); + window->property_->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + ASSERT_NE(window, nullptr); + + SessionInfo sessionInfo = { "SyncAllAttachedLimitsToChild04", "Module", "Ability" }; + sptr session = sptr::MakeSptr(sessionInfo); + window->hostSession_ = session; + + std::vector> limitsList; + std::vector> optionsList; + + limitsList.emplace_back(300, WindowLimits{ 800, 500, 50, 100, 0.0f, 0.0f, 0.0f, PixelUnit::VP }); + optionsList.emplace_back(300, AttachLimitOptions{ false, true }); + + WSError res = window->SyncAllAttachedLimitsToChild(limitsList, optionsList); + EXPECT_EQ(res, WSError::WS_OK); + + auto storedLimits = window->property_->GetAttachedWindowLimitsList(); + EXPECT_EQ(storedLimits.size(), 1u); + for (const auto& [id, limits] : storedLimits) { + if (id == 300) { + EXPECT_EQ(limits.pixelUnit_, PixelUnit::VP); + } + } +} + } // namespace } // namespace Rosen } // namespace OHOS diff --git a/wm/test/unittest/layout/window_session_impl_layout_test.cpp b/wm/test/unittest/layout/window_session_impl_layout_test.cpp index 87fe2f5439..a323f9c67c 100644 --- a/wm/test/unittest/layout/window_session_impl_layout_test.cpp +++ b/wm/test/unittest/layout/window_session_impl_layout_test.cpp @@ -573,7 +573,7 @@ HWTEST_F(WindowSessionImplLayoutTest, HookWindowSizeByDrawableRectHook, TestSize hookWindowInfo.drawableRectHook = false; window->SetAppHookWindowInfo(hookWindowInfo); Rect drawableRect = { 0, 0, defaultSize, defaultSize }; - if (window->GetAppHookWindowInfo().drawableRectHook) { + if (window->GetProperty()->GetHookWindowInfo().drawableRectHook) { window->HookWindowSizeByHookWindowInfo(drawableRect); } EXPECT_EQ(drawableRect.width_, defaultSize); @@ -582,7 +582,7 @@ HWTEST_F(WindowSessionImplLayoutTest, HookWindowSizeByDrawableRectHook, TestSize hookWindowInfo.drawableRectHook = true; window->SetAppHookWindowInfo(hookWindowInfo); drawableRect = { 0, 0, defaultSize, defaultSize }; - if (window->GetAppHookWindowInfo().drawableRectHook) { + if (window->GetProperty()->GetHookWindowInfo().drawableRectHook) { window->HookWindowSizeByHookWindowInfo(drawableRect); } EXPECT_NE(drawableRect.width_, defaultSize); diff --git a/wm/test/unittest/window_scene_session_impl_test5.cpp b/wm/test/unittest/window_scene_session_impl_test5.cpp index 50cdbe936e..25135c704a 100644 --- a/wm/test/unittest/window_scene_session_impl_test5.cpp +++ b/wm/test/unittest/window_scene_session_impl_test5.cpp @@ -2265,8 +2265,6 @@ HWTEST_F(WindowSceneSessionImplTest5, GetAppForceLandscapeConfig01, TestSize.Lev auto res = window->GetAppForceLandscapeConfig(config); if (SceneBoardJudgement::IsSceneBoardEnabled()) { ASSERT_EQ(res, WMError::WM_OK); - EXPECT_EQ(config.mode_, 0); - EXPECT_EQ(config.supportSplit_, -1); } } @@ -2286,8 +2284,6 @@ HWTEST_F(WindowSceneSessionImplTest5, GetAppForceLandscapeConfig02, TestSize.Lev auto res = window->GetAppForceLandscapeConfig(config); if (SceneBoardJudgement::IsSceneBoardEnabled()) { ASSERT_EQ(res, WMError::WM_ERROR_INVALID_WINDOW); - EXPECT_EQ(config.mode_, 0); - EXPECT_EQ(config.supportSplit_, -1); } } @@ -3243,45 +3239,6 @@ HWTEST_F(WindowSceneSessionImplTest5, SendCombinedCompatibleConfigToArkUI, TestS window->SendCombinedCompatibleConfigToArkUI(); EXPECT_TRUE(WindowSceneSessionImpl::hasSentCombinedCompatibleConfig_); } - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated01 - * @tc.desc: Test NotifyAppForceLandscapeConfigEnableUpdated when window type is not main window - * @tc.type: FUNC - */ -HWTEST_F(WindowSceneSessionImplTest5, NotifyAppForceLandscapeConfigEnableUpdated01, TestSize.Level1) -{ - sptr option = sptr::MakeSptr(); - option->SetWindowName("NotifyAppForceLandscapeConfigEnableUpdated01"); - option->SetWindowType(WindowType::WINDOW_TYPE_FLOAT); - sptr window = sptr::MakeSptr(option); - ASSERT_NE(window, nullptr); - - WSError res = window->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_DO_NOTHING); -} - -/** - * @tc.name: NotifyAppForceLandscapeConfigEnableUpdated02 - * @tc.desc: Test NotifyAppForceLandscapeConfigEnableUpdated when GetAppForceLandscapeConfigEnable fails - * @tc.type: FUNC - */ -HWTEST_F(WindowSceneSessionImplTest5, NotifyAppForceLandscapeConfigEnableUpdated02, TestSize.Level1) -{ - sptr option = sptr::MakeSptr(); - option->SetWindowName("NotifyAppForceLandscapeConfigEnableUpdated02"); - option->SetWindowType(WindowType::APP_MAIN_WINDOW_BASE); - sptr window = sptr::MakeSptr(option); - ASSERT_NE(window, nullptr); - - SessionInfo sessionInfo = {"CreateTestBundle", "CreateTestModule", "CreateTestAbility"}; - sptr session = sptr::MakeSptr(sessionInfo); - window->hostSession_ = session; - - // GetAppForceLandscapeConfigEnable will fail if listener is not registered - WSError res = window->NotifyAppForceLandscapeConfigEnableUpdated(false, SelectMode::WIDE_MODE); - EXPECT_EQ(res, WSError::WS_DO_NOTHING); -} } } // namespace Rosen } // namespace OHOS \ No newline at end of file diff --git a/wm/test/unittest/window_session_impl_test3.cpp b/wm/test/unittest/window_session_impl_test3.cpp index f54a6a3a96..042453fe2f 100755 --- a/wm/test/unittest/window_session_impl_test3.cpp +++ b/wm/test/unittest/window_session_impl_test3.cpp @@ -176,18 +176,7 @@ HWTEST_F(WindowSessionImplTest3, SetForceSplitConfig, TestSize.Level1) window_ = GetTestWindowImpl("SetForceSplitConfig"); ASSERT_NE(window_, nullptr); - int32_t FORCE_SPLIT_MODE = 5; - int32_t NAV_FORCE_SPLIT_MODE = 6; - AppForceLandscapeConfig config = { FORCE_SPLIT_MODE, true, false, {}, {}, {}, false, false, false, false }; - window_->SetForceSplitConfig(config); - - config = { FORCE_SPLIT_MODE, false, false, {}, {}, {}, false, false, false, false }; - window_->SetForceSplitConfig(config); - - config = { NAV_FORCE_SPLIT_MODE, true, false, {}, {}, {}, false, false, false, false }; - window_->SetForceSplitConfig(config); - - config = { NAV_FORCE_SPLIT_MODE, false, false, {}, {}, {}, false, false, false, false }; + AppForceLandscapeConfig config = { {}, {}, {}, false, false, false, false }; window_->SetForceSplitConfig(config); EXPECT_TRUE(logMsg.find("uiContent is null!") != std::string::npos); LOG_SetCallback(nullptr); diff --git a/wm/test/unittest/window_session_impl_test5.cpp b/wm/test/unittest/window_session_impl_test5.cpp index 795f3a91af..ba708bd2e7 100644 --- a/wm/test/unittest/window_session_impl_test5.cpp +++ b/wm/test/unittest/window_session_impl_test5.cpp @@ -1618,6 +1618,84 @@ HWTEST_F(WindowSessionImplTest5, SetUIContentInner, Function | SmallTest | Level LOG_SetCallback(nullptr); } +/** + * @tc.name: SetUIContentInnerGetSelectModeFail + * @tc.desc: Test SetUIContentInner when GetSelectMode fails + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionImplTest5, SetUIContentInnerGetSelectModeFail, Function | SmallTest | Level2) +{ + g_errLog.clear(); + LOG_SetCallback(MyLogCallback); + sptr option = sptr::MakeSptr(); + option->SetWindowName("SetUIContentInnerGetSelectModeFail"); + sptr window = sptr::MakeSptr(option); + + SessionInfo sessionInfo = {"SetUIContentInnerGetSelectModeFail", "SetUIContentInnerGetSelectModeFail", + "SetUIContentInnerGetSelectModeFail"}; + auto hostSession = sptr::MakeSptr(sessionInfo); + sptr property = sptr::MakeSptr(); + property->SetPersistentId(3); + property->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->property_ = property; + window->hostSession_ = hostSession; + window->state_ = WindowState::STATE_SHOWN; + + // Mock GetAppForceLandscapeConfig to return OK with config + AppForceLandscapeConfig config; + config.containsSysConfig_ = true; + EXPECT_CALL(*hostSession, GetAppForceLandscapeConfig(::testing::_)) + .WillOnce(::testing::DoAll(::testing::SetArgReferee<0>(config), ::testing::Return(WMError::WM_OK))); + + // Mock GetSelectMode to fail + EXPECT_CALL(*hostSession, GetSelectMode(::testing::_)) + .WillOnce(::testing::Return(WMError::WM_ERROR_NULLPTR)); + + window->SetUIContentInner("info", nullptr, nullptr, + WindowSetUIContentType::DEFAULT, BackupAndRestoreType::NONE, nullptr); + EXPECT_TRUE(g_errLog.find("get selectMode fail") == std::string::npos); + LOG_SetCallback(nullptr); +} + +/** + * @tc.name: SetUIContentInnerGetSelectModeSuccess + * @tc.desc: Test SetUIContentInner when GetSelectMode succeeds + * @tc.type: FUNC + */ +HWTEST_F(WindowSessionImplTest5, SetUIContentInnerGetSelectModeSuccess, Function | SmallTest | Level2) +{ + g_errLog.clear(); + LOG_SetCallback(MyLogCallback); + sptr option = sptr::MakeSptr(); + option->SetWindowName("SetUIContentInnerGetSelectModeSuccess"); + sptr window = sptr::MakeSptr(option); + + SessionInfo sessionInfo = {"SetUIContentInnerGetSelectModeSuccess", "SetUIContentInnerGetSelectModeSuccess", + "SetUIContentInnerGetSelectModeSuccess"}; + auto hostSession = sptr::MakeSptr(sessionInfo); + sptr property = sptr::MakeSptr(); + property->SetPersistentId(4); + property->SetWindowType(WindowType::WINDOW_TYPE_APP_MAIN_WINDOW); + window->property_ = property; + window->hostSession_ = hostSession; + window->state_ = WindowState::STATE_SHOWN; + + // Mock GetAppForceLandscapeConfig to return OK with config + AppForceLandscapeConfig config; + config.containsSysConfig_ = true; + EXPECT_CALL(*hostSession, GetAppForceLandscapeConfig(::testing::_)) + .WillOnce(::testing::DoAll(::testing::SetArgReferee<0>(config), ::testing::Return(WMError::WM_OK))); + + // Mock GetSelectMode to succeed + EXPECT_CALL(*hostSession, GetSelectMode(::testing::_)).WillOnce( + ::testing::DoAll(::testing::SetArgReferee<0>(SelectMode::WIDE_MODE), ::testing::Return(WMError::WM_OK))); + + window->SetUIContentInner("info", nullptr, nullptr, + WindowSetUIContentType::DEFAULT, BackupAndRestoreType::NONE, nullptr); + EXPECT_TRUE(g_errLog.find("get selectMode success") == std::string::npos); + LOG_SetCallback(nullptr); +} + /** * @tc.name: HideTitleButton01 * @tc.desc: HideTitleButton01 diff --git a/wmserver/BUILD.gn b/wmserver/BUILD.gn index b45f16b391..5a021f8616 100644 --- a/wmserver/BUILD.gn +++ b/wmserver/BUILD.gn @@ -247,6 +247,7 @@ ohos_shared_library("sms") { "input:libmmi-napi", "samgr:samgr_proxy", ] + ldflags = [ "-Wl,-Bsymbolic-functions" ] if (window_manager_use_sceneboard) { deps += [ "${window_base_path}/window_scene/session_manager_service:session_manager_service" ] }