首页 > 其他分享 >OpenHarmony系统解决方案 - 接入多个显示设备卡开机Logo

OpenHarmony系统解决方案 - 接入多个显示设备卡开机Logo

时间:2023-08-01 11:32:51浏览次数:45  
标签:OpenHarmony group AbstractDisplayController 17 05 screen absScreen 开机 Logo

问题描述

问题环境

系统版本:OpenHarmony-3.2-Release

问题现象

接入多个显示设备后,启动系统偶现卡开机Logo。

异常效果:

系统卡在开机Logo界面,长时间无法显示开机动画,并且无法进入系统。

OpenHarmony系统解决方案 - 接入多个显示设备卡开机Logo_系统解决方案

正常效果:

系统启动成功,显示开机动画,开机动画结束后显示锁屏界面。

OpenHarmony系统解决方案 - 接入多个显示设备卡开机Logo_系统解决方案_02

问题原因

  • 在窗口子系统中AbstractDisplayController控制器的初始化与屏幕连接事件的接收存在时序问题,启动成功时窗口子系统收到屏幕连接事件的时机均在AbstractDisplayController控制器的初始化之后,所以可以正常进入系统。
  • 当先接收到屏幕连接事件,再进行AbstractDisplayController控制器的初始化操作时,则会由于OnAbstractScreenConnect函数的处理逻辑导致设置默认屏幕操作失败。

解决方案

修改默认屏幕设置条件,解决AbstractDisplayController控制器加载慢于Display通知屏幕连接事件时,造成的无法设置默认屏幕问题。

修改文件:foundation/window/window_manager/dmserver/src/abstract_display_controller.cpp

AbstractDisplayController::OnAbstractScreenConnect函数中,将以下代码:

