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<WindowNode>& 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.......

相关推荐
勤奋的凯尔森同学1 小时前
webmin配置终端显示样式,模仿UbuntuDesktop终端
linux·运维·服务器·ubuntu·webmin
丁卯4042 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
人间打气筒(Ada)3 小时前
MySQL主从架构
服务器·数据库·mysql
落笔画忧愁e4 小时前
FastGPT快速将消息发送至飞书
服务器·数据库·飞书
小冷爱学习!4 小时前
华为动态路由-OSPF-完全末梢区域
服务器·网络·华为
2501_904447745 小时前
华为发力中端,上半年nova14下半年nova15,大力普及原生鸿蒙
华为·智能手机·django·scikit-learn·pygame
打不了嗝 ᥬ᭄5 小时前
Linux的权限
linux
落幕5 小时前
C语言-进程
linux·运维·服务器
深度Linux5 小时前
C++程序员内功修炼——Linux C/C++编程技术汇总
linux·项目实战·c/c++