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

相关推荐
了一li1 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
日记跟新中1 小时前
Ubuntu20.04 修改root密码
linux·运维·服务器
唐小旭1 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python
码农君莫笑1 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
明 庭1 小时前
Ubuntu下通过Docker部署NGINX服务器
服务器·ubuntu·docker
BUG 4041 小时前
Linux——Shell
linux·运维·服务器
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
yang_shengy2 小时前
【JavaEE】网络(6)
服务器·网络·http·https
大霞上仙2 小时前
Linux 多命令执行
linux·运维·服务器