if (group->combination_ == ScreenCombination::SCREEN_ALONE || group->GetChildCount() == 1) {

替换为:

ScreenId dmsScreenId = abstractScreenController_->GetDefaultAbstractScreenId();
sptr<AbstractDisplay> display = GetAbstractDisplayByScreen(dmsScreenId);
if (group->combination_ == ScreenCombination::SCREEN_ALONE || group->GetChildCount() == 1 || display == nullptr) {

替换后完整函数代码:

void AbstractDisplayController::OnAbstractScreenConnect(sptr<AbstractScreen> absScreen)
{
    if (absScreen == nullptr) {
        WLOGFE("absScreen is null");
        return;
    }
    WLOGI("connect new screen. id:%{public}" PRIu64"", absScreen->dmsId_);
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    sptr<AbstractScreenGroup> group = absScreen->GetGroup();
    if (group == nullptr) {
        WLOGE("the group information of the screen is wrong");
        return;
    }
    ScreenId dmsScreenId = abstractScreenController_->GetDefaultAbstractScreenId();
    sptr<AbstractDisplay> display = GetAbstractDisplayByScreen(dmsScreenId);
    if (group->combination_ == ScreenCombination::SCREEN_ALONE || group->GetChildCount() == 1 || display == nullptr) {
        BindAloneScreenLocked(absScreen);
    } else if (group->combination_ == ScreenCombination::SCREEN_MIRROR) {
        WLOGI("OnAbstractScreenConnect, ScreenCombination::SCREEN_MIRROR, AddScreenToMirrorLocked");
        AddScreenToMirrorLocked(absScreen);
    } else if (group->combination_ == ScreenCombination::SCREEN_EXPAND) {
        WLOGI("OnAbstractScreenConnect, ScreenCombination::SCREEN_EXPAND, AddScreenToExpandLocked");
        AddScreenToExpandLocked(absScreen);
    } else {
        WLOGE("support in future. combination:%{public}u", group->combination_);
    }
}

定位过程

  1. 落盘异常开机日志,发现LauncherSystemUI等系统应用在启动过程中报错,无法获取DefaultDisplayInfo

08-05 17:18:29.046   521  1088 D C04201/AbstractScreenController: <177>GetDefaultAbstractScreenId: GetDefaultAbstractScreenId, screen:0
08-05 17:18:29.046   521  1088 D C04201/DisplayManagerService: <190>GetDefaultDisplayInfo: GetDefaultDisplayInfo 0
08-05 17:18:29.046   521  1088 E C04201/AbstractDisplayController: <105>GetAbstractDisplayByScreen: fail to get AbstractDisplay 0
08-05 17:18:29.047   521  1088 E C04201/DisplayManagerService: <193>GetDefaultDisplayInfo: fail to get displayInfo by id: invalid display
08-05 17:18:29.047   240  1090 I C01800/SAMGR: SystemAbilityManagerStub::OnReceived, code = 12, callerPid = 1156, flags= 0
08-05 17:18:29.047  1156  1156 W C04201/DisplayManagerProxy: <52>GetDefaultDisplayInfo: DisplayManagerProxy::GetDefaultDisplayInfo SendRequest nullptr.
08-05 17:18:29.047  1156  1156 D C03f00/ArkCompiler: [ecmascript] Throw error: JsDisplayManager::OnGetDefaultDisplay failed.
  1. 查找默认屏幕设置逻辑,追踪Log发现默认屏幕信息从abstractDisplayMap_中获取。

// foundation/window/window_manager/dmserver/src/abstract_display_controller.cpp
sptr<AbstractDisplay> AbstractDisplayController::GetAbstractDisplayByScreen(ScreenId screenId) const
{
    if (screenId == SCREEN_ID_INVALID) {
        WLOGFE("screen id is invalid.");
        return nullptr;
    }
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    for (auto iter : abstractDisplayMap_) {
        sptr<AbstractDisplay> display = iter.second;
        if (display->GetAbstractScreenId() == screenId) {
            return display;
        }
    }
    WLOGFE("fail to get AbstractDisplay %{public}" PRIu64"", screenId);
    return nullptr;
}
  1. abstractDisplayMap_对象会在两个位置被insert数据,BindAloneScreenLocked函数和AddScreenToExpandLocked函数。而两个函数拥有同一个入口函数AbstractDisplayController::OnAbstractScreenConnect

// foundation/window/window_manager/dmserver/src/abstract_display_controller.cpp
void AbstractDisplayController::OnAbstractScreenConnect(sptr<AbstractScreen> absScreen)
{
    if (absScreen == nullptr) {
        WLOGFE("absScreen is null");
        return;
    }
    WLOGI("connect new screen. id:%{public}" PRIu64"", absScreen->dmsId_);
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    sptr<AbstractScreenGroup> group = absScreen->GetGroup();
    if (group == nullptr) {
        WLOGE("the group information of the screen is wrong");
        return;
    }
    if (group->combination_ == ScreenCombination::SCREEN_ALONE || group->GetChildCount() == 1) {
        BindAloneScreenLocked(absScreen);
    } else if (group->combination_ == ScreenCombination::SCREEN_MIRROR) {
        WLOGI("OnAbstractScreenConnect, ScreenCombination::SCREEN_MIRROR, AddScreenToMirrorLocked");
        AddScreenToMirrorLocked(absScreen);
    } else if (group->combination_ == ScreenCombination::SCREEN_EXPAND) {
        WLOGI("OnAbstractScreenConnect, ScreenCombination::SCREEN_EXPAND, AddScreenToExpandLocked");
        AddScreenToExpandLocked(absScreen);
    } else {
        WLOGE("support in future. combination:%{public}u", group->combination_);
    }
}
  1. 查看源码对应Log,发现异常情况下屏幕OnAbstractScreenConnect函数的执行操作是一起完成的,此时ScrrenID0的屏幕ScreenCombination属性为ScreenCombination::SCREEN_MIRROR。而在正常情况下OnAbstractScreenConnect函数是分开执行的。

异常Log:

08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <152>connect new screen. id:0
08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <162>OnAbstractScreenConnect, ScreenCombination::SCREEN_MIRROR, AddScreenToMirrorLocked
08-05 17:18:26.923   521   656 I C04201/AbstractScreenController: <1161>operator(): screenId:2, trigger:[foundation]
08-05 17:18:26.923   332   430 I C01799/MemMgr: MultiAccountManager::Init The manager initial succeed, accountCount = 0.
08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <558>bind display to mirror. screen:0
08-05 17:18:26.923   521   656 I C04200/ClientAgentContainer: <98>GetAgentsByType: no such type of agent registered! type:2
08-05 17:18:26.923   521   923 I C04201/AbstractScreenController: <203>RegisterAbstractScreenCallback: dmsScreenId :1
08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <152>connect new screen. id:1
08-05 17:18:26.923   521   923 E C04201/AbstractScreenController: <159>did not find screen:18446744073709551615
08-05 17:18:26.923   521   923 E C04201/AbstractDisplayController: <156>the group information of the screen is wrong
08-05 17:18:26.923   521   923 I C04201/AbstractScreenController: <203>RegisterAbstractScreenCallback: dmsScreenId :2
08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <152>connect new screen. id:2
08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <162>OnAbstractScreenConnect, ScreenCombination::SCREEN_MIRROR, AddScreenToMirrorLocked
08-05 17:18:26.923   521   923 I C04201/AbstractDisplayController: <558>bind display to mirror. screen:2

正常Log:

08-05 17:23:07.081   547   770 I C04201/AbstractDisplayController: <152>connect new screen. id:0
08-05 17:23:07.081   547   770 D C04201/AbstractScreenController: <177>GetDefaultAbstractScreenId: GetDefaultAbstractScreenId, screen:0

08-05 17:23:07.088   547   770 I C04201/AbstractDisplayController: <152>connect new screen. id:2
08-05 17:23:07.088   547   770 I C04201/AbstractDisplayController: <162>OnAbstractScreenConnect, ScreenCombination::SCREEN_MIRROR, AddScreenToMirrorLocked
08-05 17:23:07.088   547   770 I C04201/AbstractDisplayController: <558>bind display to mirror. screen:2
  1. 查看OnAbstractScreenConnect的触发逻辑有两种。

第一种,AbstractDisplayController初始化时注册屏幕事件回调,注册后遍历dmsScreenMap_调用AbstractDisplayController::OnAbstractScreenConnect初始化在回调注册前记录的屏幕数据。

// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
void AbstractDisplayController::Init(sptr<AbstractScreenController> abstractScreenController)
{
    WLOGFD("display controller init");
    displayCount_ = 0;
    abstractScreenController_ = abstractScreenController;
    abstractScreenCallback_ = new(std::nothrow) AbstractScreenController::AbstractScreenCallback();
    if (abstractScreenCallback_ == nullptr) {
        WLOGFE("abstractScreenCallback init failed");
        return;
    }
    abstractScreenCallback_->onConnect_
        = std::bind(&AbstractDisplayController::OnAbstractScreenConnect, this, std::placeholders::_1);
    abstractScreenCallback_->onDisconnect_
        = std::bind(&AbstractDisplayController::OnAbstractScreenDisconnect, this, std::placeholders::_1);
    abstractScreenCallback_->onChange_
        = std::bind(&AbstractDisplayController::OnAbstractScreenChange, this, std::placeholders::_1,
        std::placeholders::_2);
    abstractScreenController->RegisterAbstractScreenCallback(abstractScreenCallback_);
}

void AbstractScreenController::RegisterAbstractScreenCallback(sptr<AbstractScreenCallback> cb)
{
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    abstractScreenCallback_ = cb;
    for (auto& iter : dmsScreenMap_) {
        if (iter.second != nullptr && abstractScreenCallback_ != nullptr) {
            WLOGFI("dmsScreenId :%{public}" PRIu64"", iter.first);
            abstractScreenCallback_->onConnect_(iter.second);
        }
    }
}

第二种,当窗口子系统触发OnRsScreenConnectionChange回调时,会调用ProcessScreenConnected函数。如果abstractScreenCallback_回调函数注册则执行AbstractDisplayController::OnAbstractScreenConnect

// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
void AbstractScreenController::OnRsScreenConnectionChange(ScreenId rsScreenId, ScreenEvent screenEvent)
{
    WLOGFI("rs screen event. id:%{public}, event:%{public}u", rsScreenId, static_cast<uint32_t>(screenEvent));
    if (screenEvent == ScreenEvent::CONNECTED) {
        auto task = [this, rsScreenId] {
            ProcessScreenConnected(rsScreenId);
        };
        controllerHandler_->PostTask(task, AppExecFwk::EventQueue::Priority::HIGH);
    }
    ···
}

void AbstractScreenController::ProcessScreenConnected(ScreenId rsScreenId)
{
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    if (screenIdManager_.HasRsScreenId(rsScreenId)) {
        WLOGE("reconnect screen, screenId=%{public}" PRIu64"", rsScreenId);
        return;
    }
    WLOGFD("connect new screen");
    auto absScreen = InitAndGetScreen(rsScreenId);
    if (absScreen == nullptr) {
        return;
    }
    sptr<AbstractScreenGroup> screenGroup = AddToGroupLocked(absScreen);
    if (screenGroup == nullptr) {
        return;
    }
    ···
    if (abstractScreenCallback_ != nullptr) {
        abstractScreenCallback_->onConnect_(absScreen);
    }
    ···
}
  1. 如果OnAbstractScreenConnect在第二种情况执行,加载第一个屏幕时,则在group->GetChildCount() == 1时进入判断,执行BindAloneScreenLocked(absScreen);函数,系统正常运行。
  2. 如果OnAbstractScreenConnect在第一种情况执行,加载第一个屏幕时,会创建screenGroup。创建的group也会insertdmsScreenMap_,此操作会导致异常LogScreenId1的屏幕绑定异常。

// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
sptr<AbstractScreenGroup> AbstractScreenController::AddToGroupLocked(sptr<AbstractScreen> newScreen)
{
    sptr<AbstractScreenGroup> res;
    if (dmsScreenGroupMap_.empty()) {
        WLOGI("connect the first screen");
        // 第一块显示设备连接时进入
        res = AddAsFirstScreenLocked(newScreen); 
    } else {
        // 后续显示设备连接时进入
        res = AddAsSuccedentScreenLocked(newScreen);
    }
    return res;
}
sptr<AbstractScreenGroup> AbstractScreenController::AddAsFirstScreenLocked(sptr<AbstractScreen> newScreen)
{
    ScreenId dmsGroupScreenId = screenIdManager_.CreateAndGetNewScreenId(SCREEN_ID_INVALID);
    ···
    dmsScreenGroupMap_.insert(std::make_pair(dmsGroupScreenId, screenGroup));
    dmsScreenMap_.insert(std::make_pair(dmsGroupScreenId, screenGroup));
    ···
}
  1. 当第二块显示设备连接时,寻找第一块设备创建的group,并把自己添加进group中。

// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
sptr<AbstractScreenGroup> AbstractScreenController::AddAsSuccedentScreenLocked(sptr<AbstractScreen> newScreen)
{
    ScreenId defaultScreenId = GetDefaultAbstractScreenId();
    auto iter = dmsScreenMap_.find(defaultScreenId);
    if (iter == dmsScreenMap_.end()) {
        WLOGE("AddAsSuccedentScreenLocked. defaultScreenId:%{public}" PRIu64" is not in dmsScreenMap_.",
            defaultScreenId);
        return nullptr;
    }
    auto screen = iter->second;
    auto screenGroupIter = dmsScreenGroupMap_.find(screen->groupDmsId_);
    if (screenGroupIter == dmsScreenGroupMap_.end()) {
        WLOGE("AddAsSuccedentScreenLocked. groupDmsId:%{public}" PRIu64" is not in dmsScreenGroupMap_.",
            screen->groupDmsId_);
        return nullptr;
    }
    auto screenGroup = screenGroupIter->second;
    Point point;
    if (screenGroup->combination_ == ScreenCombination::SCREEN_EXPAND) {
        point = {screen->GetActiveScreenMode()->width_, 0};
    }
    screenGroup->AddChild(newScreen, point);
    return screenGroup;
}
  1. OnAbstractScreenConnect被执行时,获取到ScreenID0Group,此时Group内的屏幕数为2,所以无法进入BindAloneScreenLocked函数所在的判断,造成异常。

// foundation/window/window_manager/dmserver/src/abstract_display_controller.cpp
void AbstractDisplayController::OnAbstractScreenConnect(sptr<AbstractScreen> absScreen)
{
    if (absScreen == nullptr) {
        WLOGFE("absScreen is null");
        return;
    }
    WLOGI("connect new screen. id:%{public}" PRIu64"", absScreen->dmsId_);
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    sptr<AbstractScreenGroup> group = absScreen->GetGroup();
    if (group == nullptr) {
        WLOGE("the group information of the screen is wrong");
        return;
    }
    if (group->combination_ == ScreenCombination::SCREEN_ALONE || group->GetChildCount() == 1) {
        BindAloneScreenLocked(absScreen);
    } else if (group->combination_ == ScreenCombination::SCREEN_MIRROR) {
        WLOGI("OnAbstractScreenConnect, ScreenCombination::SCREEN_MIRROR, AddScreenToMirrorLocked");
        AddScreenToMirrorLocked(absScreen);
    } else if (group->combination_ == ScreenCombination::SCREEN_EXPAND) {
        WLOGI("OnAbstractScreenConnect, ScreenCombination::SCREEN_EXPAND, AddScreenToExpandLocked");
        AddScreenToExpandLocked(absScreen);
    } else {
        WLOGE("support in future. combination:%{public}u", group->combination_);
    }
}

本文作者:TiZizzz

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz

标签:OpenHarmony,group,AbstractDisplayController,17,05,screen,absScreen,开机,Logo
From: https://blog.51cto.com/harmonyos/6918897

相关文章

  • [ESP] ESP32开机自动配网运行iperf
    esp-idf版本tag-V4.4.2示例路径examples/wifi/iperfiperf_example_main.c(修改后的版本)/*Wi-FiiperfExampleThisexamplecodeisinthePublicDomain(orCC0licensed,atyouroption.)Unlessrequiredbyapplicablelaworagreedtoinwritin......
  • OpenHarmony/HarmonyOS路由跳转并传值
    OpenHarmony/HarmonyOS路由跳转并传值作者:坚果团队:坚果派公众号:“大前端之旅”润开鸿技术专家,华为HDE,InfoQ签约作者,OpenHarmony布道师,擅长HarmonyOS应用开发、熟悉服务卡片开发,在“战码先锋”活动中作为大队长,累计培养三个小队长,带领100+队员完成Pr的提交合入。欢迎通过主页或......
  • ArkTS语言OpenHarmony/HarmonyOS项目代码规范
    ArkTS语言OpenHarmony/HarmonyOS项目代码规范作者:坚果团队:坚果派公众号:“大前端之旅”润开鸿技术专家,华为HDE,InfoQ签约作者,OpenHarmony布道师,擅长HarmonyOS应用开发、熟悉服务卡片开发,在“战码先锋”活动中作为大队长,累计培养三个小队长,带领100+队员完成Pr的提交合入。欢迎通过主......
  • Linux安装配置nginx, 并且开机自启
    1.安装使用菜鸟教程的安装步骤这里的版本可以更换为自己想安装的即可注意:菜鸟教程的安装之后,nginx的安装目录是/usr/local/webserver/nginx,下面的开机自启也是按照这个目录2.开机自启2.1配置/etc/rc.local文件找到/etc/rc.local文件,在最后添加nginx安装目录/usr......
  • Linux学习(3)Redis开机自启动
     1.指定配置启动前台启动redis服务会阻塞整个会话窗口,如果需要通过后台方式启动redis服务,那么必须通过修改redis配置文件的方式来解决。redis配置文件即redis.conf,是存放在redis安装目录下面的。因此,首先需要切换到redis安装目录下:cd/usr/local/src/redis-6.2.6......
  • Win11下MySQL开机自启失效排除思路及解决
    问题描述MySQL服务设置了自启仍然无法启动错误排查上次关机前数据库仍可用,无不良操作,初步排除数据库自身问题设置了开机自启,所以开机时系统调用了开机启动,但失败了,查看windows系统日志win+r输入eventvwr.msc回车根据开机时间定位到错误,可知是ntdll.dll模块的问题尝试用......
  • Linux下实现程序开机自启(rc.local 和 systemctl)
    需求:实现了一个程序,需要在ubuntu上跑起来。能开机自启,且崩溃了也能自己重启。有两种实现方式,个人推荐第二种。方式1:实现方式:直接将要启动程序的运行命令加在/etc/rc.local中,开机时会运行这里的命令。我使用时:写了一个脚本DDR_Guard.sh,在脚本里面循环判断程序有没有开启。如......
  • 自定义树莓派开机启动画面-plymouth版本
    apt-get install-yplymouth-themes#查看当前已安装的主题$plymouth-set-default-theme--listfutureprototype #默认主题,貌似和details一样details #纯文本启动日志输出joy #Debian的背景图+四个小白点循环点亮动画lines #Debian的背景图+线条绘制循环动画moon......
  • Docker系列---【docker和docker容器设置开机启动】
    docker和docker容器设置开机启动1、设置docker开机启动systemctlenabledocker2、设置容器自动重启1)创建容器时设置dockerrun-d--restart=always--name设置容器名使用的镜像(上面命令--name后面两个参数根据实际情况自行修改)#Docker容器的重启策略如下:--res......
  • WPF 开机启动
    前言:  熬了几夜学到点东西,就以为翻越了一座山,艹,前面还有无数个日日夜夜。修改注册表  Registry.CurrentUser.OpenSubKey(“SOFTWARE\Microsoft\Windows\CurrentVersion\Run”,true),给当前用户设置开机启动    设置此项目,程序不需要使用管理员权限启动。  Registr......