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

TiZizzz
发布于 2023-7-16 16:38
浏览
3收藏

问题描述

问题环境

系统版本: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显示异常(偶现,去掉锁屏应用和锁屏服务后大概率出现)。

异常效果:

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

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

正常效果:

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

OpenHarmony系统解决方案 - 配置屏幕方向导致开机动画和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
  1. 查找对应源码发现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);
    ···
}
  1. 追踪设置旋转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)}`);
    });
}
  1. 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;
}
  1. 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;
}
  1. 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);
}
  1. 查看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;
}
  1. 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


©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2023-7-16 16:41:06修改
6
收藏 3
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

一般遇到这种情况不影响使用就忽视了,还是楼主细致

1
回复
2023-7-17 10:24:11
wzhishun
wzhishun

在系统启动时已成功设置旋转90度(水平),但又被设置为旋转0度(垂直)

这种属于设计失误吗

回复
2023-7-17 13:59:06
TiZizzz
TiZizzz 回复了 wzhishun
在系统启动时已成功设置旋转90度(水平),但又被设置为旋转0度(垂直)这种属于设计失误吗

程序设计出来总会有一些场景没有被考虑到,而出现Bug,尤其是当多个模块协同调用的时候,最容易出现。是否属于设计失误咱们没必要去纠结它,问题可以得到解决,我们能从中学习到一些知识,看出当初设计人的一些设计想法,充实自己,会更有意义。

3
回复
2023-7-17 14:47:40
wx635794cc36a56
wx635794cc36a56

博主您好,我也在开发OpenHarmonyOS开发板,不知是否能添加以下您的联系方式

回复
2023-12-19 08:52:18
回复
    相关推荐