数据库存储格式:
keyword:user_set_resolution_mode
value: ["F0468A2B3BAA19CE;9 2560 1440 60"]
F0468A2B3BAA19CE是serialnumber,9是modeindex,2560 1400是刷新率,60是分辨率,第二条记录用,隔开
单屏:
1、开机无屏→连接屏幕(数据库无配置),屏幕连接后,设置默认分辨率/刷新率
2、开机无屏→连接屏幕(数据库有配置),从数据库恢复分辨率/刷新率
3、带1个屏幕启动,如果数据库有配置,恢复数据库配置分辨率;如果数据库没配置,设置默认分辨率
4、单屏断开后连接,如果没配置过,设置默认;如果配置过,恢复数据库里的值
双屏:
1、已连接屏幕A,连接屏幕B
1)数据库有B的记录,首先看数据库中的和A当前刷新率是否匹配,如果与A的刷新率相等,再看两者之一是否有4k屏,有就统一60hz(底层带宽不支持),无4k屏就直接恢复;
如果与A的刷新率不等,那么执行共同刷新率算法;
2)如果数据库没有B的记录,执行共同刷新率算法;
2、开机连接两个屏幕启动,从数据库读取配置后
1)主屏数据库有配置,副屏数据库没配置,恢复A屏刷新率/分辨率后,按上面连接流程设置A/B刷新率分辨率;
2)主副屏数据库均有配置,恢复A屏刷新率/分辨率后,按上面连接流程设置A/B刷新率分辨率;
3)主副屏数据库均无配置,设置A屏刷新率/分辨率后,按上面连接流程设置A/B刷新率分辨率;
3、双屏断开副屏或者主屏
剩下那个屏幕的刷新率/分辨率不变
4、如果值有更新,都要写数据库
共同刷新率算法:
1、找A/B共有刷新率,刷新率取60或者以下刷新率最高的交集
2、刷新率确定后,分辨率取最高的,尽量保证已经插入的屏幕分辨率不变


数据库存储格式:
keyword:user_set_multi_screen_orientation
value:["2AEF8DDE0B25DB8C 2","F0468A2B3BAA19CE 1"]
2AEF8DDE0B25DB8C是serialnumber,2是方向,1是VERTICAL,2是HORIZONTAL
旋转方向恢复
单屏:
1、开机无屏→连接屏幕,数据库有配置,恢复数据库配置方向,没有默认横HORIZONTAL
2、带一个屏幕启动,数据库有配置,恢复数据库配置方向,没有默认横HORIZONTAL
3、单屏断开后连接,数据库有配置,恢复数据库配置方向,没有默认横HORIZONTAL
双屏:
1、开机无屏→依次连接主副屏(均有配置),从数据库恢复主屏/副屏方向,没有则默认横屏
2、带两个屏启动,如果数据库有配置,按数据库配置恢复两个屏幕
3、双屏断开其中一个屏幕,不影响另一个屏幕方向


void ScreenSessionManager::RecoverDesktopScreenConfig()
{
if (HasInternalScreen()) {
return;
}
for (auto& iter : screenSessionMap_) {
if (!iter.second || iter.second->GetScreenProperty().GetScreenType() != ScreenType::REAL) {
continue;
}
RecoverScreenActiveMode(iter.second->GetScreenId());
TLOGNFI(WmsLogTag::DMS, "Desktop screen config recovered, screenId = %{public}" PRIu64"",
iter.second->GetScreenId());
}
return;
}


① 获取物理尺寸(毫米) → 转换为英寸
② 获取原始物理分辨率 → 计算原始比例
③ 获取当前分辨率 → 计算当前比例
④ 比例差异调整 → 动态计算有效物理尺寸
⑤ 对角线计算 → sqrt(width² + height²)
⑥ PPI = 像素对角线 / 物理对角线


