OpenHarmony系統解決方案 - 接入多個顯示設備卡開機Logo
問題描述
問題環境
系統版本:OpenHarmony-3.2-Release。
問題現象
接入多個顯示設備后,啟動系統偶現卡開機Logo。
異常效果:
系統卡在開機Logo界面,長時間無法顯示開機動畫,并且無法進入系統。
OpenHarmony系統解決方案 - 接入多個顯示設備卡開機Logo-開源基礎軟件社區
正常效果:
系統啟動成功,顯示開機動畫,開機動畫結束后顯示鎖屏界面。
OpenHarmony系統解決方案 - 接入多個顯示設備卡開機Logo-開源基礎軟件社區
問題原因
- 在窗口子系統中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_);
}
}
定位過程
落盤異常開機日志,發現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: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
查看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);
}
···
}
如果OnAbstractScreenConnect在第二種情況執行,加載第一個屏幕時,則在group->GetChildCount() == 1時進入判斷,執行BindAloneScreenLocked(absScreen);函數,系統正常運行。
如果OnAbstractScreenConnect在第一種情況執行,加載第一個屏幕時,會創建screenGroup。創建的group也會insert進dmsScreenMap_,此操作會導致異常Log中ScreenId為1的屏幕綁定異常。
// 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_);
}
}