OpenHarmony系统解决方案 - 配置屏幕方向导致开机动画和Launcher显示异常

问题环境

系统版本:OpenHarmony-3.2-Release

问题现象

  1. 配置设备默认方向,例如修改为横屏显示,修改文件display_manager_config.xmlbuildInDefaultOrientation 参数值为2(Orientation::HORIZONTAL)。

源码中文件位于foundation/window/window_manager/resources/config/rk3568/display_manager_config.xml。

系统中文件位于/etc/window/resources/display_manager_config.xml。

  1. 系统启动后开机动画横竖屏切换,Launcher显示异常(偶现,去掉锁屏应用和锁屏服务后大概率出现)。
异常效果:
正常效果:

问题原因

  • ScreenRotationController 初始化会设置rotationLockedRotation_属性初始值,而ScreenRotationController 初始化的触发点在开机动画窗口销毁时,此时间点在LauncherWindow加载之后。
  • Launcher 加载Window 时会设置SetScreenRotation (屏幕旋转角度),因为Launcher 的方向加载配置为AUTO_ROTATION_RESTRICTED (方向随传感器旋转),所以SetScreenRotation 会根据rotationLockedRotation_属性值设置旋转角度,而此时 rotationLockedRotation_属性并未被设置初始值,所以SetScreenRotation 设置的值取得是默认值0(如果配置为Orientation::HORIZONTAL,则应旋转90度,取值为1),导致问题的产生。

解决方案

调整ScreenRotationController 初始化时序,使ScreenRotationControllerLauncher 加载Window时触发。修改源码文件:foundation/window/window_manager/wmserver/src/window_node_container.cpp

  1. WindowNodeContainer::RemoveWindowNode函数中,移除以下代码:

    if (node->GetWindowType() == WindowType::WINDOW_TYPE_BOOT_ANIMATION) {
    DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();
    }

修改后WindowNodeContainer::RemoveWindowNode函数代码:

复制代码
// foundation/window/window_manager/wmserver/src/window_node_container.cpp
WMError WindowNodeContainer::RemoveWindowNode(sptr<WindowNode>& node, bool fromAnimation)
{
    ···
    NotifyIfAvoidAreaChanged(node, AvoidControlType::AVOID_NODE_REMOVE);
    DumpScreenWindowTree();
    UpdateCameraFloatWindowStatus(node, false);
    if (node->GetWindowType() == WindowType::WINDOW_TYPE_KEYGUARD) {
        isScreenLocked_ = false;
        SetBelowScreenlockVisible(node, true);
    }
    WLOGFD("RemoveWindowNode windowId: %{public}u end", node->GetWindowId());
    RSInterfaces::GetInstance().SetAppWindowNum(GetAppWindowNum());
    return WMError::WM_OK;
}
  1. WindowNodeContainer::AddWindowNode函数中,在WLOGFD("AddWindowNode windowId: %{public}u end", node->GetWindowId());行代码前添加以下代码:

    if (node->GetWindowType() == WindowType::WINDOW_TYPE_DESKTOP) {
    DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();
    }

修改后WindowNodeContainer::AddWindowNode函数代码:

复制代码
WMError WindowNodeContainer::AddWindowNode(sptr<WindowNode>& node, sptr<WindowNode>& parentNode, bool afterAnimation)
{
    ···
    if (node->GetWindowType() == WindowType::WINDOW_TYPE_WALLPAPER) {
        RemoteAnimation::NotifyAnimationUpdateWallpaper(node);
    }
    if (node->GetWindowType() == WindowType::WINDOW_TYPE_DESKTOP) {
        DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();
    }
    WLOGFD("AddWindowNode windowId: %{public}u end", node->GetWindowId());
    RSInterfaces::GetInstance().SetAppWindowNum(GetAppWindowNum());
    return WMError::WM_OK;
}

