1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "fold_screen_controller/dual_display_fold_policy.h"
17 #include <hisysevent.h>
18 #include <hitrace_meter.h>
19 #include <power_mgr_client.h>
20 #include <transaction/rs_interfaces.h>
21 #include "dm_common.h"
22 #include "session/screen/include/screen_session.h"
23 #include "screen_session_manager.h"
24 
25 #include "window_manager_hilog.h"
26 #include "parameters.h"
27 
28 namespace OHOS::Rosen {
29 namespace {
30 const ScreenId SCREEN_ID_MAIN = 0;
31 const ScreenId SCREEN_ID_SUB = 5;
32 const bool IS_COORDINATION_SUPPORT =
33     OHOS::system::GetBoolParameter("const.window.foldabledevice.is_coordination_support", false);
34 #ifdef TP_FEATURE_ENABLE
35 const int32_t TP_TYPE = 12;
36 #endif
37 const std::string MAIN_TP = "0";
38 const std::string SUB_TP = "1";
39 const int32_t REMOVE_DISPLAY_NODE = 0;
40 const int32_t ADD_DISPLAY_NODE = 1;
41 const uint32_t CHANGE_MODE_TASK_NUM = 3;
42 } // namespace
43 
DualDisplayFoldPolicy(std::recursive_mutex & displayInfoMutex,std::shared_ptr<TaskScheduler> screenPowerTaskScheduler)44 DualDisplayFoldPolicy::DualDisplayFoldPolicy(std::recursive_mutex& displayInfoMutex,
45     std::shared_ptr<TaskScheduler> screenPowerTaskScheduler): screenPowerTaskScheduler_(screenPowerTaskScheduler)
46 {
47     TLOGI(WmsLogTag::DMS, "DualDisplayFoldPolicy created");
48 
49     ScreenId screenIdMain = 0;
50     int32_t foldCreaseRegionPosX = 0;
51     int32_t foldCreaseRegionPosY = 1256;
52     int32_t foldCreaseRegionPosWidth = 1136;
53     int32_t foldCreaseRegionPosHeight = 184;
54 
55     std::vector<DMRect> rect = {
56         {
57             foldCreaseRegionPosX, foldCreaseRegionPosY,
58             foldCreaseRegionPosWidth, foldCreaseRegionPosHeight
59         }
60     };
61     currentFoldCreaseRegion_ = new FoldCreaseRegion(screenIdMain, rect);
62 }
63 
SetdisplayModeChangeStatus(bool status,bool isOnBootAnimation)64 void DualDisplayFoldPolicy::SetdisplayModeChangeStatus(bool status, bool isOnBootAnimation)
65 {
66     if (status) {
67         pengdingTask_ = CHANGE_MODE_TASK_NUM;
68         startTimePoint_ = std::chrono::steady_clock::now();
69         displayModeChangeRunning_ = status;
70     } else {
71         pengdingTask_ --;
72         if (pengdingTask_ != 0) {
73             return;
74         }
75         displayModeChangeRunning_ = false;
76         endTimePoint_ = std::chrono::steady_clock::now();
77         if (lastCachedisplayMode_.load() != GetScreenDisplayMode()) {
78             TLOGI(WmsLogTag::DMS, "start change displaymode to lastest mode");
79             ChangeScreenDisplayMode(lastCachedisplayMode_.load());
80         }
81     }
82 }
83 
CheckDisplayMode(FoldDisplayMode displayMode)84 bool DualDisplayFoldPolicy::CheckDisplayMode(FoldDisplayMode displayMode)
85 {
86     if (displayMode == FoldDisplayMode::COORDINATION && !IS_COORDINATION_SUPPORT) {
87         TLOGI(WmsLogTag::DMS, "Current device is not support coordination");
88         return false;
89     }
90     if (currentDisplayMode_ == displayMode) {
91         TLOGW(WmsLogTag::DMS, "ChangeScreenDisplayMode already in displayMode %{public}d", displayMode);
92         return false;
93     }
94     return true;
95 }
96 
GetScreenIdByDisplayMode(FoldDisplayMode displayMode)97 ScreenId DualDisplayFoldPolicy::GetScreenIdByDisplayMode(FoldDisplayMode displayMode)
98 {
99     ScreenId screenId = SCREEN_ID_MAIN;
100     if (displayMode == FoldDisplayMode::SUB) {
101         screenId = SCREEN_ID_SUB;
102     }
103     return screenId;
104 }
105 
ChangeScreenDisplayMode(FoldDisplayMode displayMode,DisplayModeChangeReason reason)106 void DualDisplayFoldPolicy::ChangeScreenDisplayMode(FoldDisplayMode displayMode, DisplayModeChangeReason reason)
107 {
108     SetLastCacheDisplayMode(displayMode);
109     if (GetModeChangeRunningStatus()) {
110         TLOGW(WmsLogTag::DMS, "last process not complete, skip mode: %{public}d", displayMode);
111         return;
112     }
113     TLOGI(WmsLogTag::DMS, "start change displaymode: %{public}d, lastElapsedMs: %{public}" PRId64 "ms",
114         displayMode, getFoldingElapsedMs());
115     ScreenId screenId = GetScreenIdByDisplayMode(displayMode);
116     sptr<ScreenSession> screenSession = ScreenSessionManager::GetInstance().GetScreenSession(screenId);
117     if (screenSession == nullptr) {
118         TLOGE(WmsLogTag::DMS, "default screenSession is null");
119         return;
120     }
121     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "ssm:ChangeScreenDisplayMode(displayMode= %" PRIu64")", displayMode);
122     {
123         std::lock_guard<std::recursive_mutex> lock_mode(displayModeMutex_);
124         if (!CheckDisplayMode(displayMode)) {
125             return;
126         }
127     }
128     SetdisplayModeChangeStatus(true);
129     ReportFoldDisplayModeChange(displayMode);
130     switch (displayMode) {
131         case FoldDisplayMode::SUB: {
132             ChangeScreenDisplayModeInner(screenSession, SCREEN_ID_MAIN, SCREEN_ID_SUB);
133             break;
134         }
135         case FoldDisplayMode::MAIN: {
136             ChangeScreenDisplayModeInner(screenSession, SCREEN_ID_SUB, SCREEN_ID_MAIN);
137             break;
138         }
139         case FoldDisplayMode::COORDINATION: {
140             ChangeScreenDisplayModeToCoordination();
141             break;
142         }
143         default: {
144             break;
145         }
146     }
147     {
148         std::lock_guard<std::recursive_mutex> lock_mode(displayModeMutex_);
149         currentDisplayMode_ = displayMode;
150         lastDisplayMode_ = displayMode;
151     }
152     ScreenSessionManager::GetInstance().NotifyDisplayModeChanged(displayMode);
153     SetdisplayModeChangeStatus(false);
154 }
155 
SendSensorResult(FoldStatus foldStatus)156 void DualDisplayFoldPolicy::SendSensorResult(FoldStatus foldStatus)
157 {
158     TLOGI(WmsLogTag::DMS, "SendSensorResult FoldStatus: %{public}d", foldStatus);
159     FoldDisplayMode displayMode = GetModeMatchStatus();
160     bool isScreenOn = PowerMgr::PowerMgrClient::GetInstance().IsFoldScreenOn();
161     if (currentDisplayMode_ == FoldDisplayMode::COORDINATION && isScreenOn &&
162         displayMode == FoldDisplayMode::MAIN) {
163         TLOGI(WmsLogTag::DMS, "CurrentDisplayMode is coordination, HalfFold no need to change displaympde");
164         return;
165     }
166     ChangeScreenDisplayMode(displayMode);
167 }
168 
GetCurrentFoldCreaseRegion()169 sptr<FoldCreaseRegion> DualDisplayFoldPolicy::GetCurrentFoldCreaseRegion()
170 {
171     TLOGI(WmsLogTag::DMS, "GetCurrentFoldCreaseRegion");
172     return currentFoldCreaseRegion_;
173 }
174 
LockDisplayStatus(bool locked)175 void DualDisplayFoldPolicy::LockDisplayStatus(bool locked)
176 {
177     TLOGI(WmsLogTag::DMS, "LockDisplayStatus locked: %{public}d", locked);
178     lockDisplayStatus_ = locked;
179 }
180 
SetOnBootAnimation(bool onBootAnimation)181 void DualDisplayFoldPolicy::SetOnBootAnimation(bool onBootAnimation)
182 {
183     TLOGI(WmsLogTag::DMS, "SetOnBootAnimation onBootAnimation: %{public}d", onBootAnimation);
184     onBootAnimation_ = onBootAnimation;
185     if (!onBootAnimation_) {
186         TLOGI(WmsLogTag::DMS, "SetOnBootAnimation when boot animation finished, change display mode");
187         RecoverWhenBootAnimationExit();
188     }
189 }
190 
RecoverWhenBootAnimationExit()191 void DualDisplayFoldPolicy::RecoverWhenBootAnimationExit()
192 {
193     TLOGI(WmsLogTag::DMS, "RecoverWhenBootAnimationExit currentScreen(%{public}" PRIu64 ")", screenId_);
194     FoldDisplayMode displayMode = GetModeMatchStatus();
195     if (currentDisplayMode_ != displayMode) {
196         ChangeScreenDisplayMode(displayMode);
197         return;
198     }
199     ChangeScreenDisplayMode(displayMode);
200 }
201 
UpdateForPhyScreenPropertyChange()202 void DualDisplayFoldPolicy::UpdateForPhyScreenPropertyChange()
203 {
204     TLOGI(WmsLogTag::DMS, "UpdateForPhyScreenPropertyChange currentScreen(%{public}" PRIu64 ")", screenId_);
205     FoldDisplayMode displayMode = GetModeMatchStatus();
206     if (currentDisplayMode_ != displayMode) {
207         ChangeScreenDisplayMode(displayMode);
208     }
209 }
210 
GetModeMatchStatus()211 FoldDisplayMode DualDisplayFoldPolicy::GetModeMatchStatus()
212 {
213     FoldDisplayMode displayMode = FoldDisplayMode::UNKNOWN;
214     switch (currentFoldStatus_) {
215         case FoldStatus::EXPAND: {
216             displayMode = FoldDisplayMode::MAIN;
217             break;
218         }
219         case FoldStatus::FOLDED: {
220             displayMode = FoldDisplayMode::SUB;
221             break;
222         }
223         case FoldStatus::HALF_FOLD: {
224             displayMode = FoldDisplayMode::MAIN;
225             break;
226         }
227         default: {
228             TLOGI(WmsLogTag::DMS, "GetModeMatchStatus FoldStatus is invalid");
229         }
230     }
231     return displayMode;
232 }
233 
ReportFoldDisplayModeChange(FoldDisplayMode displayMode)234 void DualDisplayFoldPolicy::ReportFoldDisplayModeChange(FoldDisplayMode displayMode)
235 {
236     int32_t mode = static_cast<int32_t>(displayMode);
237     TLOGI(WmsLogTag::DMS, "ReportFoldDisplayModeChange displayMode: %{public}d", mode);
238     int32_t ret = HiSysEventWrite(
239         OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER,
240         "DISPLAY_MODE",
241         OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
242         "FOLD_DISPLAY_MODE", mode);
243     if (ret != 0) {
244         TLOGE(WmsLogTag::DMS, "ReportFoldDisplayModeChange Write HiSysEvent error, ret: %{public}d", ret);
245     }
246 }
247 
ReportFoldStatusChangeBegin(int32_t offScreen,int32_t onScreen)248 void DualDisplayFoldPolicy::ReportFoldStatusChangeBegin(int32_t offScreen, int32_t onScreen)
249 {
250     TLOGI(WmsLogTag::DMS, "ReportFoldStatusChangeBegin offScreen: %{public}d, onScreen: %{public}d",
251         offScreen, onScreen);
252     int32_t ret = HiSysEventWrite(
253         OHOS::HiviewDFX::HiSysEvent::Domain::WINDOW_MANAGER,
254         "FOLD_STATE_CHANGE_BEGIN",
255         OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
256         "POWER_OFF_SCREEN", offScreen,
257         "POWER_ON_SCREEN", onScreen);
258     if (ret != 0) {
259         TLOGE(WmsLogTag::DMS, "ReportFoldStatusChangeBegin Write HiSysEvent error, ret: %{public}d", ret);
260     }
261 }
262 
ChangeScreenDisplayModeInner(sptr<ScreenSession> screenSession,ScreenId offScreenId,ScreenId onScreenId)263 void DualDisplayFoldPolicy::ChangeScreenDisplayModeInner(sptr<ScreenSession> screenSession, ScreenId offScreenId,
264     ScreenId onScreenId)
265 {
266     if (onBootAnimation_) {
267         ChangeScreenDisplayModeOnBootAnimation(screenSession, onScreenId);
268         return;
269     }
270     std::string tp = MAIN_TP;
271     if (onScreenId == SCREEN_ID_SUB) {
272         tp = SUB_TP;
273     }
274 #ifdef TP_FEATURE_ENABLE
275     RSInterfaces::GetInstance().SetTpFeatureConfig(TP_TYPE, tp.c_str());
276 #endif
277     ReportFoldStatusChangeBegin((int32_t)SCREEN_ID_MAIN, (int32_t)SCREEN_ID_SUB);
278     bool isScreenOn = PowerMgr::PowerMgrClient::GetInstance().IsFoldScreenOn();
279     TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayModeInner, isScreenOn= %{public}d", isScreenOn);
280     auto taskScreenOff = [=] {
281         TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayMode: off screenId: %{public}" PRIu64 "", offScreenId);
282         screenId_ = offScreenId;
283         ScreenSessionManager::GetInstance().SetKeyguardDrawnDoneFlag(false);
284         ScreenSessionManager::GetInstance().SetScreenPowerForFold(ScreenPowerStatus::POWER_STATUS_OFF);
285         SetdisplayModeChangeStatus(false);
286     };
287     screenPowerTaskScheduler_->PostAsyncTask(taskScreenOff, "screenOffTask");
288     AddOrRemoveDisplayNodeToTree(offScreenId, REMOVE_DISPLAY_NODE);
289 
290     auto taskScreenOn = [=] {
291         TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayMode: on screenId: %{public}" PRIu64 "", onScreenId);
292         screenId_ = onScreenId;
293         if (isScreenOn) {
294             ScreenSessionManager::GetInstance().SetKeyguardDrawnDoneFlag(false);
295             ScreenSessionManager::GetInstance().SetScreenPowerForFold(ScreenPowerStatus::POWER_STATUS_ON);
296         } else {
297             PowerMgr::PowerMgrClient::GetInstance().WakeupDeviceAsync();
298         }
299         SetdisplayModeChangeStatus(false);
300     };
301     screenPowerTaskScheduler_->PostAsyncTask(taskScreenOn, "screenOnTask");
302     AddOrRemoveDisplayNodeToTree(onScreenId, ADD_DISPLAY_NODE);
303 }
304 
ChangeScreenDisplayModeToCoordination()305 void DualDisplayFoldPolicy::ChangeScreenDisplayModeToCoordination()
306 {
307     TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayModeToCoordination");
308     #ifdef TP_FEATURE_ENABLE
309     RSInterfaces::GetInstance().SetTpFeatureConfig(TP_TYPE, MAIN_TP.c_str());
310     #endif
311     bool isScreenOn = PowerMgr::PowerMgrClient::GetInstance().IsFoldScreenOn();
312     TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayModeToCoordination, isScreenOn= %{public}d", isScreenOn);
313     // on main screen
314     auto taskScreenOnMain = [=] {
315         TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayMode: on main screen");
316         screenId_ = SCREEN_ID_MAIN;
317         if (isScreenOn) {
318             ScreenSessionManager::GetInstance().SetKeyguardDrawnDoneFlag(false);
319             ScreenSessionManager::GetInstance().SetScreenPower(ScreenPowerStatus::POWER_STATUS_ON,
320                 PowerStateChangeReason::STATE_CHANGE_REASON_DISPLAY_SWITCH);
321         } else {
322             PowerMgr::PowerMgrClient::GetInstance().WakeupDeviceAsync();
323         }
324         SetdisplayModeChangeStatus(false);
325     };
326     screenPowerTaskScheduler_->PostAsyncTask(taskScreenOnMain, "taskScreenOnMain");
327     // on sub screen
328     auto taskScreenOnSub = [=] {
329         TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayMode: on sub screen");
330         if (isScreenOn) {
331             ScreenSessionManager::GetInstance().SetKeyguardDrawnDoneFlag(false);
332             ScreenSessionManager::GetInstance().SetScreenPowerForFold(SCREEN_ID_SUB,
333                 ScreenPowerStatus::POWER_STATUS_ON);
334         }
335         SetdisplayModeChangeStatus(false);
336     };
337     screenPowerTaskScheduler_->PostAsyncTask(taskScreenOnSub, "taskScreenOnSub");
338     AddOrRemoveDisplayNodeToTree(SCREEN_ID_SUB, ADD_DISPLAY_NODE);
339 }
340 
ChangeScreenDisplayModeOnBootAnimation(sptr<ScreenSession> screenSession,ScreenId screenId)341 void DualDisplayFoldPolicy::ChangeScreenDisplayModeOnBootAnimation(sptr<ScreenSession> screenSession, ScreenId screenId)
342 {
343     TLOGI(WmsLogTag::DMS, "ChangeScreenDisplayModeToFullOnBootAnimation");
344     screenProperty_ = ScreenSessionManager::GetInstance().GetPhyScreenProperty(screenId);
345     ScreenPropertyChangeReason reason = ScreenPropertyChangeReason::FOLD_SCREEN_EXPAND;
346     if (screenId == SCREEN_ID_SUB) {
347         reason = ScreenPropertyChangeReason::FOLD_SCREEN_FOLDING;
348     }
349     screenSession->UpdatePropertyByFoldControl(screenProperty_);
350     screenSession->PropertyChange(screenSession->GetScreenProperty(), reason);
351     TLOGI(WmsLogTag::DMS, "screenBounds : width_= %{public}f, height_= %{public}f",
352         screenSession->GetScreenProperty().GetBounds().rect_.width_,
353         screenSession->GetScreenProperty().GetBounds().rect_.height_);
354     screenId_ = screenId;
355 }
356 
AddOrRemoveDisplayNodeToTree(ScreenId screenId,int32_t command)357 void DualDisplayFoldPolicy::AddOrRemoveDisplayNodeToTree(ScreenId screenId, int32_t command)
358 {
359     TLOGI(WmsLogTag::DMS, "AddOrRemoveDisplayNodeToTree, screenId: %{public}" PRIu64 ", command: %{public}d",
360         screenId, command);
361     sptr<ScreenSession> screenSession = ScreenSessionManager::GetInstance().GetScreenSession(screenId);
362     if (screenSession == nullptr) {
363         TLOGE(WmsLogTag::DMS, "AddOrRemoveDisplayNodeToTree, screenSession is null");
364         return;
365     }
366     std::shared_ptr<RSDisplayNode> displayNode = screenSession->GetDisplayNode();
367     if (displayNode == nullptr) {
368         TLOGE(WmsLogTag::DMS, "AddOrRemoveDisplayNodeToTree, displayNode is null");
369         return;
370     }
371     if (command == ADD_DISPLAY_NODE) {
372         displayNode->AddDisplayNodeToTree();
373     } else if (command == REMOVE_DISPLAY_NODE) {
374         displayNode->RemoveDisplayNodeFromTree();
375     }
376     auto transactionProxy = RSTransactionProxy::GetInstance();
377     if (transactionProxy != nullptr) {
378         TLOGI(WmsLogTag::DMS, "add or remove displayNode");
379         transactionProxy->FlushImplicitTransaction();
380     }
381 }
382 
ExitCoordination()383 void DualDisplayFoldPolicy::ExitCoordination()
384 {
385     ScreenSessionManager::GetInstance().SetScreenPowerForFold(SCREEN_ID_SUB,
386         ScreenPowerStatus::POWER_STATUS_OFF);
387     AddOrRemoveDisplayNodeToTree(SCREEN_ID_SUB, REMOVE_DISPLAY_NODE);
388     FoldDisplayMode displayMode = GetModeMatchStatus();
389     currentDisplayMode_ = displayMode;
390     lastDisplayMode_ = displayMode;
391     TLOGI(WmsLogTag::DMS, "CurrentDisplayMode:%{public}d", displayMode);
392     ScreenSessionManager::GetInstance().NotifyDisplayModeChanged(displayMode);
393 }
394 } // namespace OHOS::Rosen