OpenHarmony系統解決方案 - 配置屏幕方向導致開機動畫和Launcher顯示異常
問題描述
問題環境
系統版本:OpenHarmony-3.2-Release
問題現象
配置設備默認方向,例如修改為橫屏顯示,修改文件display_manager_config.xml的buildInDefaultOrientation參數值為2(Orientation::HORIZONTAL)。
源碼中文件位于foundation/window/window_manager/resources/config/rk3568/display_manager_config.xml。
系統中文件位于/etc/window/resources/display_manager_config.xml。
系統啟動后開機動畫橫豎屏切換,Launcher顯示異常(偶現,去掉鎖屏應用和鎖屏服務后大概率出現)。
異常效果:
OpenHarmony系統解決方案 - 配置屏幕方向導致開機動畫和Launcher顯示異常-開源基礎軟件社區
OpenHarmony系統解決方案 - 配置屏幕方向導致開機動畫和Launcher顯示異常-開源基礎軟件社區
正常效果:
OpenHarmony系統解決方案 - 配置屏幕方向導致開機動畫和Launcher顯示異常-開源基礎軟件社區
OpenHarmony系統解決方案 - 配置屏幕方向導致開機動畫和Launcher顯示異常-開源基礎軟件社區
問題原因
- ScreenRotationController初始化會設置rotationLockedRotation_屬性初始值,而ScreenRotationController初始化的觸發點在開機動畫窗口銷毀時,此時間點在Launcher的Window加載之后。
- Launcher加載Window時會設置SetScreenRotation(屏幕旋轉角度),因為Launcher的方向加載配置為AUTO_ROTATION_RESTRICTED(方向隨傳感器旋轉),所以SetScreenRotation會根據rotationLockedRotation_屬性值設置旋轉角度,而此時rotationLockedRotation_屬性并未被設置初始值,所以SetScreenRotation設置的值取得是默認值0(如果配置為Orientation::HORIZONTAL,則應旋轉90度,取值為1),導致問題的產生。
解決方案
調整ScreenRotationController初始化時序,使ScreenRotationController在Launcher加載Window時觸發。修改源碼文件:foundation/window/window_manager/wmserver/src/window_node_container.cpp
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;
}
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;
}
定位過程
落盤異常開機日志,查找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
查找對應源碼發現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);
···
}
追蹤設置旋轉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,
};
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)}`);
});
}
當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;
}
因orientation為AUTO_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;
}
當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);
}
查看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;
}
ScreenRotationController::Init()的觸發時機是在系統檢測到啟動完成后,關閉開機動畫窗口時觸發。如果此操作在Launcher加載Window之后,則會導致問題。改變ScreenRotationController::Init()的初始化時序,在Launcher的window加載時初始化可以修復此問題。
// 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.json5的ability中配置orientation為auto_rotation_restricted。