定位过程

  1. 落盘异常开机日志,查找SetRotation相关日志,发现系统启动过程中横竖屏被设置两次。

    08-05 18:39:55.002 622 811 I C04201/AbstractScreenController: <722>SetRotation: Enter SetRotation, screenId: 0, rotation: 1, isFromWindow: 1
    08-05 18:39:58.487 622 811 I C04201/AbstractScreenController: <722>SetRotation: Enter SetRotation, screenId: 0, rotation: 0, isFromWindow: 1

  2. 查找对应源码发现rotation代表含义。在系统启动时已成功设置旋转90度(水平),但又被设置为旋转0度(垂直),导致异常。

    // foundation/window/window_manager/interfaces/innerkits/dm/dm_common.h
    enum class Rotation : uint32_t {
    ROTATION_0, // 不旋转,垂直
    ROTATION_90, // 旋转90度,水平
    ROTATION_180,
    ROTATION_270,
    };
    // foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
    bool AbstractScreenController::SetRotation(ScreenId screenId, Rotation rotationAfter, bool isFromWindow)
    {
    WLOGFI("Enter SetRotation, screenId: %{public}" PRIu64 ", rotation: %{public}u, isFromWindow: %{public}u",
    screenId, rotationAfter, isFromWindow);
    ···
    }

  3. 追踪设置旋转0度(垂直)操作日志。发现set orientation 时,orientation 被设置为8 ,对应源码含义为AUTO_ROTATION_RESTRICTED

    08-05 18:39:58.487 622 811 D C04201/AbstractScreenController: <627>set orientation. screen 0 orientation 8
    08-05 18:39:58.487 622 811 D C04201/AbstractScreenController: <144>GetAbstractScreen: screenId: 0
    08-05 18:39:58.487 622 811 D C04201/AbstractScreenController: <177>GetDefaultAbstractScreenId: GetDefaultAbstractScreenId, screen:0
    08-05 18:39:58.487 622 811 D C04201/DisplayManagerService: <190>GetDefaultDisplayInfo: GetDefaultDisplayInfo 0
    08-05 18:39:58.487 622 811 D C04201/AbstractScreenController: <177>GetDefaultAbstractScreenId: GetDefaultAbstractScreenId, screen:0
    08-05 18:39:58.487 622 811 D C04201/DisplayManagerService: <190>GetDefaultDisplayInfo: GetDefaultDisplayInfo 0
    08-05 18:39:58.487 622 811 I C04201/AbstractScreenController: <722>SetRotation: Enter SetRotation, screenId: 0, rotation: 0, isFromWindow: 1


复制代码
// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
bool AbstractScreenController::SetOrientation(ScreenId screenId, Orientation newOrientation, bool isFromWindow)
{
    WLOGD("set orientation. screen %{public}" PRIu64" orientation %{public}u", screenId, newOrientation);
    ···
}
// foundation/window/window_manager/interfaces/innerkits/dm/dm_common.h
enum class Orientation : uint32_t {
    BEGIN = 0,
    UNSPECIFIED = BEGIN,
    VERTICAL = 1,
    HORIZONTAL = 2,
    REVERSE_VERTICAL = 3,
    REVERSE_HORIZONTAL = 4,
    SENSOR = 5,
    SENSOR_VERTICAL = 6,
    SENSOR_HORIZONTAL = 7,
    AUTO_ROTATION_RESTRICTED = 8,
    AUTO_ROTATION_PORTRAIT_RESTRICTED = 9,
    AUTO_ROTATION_LANDSCAPE_RESTRICTED = 10,
    LOCKED = 11,
    END = LOCKED,
};
  1. Launcher 在创建window 时会把PreferredOrientation 设置为Window.Orientation.AUTO_ROTATION_RESTRICTED

    // common/src/main/ets/default/manager/WindowManager.ts
    createWindow(context: ServiceExtensionContext, name: string, windowType: number, loadContent: string,
    isShow: boolean, callback?: Function) {
    Window.create(context, name, windowType).then((win) => {
    void win.setPreferredOrientation(Window.Orientation.AUTO_ROTATION_RESTRICTED);
    ···
    }, (error) => {
    Log.showError(TAG, createWindow, create error: ${JSON.stringify(error)});
    });
    }

  2. Launcher 显示窗口时执行SetOrientation,isFromWindow 参数为true

    // foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
    bool AbstractScreenController::SetOrientation(ScreenId screenId, Orientation newOrientation, bool isFromWindow)
    {
    WLOGD("set orientation. screen %{public}" PRIu64" orientation %{public}u", screenId, newOrientation);
    auto screen = GetAbstractScreen(screenId);
    ···
    if (isFromWindow) {
    ScreenRotationController::ProcessOrientationSwitch(newOrientation); // 执行方向选择
    } else {
    Rotation rotationAfter = screen->CalcRotation(newOrientation);
    SetRotation(screenId, rotationAfter, false);
    screen->rotation_ = rotationAfter;
    }
    if (!screen->SetOrientation(newOrientation)) {
    WLOGE("fail to set rotation, screen %{public}" PRIu64"", screenId);
    return false;
    }
    ···
    return true;
    }

  3. orientationAUTO_ROTATION_RESTRICTED ,会执行ProcessSwitchToSensorRelatedOrientation函数。

    // foundation/window/window_manager/dmserver/src/screen_rotation_controller.cpp
    void ScreenRotationController::ProcessOrientationSwitch(Orientation orientation)
    {
    if (!IsSensorRelatedOrientation(orientation)) {
    ProcessSwitchToSensorUnrelatedOrientation(orientation);
    } else {
    ProcessSwitchToSensorRelatedOrientation(orientation, lastSensorRotationConverted_);
    }
    }
    bool ScreenRotationController::IsSensorRelatedOrientation(Orientation orientation)
    {
    if ((orientation >= Orientation::UNSPECIFIED && orientation <= Orientation::REVERSE_HORIZONTAL) ||
    orientation == Orientation::LOCKED) {
    return false;
    }
    // AUTO_ROTATION_RESTRICTED 返回 true
    return true;
    }

  4. rotationLockedRotation_ GetCurrentDisplayRotation()不一致时会切换旋转角度。在此处增加日志打印 rotationLockedRotation_ GetCurrentDisplayRotation()的值,发现在开机触发Launcher 设置屏幕旋转角度时GetCurrentDisplayRotation()函数获取的当前屏幕旋转角度为1 (水平)是正确的。而rotationLockedRotation_0(垂直)。

    // foundation/window/window_manager/dmserver/src/screen_rotation_controller.cpp
    void ScreenRotationController::ProcessSwitchToSensorRelatedOrientation(
    Orientation orientation, DeviceRotation sensorRotationConverted){
    lastOrientationType_ = orientation;
    switch (orientation) {
    case Orientation::AUTO_ROTATION_RESTRICTED: {
    if (isScreenRotationLocked_) {
    SetScreenRotation(rotationLockedRotation_);
    return;
    }
    [[fallthrough]];
    }
    ···
    }
    }
    void ScreenRotationController::SetScreenRotation(Rotation targetRotation){
    if (targetRotation == GetCurrentDisplayRotation()) {
    return;
    }
    DisplayManagerServiceInner::GetInstance().GetDefaultDisplay()->SetRotation(targetRotation);
    DisplayManagerServiceInner::GetInstance().SetRotationFromWindow(defaultDisplayId_, targetRotation);
    WLOGFI("dms: Set screen rotation: %{public}u", targetRotation);
    }

  5. 查看rotationLockedRotation_被设置的场景。分别增加日志,发现开机启动时SetScreenRotationLocked 函数不会被触发,而Init 函数则是在Launcher 启动后被触发,此时Launcher 已经把屏幕旋转角度设置为0 (垂直),rotationLockedRotation_的初始化值则会变成Launcher 设置后的参数0 (垂直)。而在Launcher 触发SetScreenRotation 时,rotationLockedRotation_还未被设置,此时取默认值0(垂直),导致异常的产生。

    // foundation/window/window_manager/dmserver/src/screen_rotation_controller.cpp
    void ScreenRotationController::Init()
    {
    ProcessRotationMapping();
    currentDisplayRotation_ = GetCurrentDisplayRotation();
    lastSensorDecidedRotation_ = currentDisplayRotation_;
    rotationLockedRotation_ = currentDisplayRotation_;
    }
    void ScreenRotationController::SetScreenRotationLocked(bool isLocked)
    {
    if (isLocked) {
    rotationLockedRotation_ = GetCurrentDisplayRotation();
    }
    isScreenRotationLocked_ = isLocked;
    }

  6. ScreenRotationController::Init()的触发时机是在系统检测到启动完成后,关闭开机动画窗口时触发。如果此操作在Launcher 加载Window 之后,则会导致问题。改变ScreenRotationController::Init()的初始化时序,在Launcherwindow加载时初始化可以修复此问题。

    // foundation/window/window_manager/wmserver/src/window_node_container.cpp
    WMError WindowNodeContainer::RemoveWindowNode(sptr& node, bool fromAnimation)
    {
    ···
    if (node->GetWindowType() == WindowType::WINDOW_TYPE_BOOT_ANIMATION) {
    DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();
    }
    ···
    return WMError::WM_OK;
    }

    // foundation/window/window_manager/dmserver/src/display_manager_service.cpp
    void DisplayManagerService::SetGravitySensorSubscriptionEnabled()
    {
    ···
    SensorConnector::SubscribeRotationSensor();
    }

    // foundation/window/window_manager/dmserver/src/sensor_connector.cpp
    void SensorConnector::SubscribeRotationSensor()
    {
    WLOGFI("dms: subscribe rotation-related sensor");
    ScreenRotationController::Init();
    ···
    }

