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 "sensor_connector.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, "SensorConnector"};
27
28#ifdef SENSOR_ENABLE
29    constexpr int64_t ORIENTATION_SENSOR_SAMPLING_RATE = 200000000; // 200ms
30    constexpr int64_t ORIENTATION_SENSOR_REPORTING_RATE = 0;
31    constexpr long ORIENTATION_SENSOR_CALLBACK_TIME_INTERVAL = 200; // 200ms
32    constexpr int VALID_INCLINATION_ANGLE_THRESHOLD_COEFFICIENT = 3;
33#endif
34
35#ifdef WM_SUBSCRIBE_MOTION_ENABLE
36    constexpr int32_t MOTION_ACTION_PORTRAIT = 0;
37    constexpr int32_t MOTION_ACTION_LEFT_LANDSCAPE = 1;
38    constexpr int32_t MOTION_ACTION_PORTRAIT_INVERTED = 2;
39    constexpr int32_t MOTION_ACTION_RIGHT_LANDSCAPE = 3;
40#endif
41}
42
43#ifdef SENSOR_ENABLE
44bool GravitySensorSubscriber::isGravitySensorSubscribed_ = false;
45SensorUser GravitySensorSubscriber::user_;
46long GravitySensorSubscriber::lastCallbackTime_ = 0;
47#endif
48
49#ifdef WM_SUBSCRIBE_MOTION_ENABLE
50bool MotionSubscriber::isMotionSensorSubscribed_ = false;
51sptr<RotationMotionEventCallback> MotionSubscriber::motionEventCallback_ = nullptr;
52#endif
53
54void SensorConnector::SubscribeRotationSensor()
55{
56    WLOGFD("dms: subscribe rotation-related sensor");
57    ScreenRotationController::Init();
58#ifdef WM_SUBSCRIBE_MOTION_ENABLE
59    MotionSubscriber::SubscribeMotionSensor();
60    if (MotionSubscriber::isMotionSensorSubscribed_) {
61        return;
62    }
63#endif
64
65#ifdef SENSOR_ENABLE
66    GravitySensorSubscriber::SubscribeGravitySensor();
67#endif
68}
69
70void SensorConnector::UnsubscribeRotationSensor()
71{
72#ifdef WM_SUBSCRIBE_MOTION_ENABLE
73    MotionSubscriber::UnsubscribeMotionSensor();
74#endif
75
76#ifdef SENSOR_ENABLE
77    GravitySensorSubscriber::UnsubscribeGravitySensor();
78#endif
79}
80
81// Gravity Sensor
82#ifdef SENSOR_ENABLE
83void GravitySensorSubscriber::SubscribeGravitySensor()
84{
85    WLOGFI("dms: Subscribe gravity Sensor");
86    if (isGravitySensorSubscribed_) {
87        WLOGFE("dms: gravity sensor's already subscribed");
88        return;
89    }
90    if (strcpy_s(user_.name, sizeof(user_.name), "ScreenRotationController") != EOK) {
91        WLOGFE("dms strcpy_s error");
92        return;
93    }
94    user_.userData = nullptr;
95    user_.callback = &HandleGravitySensorEventCallback;
96    if (SubscribeSensor(SENSOR_TYPE_ID_GRAVITY, &user_) != 0) {
97        WLOGFE("dms: Subscribe gravity sensor failed");
98        return;
99    }
100    SetBatch(SENSOR_TYPE_ID_GRAVITY, &user_, ORIENTATION_SENSOR_SAMPLING_RATE, ORIENTATION_SENSOR_REPORTING_RATE);
101    SetMode(SENSOR_TYPE_ID_GRAVITY, &user_, SENSOR_ON_CHANGE);
102    if (ActivateSensor(SENSOR_TYPE_ID_GRAVITY, &user_) != 0) {
103        WLOGFE("dms: Activate gravity sensor failed");
104        return;
105    }
106    isGravitySensorSubscribed_ = true;
107}
108
109void GravitySensorSubscriber::UnsubscribeGravitySensor()
110{
111    WLOGFI("dms: Unsubscribe gravity Sensor");
112    if (!isGravitySensorSubscribed_) {
113        WLOGFE("dms: Orientation Sensor is not subscribed");
114        return;
115    }
116    if (DeactivateSensor(SENSOR_TYPE_ID_GRAVITY, &user_) != 0) {
117        WLOGFE("dms: Deactivate gravity sensor failed");
118        return;
119    }
120    if (UnsubscribeSensor(SENSOR_TYPE_ID_GRAVITY, &user_) != 0) {
121        WLOGFE("dms: Unsubscribe gravity sensor failed");
122        return;
123    }
124    isGravitySensorSubscribed_ = false;
125}
126
127void GravitySensorSubscriber::HandleGravitySensorEventCallback(SensorEvent *event)
128{
129    if (!CheckCallbackTimeInterval() || event == nullptr) {
130        return;
131    }
132    if (event->sensorTypeId != SENSOR_TYPE_ID_GRAVITY) {
133        WLOGE("dms: Orientation Sensor Callback is not SENSOR_TYPE_ID_GRAVITY");
134        return;
135    }
136    GravityData* gravityData = reinterpret_cast<GravityData*>(event->data);
137    int sensorDegree = CalcRotationDegree(gravityData);
138    DeviceRotation sensorRotationConverted = ScreenRotationController::ConvertSensorToDeviceRotation(
139        CalcSensorRotation(sensorDegree));
140    ScreenRotationController::HandleSensorEventInput(sensorRotationConverted);
141}
142
143SensorRotation GravitySensorSubscriber::CalcSensorRotation(int sensorDegree)
144{
145    // Use ROTATION_0 when degree range is [0, 30]∪[330, 359]
146    if (sensorDegree >= 0 && (sensorDegree <= 30 || sensorDegree >= 330)) {
147        return SensorRotation::ROTATION_0;
148    } else if (sensorDegree >= 60 && sensorDegree <= 120) { // Use ROTATION_90 when degree range is [60, 120]
149        return SensorRotation::ROTATION_90;
150    } else if (sensorDegree >= 150 && sensorDegree <= 210) { // Use ROTATION_180 when degree range is [150, 210]
151        return SensorRotation::ROTATION_180;
152    } else if (sensorDegree >= 240 && sensorDegree <= 300) { // Use ROTATION_270 when degree range is [240, 300]
153        return SensorRotation::ROTATION_270;
154    } else {
155        return SensorRotation::INVALID;
156    }
157}
158
159int GravitySensorSubscriber::CalcRotationDegree(GravityData* gravityData)
160{
161    if (gravityData == nullptr) {
162        return -1;
163    }
164    float x = gravityData->x;
165    float y = gravityData->y;
166    float z = gravityData->z;
167    int degree = -1;
168    if ((x * x + y * y) * VALID_INCLINATION_ANGLE_THRESHOLD_COEFFICIENT < z * z) {
169        return degree;
170    }
171    // arccotx = pi / 2 - arctanx, 90 is used to calculate acot(in degree); degree = rad / pi * 180
172    degree = 90 - static_cast<int>(round(atan2(y, -x) / M_PI * 180));
173    // Normalize the degree to the range of 0~360
174    return degree >= 0 ? degree % 360 : degree % 360 + 360;
175}
176
177bool GravitySensorSubscriber::CheckCallbackTimeInterval()
178{
179    std::chrono::milliseconds ms = std::chrono::time_point_cast<std::chrono::milliseconds>(
180        std::chrono::steady_clock::now()).time_since_epoch();
181    long currentTimeInMillitm = ms.count();
182    if (currentTimeInMillitm - lastCallbackTime_ < ORIENTATION_SENSOR_CALLBACK_TIME_INTERVAL) {
183        return false;
184    }
185    lastCallbackTime_ = currentTimeInMillitm;
186    return true;
187}
188#endif
189
190// Motion
191#ifdef WM_SUBSCRIBE_MOTION_ENABLE
192void MotionSubscriber::SubscribeMotionSensor()
193{
194    WLOGFD("dms: Subscribe motion Sensor");
195    if (isMotionSensorSubscribed_) {
196        WLOGFE("dms: motion sensor's already subscribed");
197        return;
198    }
199    sptr<RotationMotionEventCallback> callback = new (std::nothrow) RotationMotionEventCallback();
200    if (callback == nullptr) {
201        return;
202    }
203    int32_t ret = OHOS::Msdp::SubscribeCallback(OHOS::Msdp::MOTION_TYPE_ROTATION, callback);
204    if (ret != 0) {
205        return;
206    }
207    motionEventCallback_ = callback;
208    isMotionSensorSubscribed_ = true;
209}
210
211void MotionSubscriber::UnsubscribeMotionSensor()
212{
213    if (!isMotionSensorSubscribed_) {
214        WLOGFI("dms: Unsubscribe motion sensor");
215        return;
216    }
217    int32_t ret = OHOS::Msdp::UnsubscribeCallback(OHOS::Msdp::MOTION_TYPE_ROTATION, motionEventCallback_);
218    if (ret != 0) {
219        return;
220    }
221    isMotionSensorSubscribed_ = false;
222}
223
224void RotationMotionEventCallback::OnMotionChanged(const MotionEvent& motionData)
225{
226    DeviceRotation motionRotation = DeviceRotation::INVALID;
227    switch (motionData.status) {
228        case MOTION_ACTION_PORTRAIT: {
229            motionRotation = DeviceRotation::ROTATION_PORTRAIT;
230            break;
231        }
232        case MOTION_ACTION_LEFT_LANDSCAPE: {
233            motionRotation = ScreenRotationController::IsDefaultDisplayRotationPortrait() ?
234                DeviceRotation::ROTATION_LANDSCAPE_INVERTED : DeviceRotation::ROTATION_LANDSCAPE;
235            break;
236        }
237        case MOTION_ACTION_PORTRAIT_INVERTED: {
238            motionRotation = DeviceRotation::ROTATION_PORTRAIT_INVERTED;
239            break;
240        }
241        case MOTION_ACTION_RIGHT_LANDSCAPE: {
242            motionRotation = ScreenRotationController::IsDefaultDisplayRotationPortrait() ?
243                DeviceRotation::ROTATION_LANDSCAPE : DeviceRotation::ROTATION_LANDSCAPE_INVERTED;
244            break;
245        }
246        default: {
247            break;
248        }
249    }
250    ScreenRotationController::HandleSensorEventInput(motionRotation);
251}
252#endif
253} // Rosen
254} // OHOS