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 "power_napi.h"
17
18#include "napi_errors.h"
19#include "napi_utils.h"
20#include "power_common.h"
21#include "power_log.h"
22#include "power_mgr_client.h"
23#include <fcntl.h>
24#include <sys/ioctl.h>
25#include <unistd.h>
26
27#define SHUT_STAGE_FRAMEWORK_START 1
28#define BOOT_DETECTOR_IOCTL_BASE 'B'
29#define SET_SHUT_STAGE _IOW(BOOT_DETECTOR_IOCTL_BASE, 106, int)
30#define SET_REBOOT _IOW(BOOT_DETECTOR_IOCTL_BASE, 109, int)
31
32namespace OHOS {
33namespace PowerMgr {
34namespace {
35constexpr uint32_t REBOOT_SHUTDOWN_MAX_ARGC = 1;
36constexpr uint32_t WAKEUP_MAX_ARGC = 1;
37constexpr uint32_t SET_MODE_CALLBACK_MAX_ARGC = 2;
38constexpr uint32_t SET_MODE_PROMISE_MAX_ARGC = 1;
39constexpr uint32_t SUSPEND_MAX_ARGC = 1;
40constexpr uint32_t SET_SCREEN_OFFTIME_ARGC = 1;
41constexpr uint32_t HIBERNATE_ARGC = 1;
42constexpr int32_t INDEX_0 = 0;
43constexpr int32_t INDEX_1 = 1;
44constexpr int32_t RESTORE_DEFAULT_SCREENOFF_TIME = -1;
45static PowerMgrClient& g_powerMgrClient = PowerMgrClient::GetInstance();
46} // namespace
47napi_value PowerNapi::Shutdown(napi_env env, napi_callback_info info)
48{
49    return RebootOrShutdown(env, info, false);
50}
51
52napi_value PowerNapi::Reboot(napi_env env, napi_callback_info info)
53{
54    return RebootOrShutdown(env, info, true);
55}
56
57napi_value PowerNapi::IsActive(napi_env env, napi_callback_info info)
58{
59    bool isScreen = g_powerMgrClient.IsScreenOn();
60    napi_value napiValue;
61    NAPI_CALL(env, napi_get_boolean(env, isScreen, &napiValue));
62    return napiValue;
63}
64
65napi_value PowerNapi::Wakeup(napi_env env, napi_callback_info info)
66{
67    size_t argc = WAKEUP_MAX_ARGC;
68    napi_value argv[argc];
69    NapiUtils::GetCallbackInfo(env, info, argc, argv);
70
71    NapiErrors error;
72    if (argc != WAKEUP_MAX_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_string)) {
73        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
74    }
75
76    std::string detail = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
77    POWER_HILOGD(FEATURE_WAKEUP, "Wakeup type: APPLICATION, reason: %{public}s", detail.c_str());
78    PowerErrors code = g_powerMgrClient.WakeupDevice(WakeupDeviceType::WAKEUP_DEVICE_APPLICATION, detail);
79    if (code != PowerErrors::ERR_OK) {
80        error.ThrowError(env, code);
81    }
82    return nullptr;
83}
84
85napi_value PowerNapi::Suspend(napi_env env, napi_callback_info info)
86{
87    size_t argc = SUSPEND_MAX_ARGC;
88    napi_value argv[argc];
89    NapiUtils::GetCallbackInfo(env, info, argc, argv);
90
91    NapiErrors error;
92    if (argc != SUSPEND_MAX_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_boolean)) {
93        if (!NapiUtils::CheckValueType(env, argv[INDEX_0], napi_undefined)) {
94            std::string detail = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
95            if (!detail.empty()) {
96                return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
97            }
98        }
99    }
100
101    bool isForce = false;
102    napi_get_value_bool(env, argv[0], &isForce);
103
104    PowerErrors code;
105    if (isForce) {
106        code = g_powerMgrClient.ForceSuspendDevice();
107    } else {
108        code = g_powerMgrClient.SuspendDevice();
109    }
110    if (code != PowerErrors::ERR_OK) {
111        POWER_HILOGE(FEATURE_WAKEUP, "Suspend Device fail, isForce:%{public}d", isForce);
112        return error.ThrowError(env, code);
113    }
114    return nullptr;
115}
116
117napi_value PowerNapi::Hibernate(napi_env env, napi_callback_info info)
118{
119    size_t argc = HIBERNATE_ARGC;
120    napi_value argv[argc];
121    NapiUtils::GetCallbackInfo(env, info, argc, argv);
122
123    NapiErrors error;
124    if (argc != HIBERNATE_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_boolean)) {
125        if (!NapiUtils::CheckValueType(env, argv[INDEX_0], napi_undefined)) {
126            std::string detail = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
127            if (!detail.empty()) {
128                return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
129            }
130        }
131    }
132
133    bool clearMemory = false;
134    napi_get_value_bool(env, argv[0], &clearMemory);
135
136    PowerErrors code = g_powerMgrClient.Hibernate(clearMemory);
137    if (code != PowerErrors::ERR_OK) {
138        POWER_HILOGE(FEATURE_WAKEUP, "Hibernate failed.");
139        error.ThrowError(env, code);
140    }
141    return nullptr;
142}
143
144napi_value PowerNapi::SetPowerMode(napi_env env, napi_callback_info info)
145{
146    size_t argc = SET_MODE_CALLBACK_MAX_ARGC;
147    napi_value argv[argc];
148    NapiUtils::GetCallbackInfo(env, info, argc, argv);
149
150    NapiErrors error;
151    if (argc != SET_MODE_CALLBACK_MAX_ARGC && argc != SET_MODE_PROMISE_MAX_ARGC) {
152        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
153    }
154
155    std::unique_ptr<AsyncCallbackInfo> asyncInfo = std::make_unique<AsyncCallbackInfo>();
156    RETURN_IF_WITH_RET(asyncInfo == nullptr, nullptr);
157    // callback
158    if (argc == SET_MODE_CALLBACK_MAX_ARGC) {
159        POWER_HILOGD(FEATURE_POWER_MODE, "Call setPowerMode callback");
160        return SetPowerModeCallback(env, argv, asyncInfo);
161    }
162
163    // promise
164    POWER_HILOGD(FEATURE_POWER_MODE, "Call setPowerMode promise");
165    return SetPowerModePromise(env, argv, asyncInfo);
166}
167
168napi_value PowerNapi::GetPowerMode(napi_env env, napi_callback_info info)
169{
170    PowerMode mode = g_powerMgrClient.GetDeviceMode();
171    napi_value napiValue;
172    NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(mode), &napiValue));
173    return napiValue;
174}
175
176static void SetFrameworkBootStage(bool isReboot)
177{
178    int fd = open("/dev/bbox", O_WRONLY);
179    if (fd < 0) {
180        POWER_HILOGE(FEATURE_SHUTDOWN, "open /dev/bbox failed!");
181        return;
182    }
183    int rebootFlag = isReboot ? 1 : 0;
184    int ret = ioctl(fd, SET_REBOOT, &rebootFlag);
185    if (ret < 0) {
186        POWER_HILOGE(FEATURE_SHUTDOWN, "set reboot flag failed!");
187        close(fd);
188        return;
189    }
190    int stage = SHUT_STAGE_FRAMEWORK_START;
191    ret = ioctl(fd, SET_SHUT_STAGE, &stage);
192    if (ret < 0) {
193        POWER_HILOGE(FEATURE_SHUTDOWN, "set shut stage failed!");
194    }
195    close(fd);
196    return;
197}
198
199napi_value PowerNapi::RebootOrShutdown(napi_env env, napi_callback_info info, bool isReboot)
200{
201    size_t argc = REBOOT_SHUTDOWN_MAX_ARGC;
202    napi_value argv[argc];
203    SetFrameworkBootStage(isReboot);
204    NapiUtils::GetCallbackInfo(env, info, argc, argv);
205
206    NapiErrors error;
207    if (argc != REBOOT_SHUTDOWN_MAX_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_string)) {
208        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
209    }
210
211    std::string reason = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
212    POWER_HILOGD(FEATURE_SHUTDOWN, "reboot: %{public}d, reason: %{public}s", isReboot, reason.c_str());
213
214    PowerErrors code;
215    if (isReboot) {
216        code = g_powerMgrClient.RebootDevice(reason);
217    } else {
218        code = g_powerMgrClient.ShutDownDevice(reason);
219    }
220    if (code != PowerErrors::ERR_OK) {
221        error.ThrowError(env, code);
222    }
223
224    return nullptr;
225}
226
227napi_value PowerNapi::SetPowerModeCallback(
228    napi_env& env, napi_value argv[], std::unique_ptr<AsyncCallbackInfo>& asyncInfo)
229{
230    bool isNum = NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number);
231    bool isFunc = NapiUtils::CheckValueType(env, argv[INDEX_1], napi_function);
232    if (!isNum || !isFunc) {
233        POWER_HILOGW(FEATURE_POWER_MODE, "isNum: %{public}d, isFunc: %{public}d", isNum, isFunc);
234        return asyncInfo->GetError().ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
235    }
236
237    asyncInfo->GetData().SetMode(env, argv[INDEX_0]);
238    asyncInfo->CreateCallback(env, argv[INDEX_1]);
239
240    AsyncWork(
241        env, asyncInfo, "SetPowerModeCallback",
242        [](napi_env env, void* data) {
243            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
244            RETURN_IF(asyncInfo == nullptr);
245            PowerErrors error = g_powerMgrClient.SetDeviceMode(asyncInfo->GetData().GetMode());
246            asyncInfo->GetError().Error(error);
247        },
248        [](napi_env env, napi_status status, void* data) {
249            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
250            RETURN_IF(asyncInfo == nullptr);
251            asyncInfo->CallFunction(env, nullptr);
252            asyncInfo->Release(env);
253            delete asyncInfo;
254        });
255    return nullptr;
256}
257
258napi_value PowerNapi::SetPowerModePromise(
259    napi_env& env, napi_value argv[], std::unique_ptr<AsyncCallbackInfo>& asyncInfo)
260{
261    bool isNum = NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number);
262    if (!isNum) {
263        POWER_HILOGW(FEATURE_POWER_MODE, "isNum: %{public}d", isNum);
264        return asyncInfo->GetError().ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
265    }
266    napi_value promise;
267    asyncInfo->CreatePromise(env, promise);
268    RETURN_IF_WITH_RET(promise == nullptr, nullptr);
269    asyncInfo->GetData().SetMode(env, argv[INDEX_0]);
270
271    AsyncWork(
272        env, asyncInfo, "SetPowerModePromise",
273        [](napi_env env, void* data) {
274            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
275            RETURN_IF(asyncInfo == nullptr);
276            PowerErrors error = g_powerMgrClient.SetDeviceMode(asyncInfo->GetData().GetMode());
277            asyncInfo->GetError().Error(error);
278        },
279        [](napi_env env, napi_status status, void* data) {
280            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
281            RETURN_IF(asyncInfo == nullptr);
282            if (asyncInfo->GetError().IsError()) {
283                napi_reject_deferred(env, asyncInfo->GetDeferred(), asyncInfo->GetError().GetNapiError(env));
284            } else {
285                napi_value undefined;
286                napi_get_undefined(env, &undefined);
287                napi_resolve_deferred(env, asyncInfo->GetDeferred(), undefined);
288            }
289            asyncInfo->Release(env);
290            delete asyncInfo;
291        });
292    return promise;
293}
294
295void PowerNapi::AsyncWork(napi_env& env, std::unique_ptr<AsyncCallbackInfo>& asyncInfo, const std::string& resourceName,
296    napi_async_execute_callback execute, napi_async_complete_callback complete)
297{
298    napi_value resource = nullptr;
299    napi_create_string_utf8(env, resourceName.c_str(), NAPI_AUTO_LENGTH, &resource);
300    napi_create_async_work(env, nullptr, resource, execute, complete,
301        reinterpret_cast<void*>(asyncInfo.get()), &(asyncInfo->GetAsyncWork()));
302    NAPI_CALL_RETURN_VOID(env, napi_queue_async_work_with_qos(env, asyncInfo->GetAsyncWork(), napi_qos_utility));
303    asyncInfo.release();
304}
305
306napi_value PowerNapi::SetScreenOffTime(napi_env env, napi_callback_info info)
307{
308    size_t argc = SET_SCREEN_OFFTIME_ARGC;
309    napi_value argv[argc];
310    NapiUtils::GetCallbackInfo(env, info, argc, argv);
311
312    NapiErrors error;
313    if (argc != SET_SCREEN_OFFTIME_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number)) {
314        POWER_HILOGE(FEATURE_WAKEUP, "check value type failed.");
315        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
316    }
317
318    int64_t timeout;
319    if (napi_ok != napi_get_value_int64(env, argv[INDEX_0], &timeout)) {
320        POWER_HILOGE(FEATURE_WAKEUP, "napi get int64 value failed.");
321        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
322    }
323
324    if (timeout == 0 || (timeout < 0 && timeout != RESTORE_DEFAULT_SCREENOFF_TIME)) {
325        POWER_HILOGE(FEATURE_WAKEUP, "timeout is not right.");
326        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
327    }
328
329    PowerErrors code;
330    if (timeout == RESTORE_DEFAULT_SCREENOFF_TIME) {
331        code = g_powerMgrClient.RestoreScreenOffTime();
332    } else {
333        code = g_powerMgrClient.OverrideScreenOffTime(timeout);
334    }
335    if (code != PowerErrors::ERR_OK) {
336        POWER_HILOGE(FEATURE_WAKEUP, "SetScreenOffTime failed.");
337        return error.ThrowError(env, code);
338    }
339    return nullptr;
340}
341
342napi_value PowerNapi::IsStandby(napi_env env, napi_callback_info info)
343{
344    bool isStandby = false;
345    PowerErrors code = g_powerMgrClient.IsStandby(isStandby);
346    if (code == PowerErrors::ERR_OK) {
347        napi_value napiValue;
348        NAPI_CALL(env, napi_get_boolean(env, isStandby, &napiValue));
349        return napiValue;
350    }
351    NapiErrors error;
352    return error.ThrowError(env, code);
353}
354} // namespace PowerMgr
355} // namespace OHOS
356