知识分享

如果应用的方向需要随系统切换,可以在module.json5ability 中配置orientationauto_rotation_restricted

为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ......

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ......

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ......

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发

2.UI开发

3.公共事件与通知

4.窗口管理

5.媒体

6.安全

7.网络与链接

8.电话服务

9.数据管理

10.后台任务(Background Task)管理

11.设备管理

12.设备使用信息统计

13.DFX

14.国际化开发

15.折叠屏系列

16.......

相关推荐
TrisighT11 小时前
DevEco Code 写鸿蒙 ArkTS 确实快,但我试了三天后把默认引擎换成了 Cursor
ai编程·harmonyos·cursor
liz7up11 小时前
鸿蒙原生流程图 & 审批流组件 hmflowkit
harmonyos
你好潘先生11 小时前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
orion571 天前
Missing Semester Class1:course overview and introduction of shell
linux
网易云信1 天前
全框架覆盖!网易智企IM鸿蒙生态适配再进一步
人工智能·aigc·harmonyos
用户120487221611 天前
Linux驱动编译与加载
linux·嵌入式
程序员老赵1 天前
服务器文件不想 SFTP 上传?Docker 跑个 File Browser,浏览器就能管理
服务器·docker·开源
vivo互联网技术1 天前
从 10 分钟到 1 秒:ES 深度分页任意跳页的三轮优化实战
服务器·数据库·redis·elasticsearch·深度分页
TrisighT1 天前
我用 AI 逆向了 ArkTS @Builder 的编译产物,看完再也不敢乱写嵌套了
ai编程·harmonyos·arkts