1 关键字
显示设备;HDMI;开机Logo;
2 问题描述
问题环境:
- 系统版本:OpenHarmony-3.2-Release
问题现象:
插入外接显示器,启动系统偶现卡开机Logo。
3 问题原因
3.1 正常机制
系统启动成功,显示开机动画,开机动画结束后进入锁屏界面。
3.2 异常机制
系统卡在开机Logo界面,长时间无法显示开机动画,无法进入系统。
4 解决方案
修改默认屏幕设置条件,解决Window模块加载慢于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_);}
}
5 定位过程
- 落盘异常开机日志,发现Launcher和SystemUI等系统应用在启动过程中报错,无法获取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.
- 查找默认屏幕设置逻辑,追踪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;
}
- 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_);}
}
- 查看源码对应Log,发现异常情况下屏幕OnAbstractScreenConnect函数的执行是一起完成的,此时ScrrenID为0的屏幕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:008-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
- 查看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);}}
}
第二种,当window子系统触发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}" PRIu64", 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);}···
}
- 如果OnAbstractScreenConnect在第二种情况执行,加载第一个屏幕时,则在group->GetChildCount() == 1时进入判断,执行**BindAloneScreenLocked(absScreen);**函数,系统正常运行。
- 如果OnAbstractScreenConnect在第一种情况执行,加载第一个屏幕时,会创建screenGroup。创建的group也会insert进dmsScreenMap_。
// 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));···
}
- 当第二块显示设备连接时,寻找第一块设备创建的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;
}
- 当OnAbstractScreenConnect被执行时,获取到ScreenID为0的Group,此时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_);}
}
6 知识分享
将多个屏幕设置成扩展屏,需要修改系统参数persist.display.expand.enabled为1后重启系统生效。
随着鸿蒙生态的发展,鸿蒙开发已成为时代新风口,学习鸿蒙开发势在必行。
为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05
《鸿蒙 (Harmony OS)开发学习手册》
入门必看:https://qr21.cn/FV7h05
- 应用开发导读(ArkTS)
- ……
HarmonyOS 概念:https://qr21.cn/FV7h05
- 系统定义
- 技术架构
- 技术特性
- 系统安全
如何快速入门?:https://qr21.cn/FV7h05
- 基本概念
- 构建第一个ArkTS应用
- 构建第一个JS应用
- ……
开发基础知识:https://qr21.cn/FV7h05
- 应用基础知识
- 配置文件
- 应用数据管理
- 应用安全管理
- 应用隐私保护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- ……
基于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.……