void ScreenSessionManager::RecoverMultiScreenRelativePositionForDesktop(ScreenId screenId)
{
#ifdef WM_MULTI_SCREEN_ENABLE
TLOGNFI(WmsLogTag::DMS, "RecoverMultiScreenRelativePositionForDesktop enter, rsId=%{public}" PRIu64, screenId);
sptr<ScreenSession> screenSession = GetScreenSessionByRsId(screenId);
if (!screenSession || screenSession->GetScreenProperty().GetScreenType() != ScreenType::REAL ||
!CheckPhysicalScreenReady(screenSession)) {
return;
}
sptr<ScreenSession> defaultScreen = FindOtherPhysicalScreen(screenSession);
if (defaultScreen == nullptr) {
TLOGE(WmsLogTag::DMS, "no other physical screen found, skip recovery");
return;
}
std::map<std::string, MultiScreenModeInfo> desktopInfoMap = ScreenSettingHelper::GetMultiScreenModeInfo();
std::string connectSerialNumber = screenSession->GetSerialNumber();
std::string defaultSerialNumber = defaultScreen->GetSerialNumber();
std::string recoveryKey = (connectSerialNumber < defaultSerialNumber) ?
connectSerialNumber + " " + defaultSerialNumber : defaultSerialNumber + " " + connectSerialNumber;
TLOGNFI(WmsLogTag::DMS, "recoveryKey: %{public}s", recoveryKey.c_str());
if (!CheckMapHasKey(desktopInfoMap, recoveryKey)) {
SetMultiScreenDefaultRelativePosition();
return;
}
MultiScreenModeInfo info = desktopInfoMap[recoveryKey];
if (info.mainScreen.empty()) {
TLOGE(WmsLogTag::DMS, "mainScreen is empty, skip recovery");
return;
}
auto resolveResult = ResolveScreenIds(desktopInfoMap[recoveryKey], defaultScreen, screenSession);
if (resolveResult.mainScreenId == SCREEN_ID_INVALID ||
resolveResult.otherScreenDetail.screenMode == MultiScreenSourceMode::SCREEN_MIRROR ||
resolveResult.otherScreenDetail.screenMode == MultiScreenSourceMode::SCREEN_UNUSED) {
TLOGNFI(WmsLogTag::DMS, "MIRROR or unused mode or invalid main screenId, no need recover relative position");
return;
}
TLOGNFI(WmsLogTag::DMS, "mainScreenId: %{public}" PRIu64 ", secondaryScreenId: %{public}" PRIu64,
resolveResult.mainScreenId, resolveResult.secondaryScreenId);
auto ret = SetMultiScreenRelativePosition(resolveResult.mainScreenDetail.position, resolveResult.otherScreenDetail.position);
if (ret != DMError::DM_OK) {
TLOGNFI(WmsLogTag::DMS, "SetMultiScreenRelativePosition failed, fallback to default position");
SetMultiScreenDefaultRelativePosition();
}
#else
TLOGNFI(WmsLogTag::DMS, "WM_MULTI_SCREEN_ENABLE not defined, skip");
#endif
}
```


/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop01
* @tc.desc: Recover extend mode screen relative position successfully
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop01, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
// Setup: create two physical screens
ScreenId mainScreenId = 0;
ScreenId extendScreenId = 1;
ScreenId mainRsId = 15;
ScreenId extendRsId = 9;
sptr<ScreenSession> mainSession = new ScreenSession(mainScreenId, ScreenProperty(), mainRsId);
sptr<ScreenSession> extendSession = new ScreenSession(extendScreenId, ScreenProperty(), extendRsId);
mainSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
mainSession->GetScreenProperty().SetBounds(RRect{0, 0, 2560, 1440, 0});
mainSession->SetSerialNumber("serialA");
extendSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
extendSession->GetScreenProperty().SetBounds(RRect{2560, 0, 1920, 1080, 0});
extendSession->SetSerialNumber("serialB");
ssm_->screenSessionMap_[mainScreenId] = mainSession;
ssm_->screenSessionMap_[extendScreenId] = extendSession;
// Inject mock config (need to mock ScreenSettingHelper::GetMultiScreenModeInfo)
// Or use test-specific setting provider
// Mock log callback to verify SetMultiScreenRelativePosition is called
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(mainRsId);
// Expect: log contains "mainScreenId" and "secondaryScreenId"
EXPECT_TRUE(g_logMsg.find("mainScreenId") != std::string::npos);
EXPECT_TRUE(g_logMsg.find("secondaryScreenId") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(mainScreenId);
ssm_->screenSessionMap_.erase(extendScreenId);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop02
* @tc.desc: Skip position recovery for mirror mode
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop02, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId otherScreenId = 1;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
sptr<ScreenSession> otherSession = new ScreenSession(otherScreenId, ScreenProperty(), 9);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
screenSession->SetSerialNumber("serialA");
otherSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
otherSession->SetSerialNumber("serialB");
ssm_->screenSessionMap_[screenId] = screenSession;
ssm_->screenSessionMap_[otherScreenId] = otherSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: log contains "MIRROR" or "no need recover relative position"
EXPECT_TRUE(g_logMsg.find("MIRROR") != std::string::npos ||
g_logMsg.find("no need recover") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
ssm_->screenSessionMap_.erase(otherScreenId);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop03
* @tc.desc: Skip position recovery for unused mode
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop03, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
sptr<ScreenSession> otherSession = new ScreenSession(1, ScreenProperty(), 9);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
screenSession->SetSerialNumber("serialA");
otherSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
otherSession->SetSerialNumber("serialB");
ssm_->screenSessionMap_[screenId] = screenSession;
ssm_->screenSessionMap_[1] = otherSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: log contains "unused" mode skip message
EXPECT_TRUE(g_logMsg.find("unused") != std::string::npos ||
g_logMsg.find("no need recover") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
ssm_->screenSessionMap_.erase(1);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop04
* @tc.desc: Fallback to default position when config not found
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop04, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
sptr<ScreenSession> otherSession = new ScreenSession(1, ScreenProperty(), 9);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
screenSession->SetSerialNumber("serialNotExist"); // Serial not in config
otherSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
otherSession->SetSerialNumber("serialNotExist2");
ssm_->screenSessionMap_[screenId] = screenSession;
ssm_->screenSessionMap_[1] = otherSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: SetMultiScreenDefaultRelativePosition was called (verify by log)
EXPECT_TRUE(g_logMsg.find("recoveryKey not found") != std::string::npos ||
g_logMsg.find("default position") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
ssm_->screenSessionMap_.erase(1);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop05
* @tc.desc: Skip recovery when mainScreen is empty in config
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop05, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
sptr<ScreenSession> otherSession = new ScreenSession(1, ScreenProperty(), 9);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
screenSession->SetSerialNumber("serialA");
otherSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
otherSession->SetSerialNumber("serialB");
ssm_->screenSessionMap_[screenId] = screenSession;
ssm_->screenSessionMap_[1] = otherSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: log contains "mainScreen is empty"
EXPECT_TRUE(g_logMsg.find("mainScreen is empty") != std::string::npos ||
g_logMsg.find("skip recovery") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
ssm_->screenSessionMap_.erase(1);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop06
* @tc.desc: Return without crash when screenSession is null
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop06, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId invalidRsId = 999;
g_errLog.clear();
LOG_SetCallback(MyLogCallback);
// Expect: no crash, method returns safely
ssm_->RecoverMultiScreenRelativePositionForDesktop(invalidRsId);
EXPECT_TRUE(true); // If we reach here, no crash occurred
// Optional: verify error log if method logs "screenSession is nullptr"
// EXPECT_TRUE(g_errLog.find("nullptr") != std::string::npos);
LOG_SetCallback(nullptr);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop07
* @tc.desc: Return for non-REAL screen type
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop07, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
screenSession->GetScreenProperty().SetScreenType(ScreenType::VIRTUAL);
ssm_->screenSessionMap_[screenId] = screenSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: method returns early (no "mainScreenId" log)
EXPECT_TRUE(g_logMsg.find("mainScreenId") == std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop08
* @tc.desc: Return when physical screen not ready
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop08, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
// Set screen to uninitialized state to make CheckPhysicalScreenReady return false
screenSession->GetScreenProperty().SetBounds(RRect{0, 0, 0, 0, 0}); // Invalid bounds
ssm_->screenSessionMap_[screenId] = screenSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: log contains "physical screen not ready"
EXPECT_TRUE(g_logMsg.find("not ready") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop09
* @tc.desc: Return when no other physical screen found
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop09, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
ssm_->screenSessionMap_[screenId] = screenSession;
// Only one screen in map
g_errLog.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: error log contains "no other physical screen"
EXPECT_TRUE(g_errLog.find("no other") != std::string::npos ||
g_errLog.find("physical screen found") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop10
* @tc.desc: Fallback to default when SetMultiScreenRelativePosition fails
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop10, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId mainScreenId = 0;
ScreenId extendScreenId = 1;
ScreenId mainRsId = 15;
ScreenId extendRsId = 9;
sptr<ScreenSession> mainSession = new ScreenSession(mainScreenId, ScreenProperty(), mainRsId);
sptr<ScreenSession> extendSession = new ScreenSession(extendScreenId, ScreenProperty(), extendRsId);
mainSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
mainSession->GetScreenProperty().SetBounds(RRect{0, 0, 2560, 1440, 0});
mainSession->SetSerialNumber("serialA");
extendSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
// Set invalid bounds that will cause AreScreensTouching check to fail
extendSession->GetScreenProperty().SetBounds(RRect{9999, 9999, 1920, 1080, 0});
extendSession->SetSerialNumber("serialB");
ssm_->screenSessionMap_[mainScreenId] = mainSession;
ssm_->screenSessionMap_[extendScreenId] = extendSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(mainRsId);
// Expect: log contains "failed" and "default position"
EXPECT_TRUE(g_logMsg.find("failed") != std::string::npos ||
g_logMsg.find("default position") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(mainScreenId);
ssm_->screenSessionMap_.erase(extendScreenId);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop11
* @tc.desc: Return when ResolveScreenIds returns invalid mainScreenId
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop11, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
ScreenId screenId = 0;
ScreenId rsId = 15;
sptr<ScreenSession> screenSession = new ScreenSession(screenId, ScreenProperty(), rsId);
sptr<ScreenSession> otherSession = new ScreenSession(1, ScreenProperty(), 9);
screenSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
screenSession->SetSerialNumber("unknownSerial"); // Serial that won't match config
otherSession->GetScreenProperty().SetScreenType(ScreenType::REAL);
otherSession->SetSerialNumber("unknownSerial2");
ssm_->screenSessionMap_[screenId] = screenSession;
ssm_->screenSessionMap_[1] = otherSession;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId);
// Expect: log contains "ResolveScreenIds failed" or early return message
EXPECT_TRUE(g_logMsg.find("ResolveScreenIds") != std::string::npos ||
g_logMsg.find("invalid") != std::string::npos ||
g_logMsg.find("no need recover") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screenId);
ssm_->screenSessionMap_.erase(1);
}
/**
* @tc.name: RecoverMultiScreenRelativePositionForDesktop12
* @tc.desc: Multiple screen recovery scenario
* @tc.type: FUNC
*/
HWTEST_F(ScreenSessionManagerTest, RecoverMultiScreenRelativePositionForDesktop12, TestSize.Level1)
{
ASSERT_NE(ssm_, nullptr);
// Setup 3 screens (though method only handles 2 screens from config)
ScreenId screen1Id = 0;
ScreenId screen2Id = 1;
ScreenId screen3Id = 2;
ScreenId rsId1 = 15;
ScreenId rsId2 = 9;
ScreenId rsId3 = 20;
sptr<ScreenSession> session1 = new ScreenSession(screen1Id, ScreenProperty(), rsId1);
sptr<ScreenSession> session2 = new ScreenSession(screen2Id, ScreenProperty(), rsId2);
sptr<ScreenSession> session3 = new ScreenSession(screen3Id, ScreenProperty(), rsId3);
session1->GetScreenProperty().SetScreenType(ScreenType::REAL);
session1->SetSerialNumber("serialA");
session2->GetScreenProperty().SetScreenType(ScreenType::REAL);
session2->SetSerialNumber("serialB");
session3->GetScreenProperty().SetScreenType(ScreenType::REAL);
session3->SetSerialNumber("serialC");
ssm_->screenSessionMap_[screen1Id] = session1;
ssm_->screenSessionMap_[screen2Id] = session2;
ssm_->screenSessionMap_[screen3Id] = session3;
g_logMsg.clear();
LOG_SetCallback(MyLogCallback);
// Recovery for rsId1 - should find screen2 (from config) as other screen
ssm_->RecoverMultiScreenRelativePositionForDesktop(rsId1);
// Expect: recovery succeeded for first two screens
EXPECT_TRUE(g_logMsg.find("mainScreenId") != std::string::npos ||
g_logMsg.find("no need recover") != std::string::npos);
LOG_SetCallback(nullptr);
ssm_->screenSessionMap_.erase(screen1Id);
ssm_->screenSessionMap_.erase(screen2Id);
ssm_->screenMap_.erase(screen3Id);
}


分辨率/刷新率