1/*
2 * Copyright (c) 2022 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 "screen_rotation_controller.h"
17
18#include <chrono>
19#include <securec.h>
20
21#include "display_manager_service_inner.h"
22
23namespace OHOS {
24namespace Rosen {
25namespace {
26constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "ScreenRotationController"};
27}
28
29DisplayId ScreenRotationController::defaultDisplayId_ = 0;
30Rotation ScreenRotationController::currentDisplayRotation_;
31bool ScreenRotationController::isScreenRotationLocked_ = true;
32uint32_t ScreenRotationController::defaultDeviceRotationOffset_ = 0;
33Orientation ScreenRotationController::lastOrientationType_ = Orientation::UNSPECIFIED;
34Rotation ScreenRotationController::lastSensorDecidedRotation_;
35Rotation ScreenRotationController::rotationLockedRotation_;
36uint32_t ScreenRotationController::defaultDeviceRotation_ = 0;
37std::map<SensorRotation, DeviceRotation> ScreenRotationController::sensorToDeviceRotationMap_;
38std::map<DeviceRotation, Rotation> ScreenRotationController::deviceToDisplayRotationMap_;
39std::map<Rotation, DisplayOrientation> ScreenRotationController::displayToDisplayOrientationMap_;
40DeviceRotation ScreenRotationController::lastSensorRotationConverted_ = DeviceRotation::INVALID;
41
42void ScreenRotationController::Init()
43{
44    ProcessRotationMapping();
45    currentDisplayRotation_ = GetCurrentDisplayRotation();
46    defaultDisplayId_ = DisplayManagerServiceInner::GetInstance().GetDefaultDisplayId();
47    if (defaultDisplayId_  == DISPLAY_ID_INVALID) {
48        WLOGFE("defaultDisplayId_ is invalid");
49    }
50    lastSensorDecidedRotation_ = currentDisplayRotation_;
51    rotationLockedRotation_ = currentDisplayRotation_;
52}
53
54bool ScreenRotationController::IsScreenRotationLocked()
55{
56    return isScreenRotationLocked_;
57}
58
59DMError ScreenRotationController::SetScreenRotationLocked(bool isLocked)
60{
61    isScreenRotationLocked_ = isLocked;
62    if (isLocked) {
63        rotationLockedRotation_ = GetCurrentDisplayRotation();
64        return DMError::DM_OK;
65    }
66    if (GetCurrentDisplayRotation() == ConvertDeviceToDisplayRotation(lastSensorRotationConverted_)) {
67        return DMError::DM_OK;
68    }
69    Orientation currentOrientation = GetPreferredOrientation();
70    if (IsSensorRelatedOrientation(currentOrientation)) {
71        ProcessSwitchToSensorRelatedOrientation(currentOrientation, lastSensorRotationConverted_);
72    }
73    return DMError::DM_OK;
74}
75
76void ScreenRotationController::SetDefaultDeviceRotationOffset(uint32_t defaultDeviceRotationOffset)
77{
78    // Available options for defaultDeviceRotationOffset: {0, 90, 180, 270}
79    if (defaultDeviceRotationOffset < 0 || defaultDeviceRotationOffset > 270 || defaultDeviceRotationOffset % 90 != 0) {
80        return;
81    }
82    defaultDeviceRotationOffset_ = defaultDeviceRotationOffset;
83}
84
85void ScreenRotationController::HandleSensorEventInput(DeviceRotation deviceRotation)
86{
87    if (deviceRotation == DeviceRotation::INVALID) {
88        WLOGFW("deviceRotation is invalid, return.");
89        return;
90    }
91    Orientation orientation = GetPreferredOrientation();
92    currentDisplayRotation_ = GetCurrentDisplayRotation();
93    lastSensorRotationConverted_ = deviceRotation;
94    if (!IsSensorRelatedOrientation(orientation)) {
95        WLOGFD("If the current preferred orientation is locked or sensor-independent, return.");
96        return;
97    }
98
99    if (currentDisplayRotation_ == ConvertDeviceToDisplayRotation(deviceRotation)) {
100        WLOGFD("If the current display rotation is same to sensor rotation, return.");
101        return;
102    }
103    Rotation targetDisplayRotation = CalcTargetDisplayRotation(orientation, deviceRotation);
104    SetScreenRotation(targetDisplayRotation);
105}
106
107Rotation ScreenRotationController::GetCurrentDisplayRotation()
108{
109    sptr<DisplayInfo> defaultDisplayInfo = DisplayManagerServiceInner::GetInstance().GetDefaultDisplay();
110    if (defaultDisplayInfo == nullptr) {
111        WLOGFE("Cannot get default display info");
112        return defaultDeviceRotation_ == 0 ? ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT) :
113            ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE);
114    }
115    return defaultDisplayInfo->GetRotation();
116}
117
118Orientation ScreenRotationController::GetPreferredOrientation()
119{
120    sptr<ScreenInfo> screenInfo = DisplayManagerServiceInner::GetInstance().GetScreenInfoByDisplayId(defaultDisplayId_);
121    if (screenInfo == nullptr) {
122        WLOGFE("Cannot get default screen info");
123        return Orientation::UNSPECIFIED;
124    }
125    return screenInfo->GetOrientation();
126}
127
128Rotation ScreenRotationController::CalcTargetDisplayRotation(
129    Orientation requestedOrientation, DeviceRotation sensorRotationConverted)
130{
131    switch (requestedOrientation) {
132        case Orientation::SENSOR: {
133            lastSensorDecidedRotation_ = ConvertDeviceToDisplayRotation(sensorRotationConverted);
134            return lastSensorDecidedRotation_;
135        }
136        case Orientation::SENSOR_VERTICAL: {
137            return ProcessAutoRotationPortraitOrientation(sensorRotationConverted);
138        }
139        case Orientation::SENSOR_HORIZONTAL: {
140            return ProcessAutoRotationLandscapeOrientation(sensorRotationConverted);
141        }
142        case Orientation::AUTO_ROTATION_RESTRICTED: {
143            if (isScreenRotationLocked_) {
144                return currentDisplayRotation_;
145            }
146            lastSensorDecidedRotation_ = ConvertDeviceToDisplayRotation(sensorRotationConverted);
147            return lastSensorDecidedRotation_;
148        }
149        case Orientation::AUTO_ROTATION_PORTRAIT_RESTRICTED: {
150            if (isScreenRotationLocked_) {
151                return currentDisplayRotation_;
152            }
153            return ProcessAutoRotationPortraitOrientation(sensorRotationConverted);
154        }
155        case Orientation::AUTO_ROTATION_LANDSCAPE_RESTRICTED: {
156            if (isScreenRotationLocked_) {
157                return currentDisplayRotation_;
158            }
159            return ProcessAutoRotationLandscapeOrientation(sensorRotationConverted);
160        }
161        default: {
162            return currentDisplayRotation_;
163        }
164    }
165}
166
167Rotation ScreenRotationController::ProcessAutoRotationPortraitOrientation(DeviceRotation sensorRotationConverted)
168{
169    if (IsDeviceRotationHorizontal(sensorRotationConverted)) {
170        return currentDisplayRotation_;
171    }
172    lastSensorDecidedRotation_ = ConvertDeviceToDisplayRotation(sensorRotationConverted);
173    return lastSensorDecidedRotation_;
174}
175
176Rotation ScreenRotationController::ProcessAutoRotationLandscapeOrientation(DeviceRotation sensorRotationConverted)
177{
178    if (IsDeviceRotationVertical(sensorRotationConverted)) {
179        return currentDisplayRotation_;
180    }
181    lastSensorDecidedRotation_ = ConvertDeviceToDisplayRotation(sensorRotationConverted);
182    return lastSensorDecidedRotation_;
183}
184
185void ScreenRotationController::SetScreenRotation(Rotation targetRotation, bool withAnimation)
186{
187    if (targetRotation == GetCurrentDisplayRotation()) {
188        return;
189    }
190    DisplayManagerServiceInner::GetInstance().GetDefaultDisplay()->SetRotation(targetRotation);
191    DisplayManagerServiceInner::GetInstance().SetRotationFromWindow(defaultDisplayId_, targetRotation, withAnimation);
192    WLOGFI("dms: Set screen rotation: %{public}u withAnimation: %{public}u", targetRotation, withAnimation);
193}
194
195DeviceRotation ScreenRotationController::CalcDeviceRotation(SensorRotation sensorRotation)
196{
197    if (sensorRotation == SensorRotation::INVALID) {
198        return DeviceRotation::INVALID;
199    }
200    // offset(in degree) divided by 90 to get rotation bias
201    int32_t bias = static_cast<int32_t>(defaultDeviceRotationOffset_ / 90);
202    int32_t deviceRotationValue = static_cast<int32_t>(sensorRotation) - bias;
203    while (deviceRotationValue < 0) {
204        // +4 is used to normalize the values into the range 0~3, corresponding to the four rotations.
205        deviceRotationValue += 4;
206    }
207    if (defaultDeviceRotation_ == 1) {
208        deviceRotationValue += static_cast<int32_t>(defaultDeviceRotation_);
209        // %2 to determine whether the rotation is horizontal or vertical.
210        if (deviceRotationValue % 2 == 0) {
211            // if device's default rotation is landscape, use -2 to swap 0 and 90, 180 and 270.
212            deviceRotationValue -= 2;
213        }
214    }
215    return static_cast<DeviceRotation>(deviceRotationValue);
216}
217
218bool ScreenRotationController::IsSensorRelatedOrientation(Orientation orientation)
219{
220    if ((orientation >= Orientation::UNSPECIFIED && orientation <= Orientation::REVERSE_HORIZONTAL) ||
221        orientation == Orientation::LOCKED) {
222        return false;
223    }
224    return true;
225}
226
227void ScreenRotationController::ProcessSwitchToSensorRelatedOrientation(
228    Orientation orientation, DeviceRotation sensorRotationConverted)
229{
230    lastOrientationType_ = orientation;
231    switch (orientation) {
232        case Orientation::AUTO_ROTATION_RESTRICTED: {
233            if (isScreenRotationLocked_) {
234                SetScreenRotation(rotationLockedRotation_);
235                return;
236            }
237            [[fallthrough]];
238        }
239        case Orientation::SENSOR: {
240            ProcessSwitchToAutoRotation(sensorRotationConverted);
241            return;
242        }
243        case Orientation::AUTO_ROTATION_PORTRAIT_RESTRICTED: {
244            if (isScreenRotationLocked_) {
245                ProcessSwitchToAutoRotationPortraitRestricted();
246                return;
247            }
248            [[fallthrough]];
249        }
250        case Orientation::SENSOR_VERTICAL: {
251            ProcessSwitchToAutoRotationPortrait(sensorRotationConverted);
252            return;
253        }
254        case Orientation::AUTO_ROTATION_LANDSCAPE_RESTRICTED: {
255            if (isScreenRotationLocked_) {
256                ProcessSwitchToAutoRotationLandscapeRestricted();
257                return;
258            }
259            [[fallthrough]];
260        }
261        case Orientation::SENSOR_HORIZONTAL: {
262            ProcessSwitchToAutoRotationLandscape(sensorRotationConverted);
263            return;
264        }
265        default: {
266            return;
267        }
268    }
269}
270
271void ScreenRotationController::ProcessSwitchToAutoRotation(DeviceRotation rotation)
272{
273    if (rotation != DeviceRotation::INVALID) {
274        SetScreenRotation(ConvertDeviceToDisplayRotation(rotation));
275    }
276}
277
278void ScreenRotationController::ProcessSwitchToAutoRotationPortrait(DeviceRotation rotation)
279{
280    if (IsDeviceRotationVertical(rotation)) {
281        SetScreenRotation(ConvertDeviceToDisplayRotation(rotation));
282        return;
283    }
284    SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT));
285}
286
287void ScreenRotationController::ProcessSwitchToAutoRotationLandscape(DeviceRotation rotation)
288{
289    if (IsDeviceRotationHorizontal(rotation)) {
290        SetScreenRotation(ConvertDeviceToDisplayRotation(rotation));
291        return;
292    }
293    SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE));
294}
295
296void ScreenRotationController::ProcessSwitchToAutoRotationPortraitRestricted()
297{
298    if (IsCurrentDisplayVertical()) {
299        return;
300    }
301    if (IsDisplayRotationVertical(rotationLockedRotation_)) {
302        SetScreenRotation(rotationLockedRotation_);
303        return;
304    }
305    SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT));
306}
307
308void ScreenRotationController::ProcessSwitchToAutoRotationLandscapeRestricted()
309{
310    if (IsCurrentDisplayHorizontal()) {
311        return;
312    }
313    if (IsDisplayRotationHorizontal(rotationLockedRotation_)) {
314        SetScreenRotation(rotationLockedRotation_);
315        return;
316    }
317    SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE));
318}
319
320DeviceRotation ScreenRotationController::ConvertSensorToDeviceRotation(SensorRotation sensorRotation)
321{
322    if (sensorToDeviceRotationMap_.empty()) {
323        ProcessRotationMapping();
324    }
325    return sensorToDeviceRotationMap_.at(sensorRotation);
326}
327
328DisplayOrientation ScreenRotationController::ConvertRotationToDisplayOrientation(Rotation rotation)
329{
330    if (displayToDisplayOrientationMap_.empty()) {
331        ProcessRotationMapping();
332    }
333    return displayToDisplayOrientationMap_.at(rotation);
334}
335
336Rotation ScreenRotationController::ConvertDeviceToDisplayRotation(DeviceRotation deviceRotation)
337{
338    if (deviceRotation == DeviceRotation::INVALID) {
339        return GetCurrentDisplayRotation();
340    }
341    if (deviceToDisplayRotationMap_.empty()) {
342        ProcessRotationMapping();
343    }
344    return deviceToDisplayRotationMap_.at(deviceRotation);
345}
346
347void ScreenRotationController::ProcessRotationMapping()
348{
349    sptr<SupportedScreenModes> modes =
350        DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId(defaultDisplayId_);
351
352    // 0 means PORTRAIT, 1 means LANDSCAPE.
353    defaultDeviceRotation_ = (modes == nullptr || modes->width_ < modes->height_) ? 0 : 1;
354
355    if (deviceToDisplayRotationMap_.empty()) {
356        deviceToDisplayRotationMap_ = {
357            {DeviceRotation::ROTATION_PORTRAIT,
358                defaultDeviceRotation_ == 0 ? Rotation::ROTATION_0 : Rotation::ROTATION_90},
359            {DeviceRotation::ROTATION_LANDSCAPE,
360                defaultDeviceRotation_ == 1 ? Rotation::ROTATION_0 : Rotation::ROTATION_90},
361            {DeviceRotation::ROTATION_PORTRAIT_INVERTED,
362                defaultDeviceRotation_ == 0 ? Rotation::ROTATION_180 : Rotation::ROTATION_270},
363            {DeviceRotation::ROTATION_LANDSCAPE_INVERTED,
364                defaultDeviceRotation_ == 1 ? Rotation::ROTATION_180 : Rotation::ROTATION_270},
365        };
366    }
367    if (displayToDisplayOrientationMap_.empty()) {
368        displayToDisplayOrientationMap_ = {
369            {defaultDeviceRotation_ == 0 ? Rotation::ROTATION_0 : Rotation::ROTATION_90,
370                DisplayOrientation::PORTRAIT},
371            {defaultDeviceRotation_ == 1 ? Rotation::ROTATION_0 : Rotation::ROTATION_90,
372                DisplayOrientation::LANDSCAPE},
373            {defaultDeviceRotation_ == 0 ? Rotation::ROTATION_180 : Rotation::ROTATION_270,
374                DisplayOrientation::PORTRAIT_INVERTED},
375            {defaultDeviceRotation_ == 1 ? Rotation::ROTATION_180 : Rotation::ROTATION_270,
376                DisplayOrientation::LANDSCAPE_INVERTED},
377        };
378    }
379    if (sensorToDeviceRotationMap_.empty()) {
380        sensorToDeviceRotationMap_ = {
381            {SensorRotation::ROTATION_0, CalcDeviceRotation(SensorRotation::ROTATION_0)},
382            {SensorRotation::ROTATION_90, CalcDeviceRotation(SensorRotation::ROTATION_90)},
383            {SensorRotation::ROTATION_180, CalcDeviceRotation(SensorRotation::ROTATION_180)},
384            {SensorRotation::ROTATION_270, CalcDeviceRotation(SensorRotation::ROTATION_270)},
385            {SensorRotation::INVALID, DeviceRotation::INVALID},
386        };
387    }
388}
389
390bool ScreenRotationController::IsDeviceRotationVertical(DeviceRotation deviceRotation)
391{
392    return (deviceRotation == DeviceRotation::ROTATION_PORTRAIT) ||
393        (deviceRotation == DeviceRotation::ROTATION_PORTRAIT_INVERTED);
394}
395
396bool ScreenRotationController::IsDeviceRotationHorizontal(DeviceRotation deviceRotation)
397{
398    return (deviceRotation == DeviceRotation::ROTATION_LANDSCAPE) ||
399        (deviceRotation == DeviceRotation::ROTATION_LANDSCAPE_INVERTED);
400}
401
402bool ScreenRotationController::IsCurrentDisplayVertical()
403{
404    return IsDisplayRotationVertical(GetCurrentDisplayRotation());
405}
406
407bool ScreenRotationController::IsCurrentDisplayHorizontal()
408{
409    return IsDisplayRotationHorizontal(GetCurrentDisplayRotation());
410}
411
412bool ScreenRotationController::IsDefaultDisplayRotationPortrait()
413{
414    return Rotation::ROTATION_0 == ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT);
415}
416
417bool ScreenRotationController::IsDisplayRotationVertical(Rotation rotation)
418{
419    return (rotation == ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT)) ||
420        (rotation == ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT_INVERTED));
421}
422
423bool ScreenRotationController::IsDisplayRotationHorizontal(Rotation rotation)
424{
425    return (rotation == ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE)) ||
426        (rotation == ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE_INVERTED));
427}
428
429void ScreenRotationController::ProcessSwitchToSensorUnrelatedOrientation(Orientation orientation, bool withAnimation)
430{
431    if (lastOrientationType_ == orientation) {
432        return;
433    }
434    lastOrientationType_ = orientation;
435    switch (orientation) {
436        case Orientation::UNSPECIFIED: {
437            SetScreenRotation(Rotation::ROTATION_0, withAnimation);
438            break;
439        }
440        case Orientation::VERTICAL: {
441            SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT), withAnimation);
442            break;
443        }
444        case Orientation::REVERSE_VERTICAL: {
445            SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_PORTRAIT_INVERTED),
446                withAnimation);
447            break;
448        }
449        case Orientation::HORIZONTAL: {
450            SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE), withAnimation);
451            break;
452        }
453        case Orientation::REVERSE_HORIZONTAL: {
454            SetScreenRotation(ConvertDeviceToDisplayRotation(DeviceRotation::ROTATION_LANDSCAPE_INVERTED),
455                withAnimation);
456            break;
457        }
458        default: {
459            return;
460        }
461    }
462}
463
464void ScreenRotationController::ProcessOrientationSwitch(Orientation orientation, bool withAnimation)
465{
466    if (!IsSensorRelatedOrientation(orientation)) {
467        ProcessSwitchToSensorUnrelatedOrientation(orientation, withAnimation);
468    } else {
469        ProcessSwitchToSensorRelatedOrientation(orientation, lastSensorRotationConverted_);
470    }
471}
472} // Rosen
473} // OHOS