1 /*
2  * Copyright (C) 2021 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 #include <initializer_list>
16 #include <string>
17 
18 #include "napi_utils.h"
19 #include "time_common.h"
20 #include "time_service_client.h"
21 
22 namespace OHOS {
23 namespace MiscServicesNapi {
24 using namespace OHOS::MiscServices;
25 using namespace OHOS::MiscServices::Time;
26 typedef struct AsyncContext {
~AsyncContextOHOS::MiscServicesNapi::AsyncContext27     ~AsyncContext()
28     {
29         if (callbackRef != nullptr) {
30             napi_delete_reference(env, callbackRef);
31         }
32     }
33     napi_env env = nullptr;
34     napi_async_work work = nullptr;
35     int64_t time = INVALID_TIME;
36     std::string timeZone = "";
37     napi_deferred deferred = nullptr;
38     napi_ref callbackRef = nullptr;
39     bool isCallback = false;
40     bool isOK = false;
41     bool isNano = false;
42     int32_t errorCode = E_TIME_OK;
43     std::string message = "system error";
44 } AsyncContext;
45 
FreeWorkIfFail(napi_status status,napi_env env,AsyncContext * asyncContext)46 void FreeWorkIfFail(napi_status status, napi_env env, AsyncContext *asyncContext)
47 {
48     if (status != napi_ok) {
49         napi_delete_async_work(env, asyncContext->work);
50         delete asyncContext;
51         NAPI_CALL_RETURN_VOID(env, status);
52     }
53 }
54 
TimePaddingAsyncCallbackInfo(const napi_env & env,AsyncContext * & asynccallbackinfo,const napi_ref & callback,napi_value & promise)55 void TimePaddingAsyncCallbackInfo(const napi_env &env, AsyncContext *&asynccallbackinfo, const napi_ref &callback,
56     napi_value &promise)
57 {
58     if (callback) {
59         asynccallbackinfo->callbackRef = callback;
60         asynccallbackinfo->isCallback = true;
61     } else {
62         napi_deferred deferred = nullptr;
63         NAPI_CALL_RETURN_VOID(env, napi_create_promise(env, &deferred, &promise));
64         asynccallbackinfo->deferred = deferred;
65         asynccallbackinfo->isCallback = false;
66     }
67 }
68 
JSSystemTimeSetTime(napi_env env,napi_callback_info info)69 napi_value JSSystemTimeSetTime(napi_env env, napi_callback_info info)
70 {
71     size_t argc = SET_TIME_MAX_PARA;
72     napi_value argv[SET_TIME_MAX_PARA] = { 0 };
73     napi_value thisVar = nullptr;
74     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
75     int64_t times = INVALID_TIME;
76     napi_ref callback = nullptr;
77     if (NapiUtils::ParseParametersBySetTime(env, argv, argc, times, callback) == nullptr) {
78         return NapiUtils::GetUndefinedValue(env);
79     }
80     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env, .time = times };
81     if (!asyncContext) {
82         return NapiUtils::JSParaError(env, callback);
83     }
84     napi_value promise = nullptr;
85     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
86     napi_value resource = nullptr;
87     napi_create_string_utf8(env, "JSSystemTimeSetTime", NAPI_AUTO_LENGTH, &resource);
88     napi_create_async_work(
89         env, nullptr, resource,
90         [](napi_env env, void *data) {
91             AsyncContext *asyncContext = (AsyncContext *)data;
92             int32_t errorCode = E_TIME_OK;
93             asyncContext->isOK = TimeServiceClient::GetInstance()->SetTime(asyncContext->time, errorCode);
94             if (!asyncContext->isOK) {
95                 auto jsErrorCode = NapiUtils::ConvertErrorCode(errorCode);
96                 asyncContext->message = NapiUtils::GetErrorMessage(jsErrorCode);
97                 asyncContext->errorCode = JsErrorCode::ERROR;
98             }
99         },
100         [](napi_env env, napi_status status, void *data) {
101             AsyncContext *asyncContext = (AsyncContext *)data;
102             if (asyncContext == nullptr) {
103                 return;
104             }
105             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
106                 asyncContext->errorCode, asyncContext->message };
107             napi_value result = 0;
108             napi_get_null(env, &result);
109             NapiUtils::ReturnCallbackPromise(env, info, result);
110             napi_delete_async_work(env, asyncContext->work);
111             delete asyncContext;
112         },
113         (void *)asyncContext, &asyncContext->work);
114     bool isCallback = asyncContext->isCallback;
115     FreeWorkIfFail(
116         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
117     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
118 }
119 
JSSystemTimeSetTimeZone(napi_env env,napi_callback_info info)120 napi_value JSSystemTimeSetTimeZone(napi_env env, napi_callback_info info)
121 {
122     size_t argc = SET_TIMEZONE_MAX_PARA;
123     napi_value argv[SET_TIMEZONE_MAX_PARA] = { 0 };
124     napi_value thisVar = nullptr;
125     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
126     std::string timezoneId;
127     napi_ref callback = nullptr;
128     if (NapiUtils::ParseParametersBySetTimezone(env, argv, argc, timezoneId, callback) == nullptr) {
129         return NapiUtils::GetUndefinedValue(env);
130     }
131     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env, .timeZone = timezoneId };
132     if (!asyncContext) {
133         return NapiUtils::JSParaError(env, callback);
134     }
135     napi_value promise = nullptr;
136     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
137     napi_value resource = nullptr;
138     napi_create_string_utf8(env, "JSSystemTimeSetTimeZone", NAPI_AUTO_LENGTH, &resource);
139     napi_create_async_work(
140         env, nullptr, resource,
141         [](napi_env env, void *data) {
142             AsyncContext *asyncContext = (AsyncContext *)data;
143             int32_t errorCode = E_TIME_OK;
144             asyncContext->isOK = TimeServiceClient::GetInstance()->SetTimeZone(asyncContext->timeZone, errorCode);
145             if (!asyncContext->isOK) {
146                 auto jsErrorCode = NapiUtils::ConvertErrorCode(errorCode);
147                 asyncContext->message = NapiUtils::GetErrorMessage(jsErrorCode).c_str();
148                 asyncContext->errorCode = JsErrorCode::ERROR;
149             }
150         },
151         [](napi_env env, napi_status status, void *data) {
152             AsyncContext *asyncContext = (AsyncContext *)data;
153             if (asyncContext == nullptr) {
154                 return;
155             }
156             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
157                 asyncContext->errorCode, asyncContext->message };
158             napi_value result = 0;
159             napi_get_null(env, &result);
160             NapiUtils::ReturnCallbackPromise(env, info, result);
161             napi_delete_async_work(env, asyncContext->work);
162             delete asyncContext;
163         },
164         (void *)asyncContext, &asyncContext->work);
165     bool isCallback = asyncContext->isCallback;
166     FreeWorkIfFail(
167         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
168     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
169 }
170 
JSSystemTimeGetCurrentTime(napi_env env,napi_callback_info info)171 napi_value JSSystemTimeGetCurrentTime(napi_env env, napi_callback_info info)
172 {
173     size_t argc = SET_TIMEZONE_MAX_PARA;
174     napi_value argv[SET_TIMEZONE_MAX_PARA] = { 0 };
175     napi_value thisVar = nullptr;
176     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
177     napi_ref callback = nullptr;
178     bool isNano = false;
179     if (NapiUtils::ParseParametersGetNA(env, argv, argc, callback, &isNano) == nullptr) {
180         return NapiUtils::GetUndefinedValue(env);
181     }
182     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env };
183     if (!asyncContext) {
184         return NapiUtils::JSParaError(env, callback);
185     }
186     napi_value promise = nullptr;
187     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
188     asyncContext->isNano = isNano;
189     napi_value resource = nullptr;
190     napi_create_string_utf8(env, "JSSystemTimeGetCurrentTime", NAPI_AUTO_LENGTH, &resource);
191     napi_create_async_work(
192         env, nullptr, resource,
193         [](napi_env env, void *data) {
194             AsyncContext *asyncContext = (AsyncContext *)data;
195             if (asyncContext->isNano) {
196                 asyncContext->time = TimeServiceClient::GetInstance()->GetWallTimeNs();
197             } else {
198                 asyncContext->time = TimeServiceClient::GetInstance()->GetWallTimeMs();
199             }
200         },
201         [](napi_env env, napi_status status, void *data) {
202             AsyncContext *asyncContext = (AsyncContext *)data;
203             if (asyncContext == nullptr) {
204                 return;
205             }
206             if (asyncContext->time < 0) {
207                 asyncContext->errorCode = JsErrorCode::ERROR;
208             }
209             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
210                 asyncContext->errorCode, asyncContext->message };
211             napi_value result = nullptr;
212             napi_create_int64(env, asyncContext->time, &result);
213             NapiUtils::ReturnCallbackPromise(env, info, result);
214             napi_delete_async_work(env, asyncContext->work);
215             delete asyncContext;
216         },
217         (void *)asyncContext, &asyncContext->work);
218     bool isCallback = asyncContext->isCallback;
219     FreeWorkIfFail(
220         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
221     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
222 }
223 
JSSystemTimeGetRealActiveTime(napi_env env,napi_callback_info info)224 napi_value JSSystemTimeGetRealActiveTime(napi_env env, napi_callback_info info)
225 {
226     size_t argc = SET_TIMEZONE_MAX_PARA;
227     napi_value argv[SET_TIMEZONE_MAX_PARA] = { 0 };
228     napi_value thisVar = nullptr;
229     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
230     napi_ref callback = nullptr;
231     bool isNano = false;
232     if (NapiUtils::ParseParametersGetNA(env, argv, argc, callback, &isNano) == nullptr) {
233         return NapiUtils::GetUndefinedValue(env);
234     }
235     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env };
236     if (!asyncContext) {
237         return NapiUtils::JSParaError(env, callback);
238     }
239     napi_value promise = nullptr;
240     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
241     asyncContext->isNano = isNano;
242     napi_value resource = nullptr;
243     napi_create_string_utf8(env, "JSSystemTimeGetRealActiveTime", NAPI_AUTO_LENGTH, &resource);
244     napi_create_async_work(
245         env, nullptr, resource,
246         [](napi_env env, void *data) {
247             AsyncContext *asyncContext = (AsyncContext *)data;
248             if (asyncContext->isNano) {
249                 asyncContext->time = TimeServiceClient::GetInstance()->GetMonotonicTimeNs();
250             } else {
251                 asyncContext->time = TimeServiceClient::GetInstance()->GetMonotonicTimeMs();
252             }
253         },
254         [](napi_env env, napi_status status, void *data) {
255             AsyncContext *asyncContext = (AsyncContext *)data;
256             if (asyncContext == nullptr) {
257                 return;
258             }
259             if (asyncContext->time < 0) {
260                 asyncContext->errorCode = JsErrorCode::ERROR;
261             }
262             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
263                 asyncContext->errorCode, asyncContext->message };
264             napi_value result = nullptr;
265             napi_create_int64(env, asyncContext->time, &result);
266             NapiUtils::ReturnCallbackPromise(env, info, result);
267             napi_delete_async_work(env, asyncContext->work);
268             delete asyncContext;
269         },
270         (void *)asyncContext, &asyncContext->work);
271     bool isCallback = asyncContext->isCallback;
272     FreeWorkIfFail(
273         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
274     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
275 }
276 
JSSystemTimeGetRealTime(napi_env env,napi_callback_info info)277 napi_value JSSystemTimeGetRealTime(napi_env env, napi_callback_info info)
278 {
279     size_t argc = SET_TIMEZONE_MAX_PARA;
280     napi_value argv[SET_TIMEZONE_MAX_PARA] = { 0 };
281     napi_value thisVar = nullptr;
282     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
283     napi_ref callback = nullptr;
284     bool isNano = false;
285     if (NapiUtils::ParseParametersGetNA(env, argv, argc, callback, &isNano) == nullptr) {
286         return NapiUtils::GetUndefinedValue(env);
287     }
288     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env };
289     if (!asyncContext) {
290         return NapiUtils::JSParaError(env, callback);
291     }
292     napi_value promise = nullptr;
293     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
294     asyncContext->isNano = isNano;
295     napi_value resource = nullptr;
296     napi_create_string_utf8(env, "JSSystemTimeGetRealTime", NAPI_AUTO_LENGTH, &resource);
297     napi_create_async_work(
298         env, nullptr, resource,
299         [](napi_env env, void *data) {
300             AsyncContext *asyncContext = (AsyncContext *)data;
301             if (asyncContext->isNano) {
302                 asyncContext->time = TimeServiceClient::GetInstance()->GetBootTimeNs();
303             } else {
304                 asyncContext->time = TimeServiceClient::GetInstance()->GetBootTimeMs();
305             }
306         },
307         [](napi_env env, napi_status status, void *data) {
308             AsyncContext *asyncContext = (AsyncContext *)data;
309             if (asyncContext == nullptr) {
310                 return;
311             }
312             if (asyncContext->time < 0) {
313                 asyncContext->errorCode = JsErrorCode::ERROR;
314             }
315             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
316                 asyncContext->errorCode, asyncContext->message };
317             napi_value result = nullptr;
318             napi_create_int64(env, asyncContext->time, &result);
319             NapiUtils::ReturnCallbackPromise(env, info, result);
320             napi_delete_async_work(env, asyncContext->work);
321             delete asyncContext;
322         },
323         (void *)asyncContext, &asyncContext->work);
324     bool isCallback = asyncContext->isCallback;
325     FreeWorkIfFail(
326         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
327     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
328 }
329 
JSSystemTimeGetDate(napi_env env,napi_callback_info info)330 napi_value JSSystemTimeGetDate(napi_env env, napi_callback_info info)
331 {
332     size_t argc = SET_TIMEZONE_MAX_PARA;
333     napi_value argv[SET_TIMEZONE_MAX_PARA] = { 0 };
334     napi_value thisVar = nullptr;
335     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
336     napi_ref callback = nullptr;
337     if (NapiUtils::ParseParametersGet(env, argv, argc, callback) == nullptr) {
338         return NapiUtils::GetUndefinedValue(env);
339     }
340     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env };
341     if (!asyncContext) {
342         return NapiUtils::JSParaError(env, callback);
343     }
344     napi_value promise = nullptr;
345     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
346     napi_value resource = nullptr;
347     napi_create_string_utf8(env, "JSSystemTimeGetDate", NAPI_AUTO_LENGTH, &resource);
348     napi_create_async_work(
349         env, nullptr, resource,
350         [](napi_env env, void *data) {
351             AsyncContext *asyncContext = (AsyncContext *)data;
352             asyncContext->time = TimeServiceClient::GetInstance()->GetWallTimeMs();
353         },
354         [](napi_env env, napi_status status, void *data) {
355             AsyncContext *asyncContext = (AsyncContext *)data;
356             if (asyncContext == nullptr) {
357                 return;
358             }
359             if (asyncContext->time < 0) {
360                 asyncContext->errorCode = JsErrorCode::ERROR;
361             }
362             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
363                 asyncContext->errorCode, asyncContext->message };
364             napi_value result = nullptr;
365             napi_create_date(env, asyncContext->time, &result);
366             NapiUtils::ReturnCallbackPromise(env, info, result);
367             napi_delete_async_work(env, asyncContext->work);
368             delete asyncContext;
369         },
370         (void *)asyncContext, &asyncContext->work);
371     bool isCallback = asyncContext->isCallback;
372     FreeWorkIfFail(
373         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
374     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
375 }
376 
JSSystemTimeGetTimeZone(napi_env env,napi_callback_info info)377 napi_value JSSystemTimeGetTimeZone(napi_env env, napi_callback_info info)
378 {
379     size_t argc = SET_TIMEZONE_MAX_PARA;
380     napi_value argv[SET_TIMEZONE_MAX_PARA] = { 0 };
381     napi_value thisVar = nullptr;
382     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
383     napi_ref callback = nullptr;
384     if (NapiUtils::ParseParametersGet(env, argv, argc, callback) == nullptr) {
385         return NapiUtils::GetUndefinedValue(env);
386     }
387     AsyncContext *asyncContext = new (std::nothrow) AsyncContext{ .env = env };
388     if (!asyncContext) {
389         return NapiUtils::JSParaError(env, callback);
390     }
391     napi_value promise = nullptr;
392     TimePaddingAsyncCallbackInfo(env, asyncContext, callback, promise);
393     napi_value resource = nullptr;
394     napi_create_string_utf8(env, "JSSystemTimeGetTimeZone", NAPI_AUTO_LENGTH, &resource);
395     napi_create_async_work(
396         env, nullptr, resource,
397         [](napi_env env, void *data) {
398             AsyncContext *asyncContext = (AsyncContext *)data;
399             asyncContext->timeZone = TimeServiceClient::GetInstance()->GetTimeZone();
400         },
401         [](napi_env env, napi_status status, void *data) {
402             AsyncContext *asyncContext = (AsyncContext *)data;
403             if (asyncContext == nullptr) {
404                 return;
405             }
406             if (asyncContext->timeZone == "") {
407                 asyncContext->errorCode = JsErrorCode::ERROR;
408             }
409             CallbackPromiseInfo info{ asyncContext->callbackRef, asyncContext->deferred, asyncContext->isCallback,
410                 asyncContext->errorCode, asyncContext->message };
411             napi_value result = nullptr;
412             napi_create_string_utf8(env, asyncContext->timeZone.c_str(), asyncContext->timeZone.length(), &result);
413             NapiUtils::ReturnCallbackPromise(env, info, result);
414             napi_delete_async_work(env, asyncContext->work);
415             delete asyncContext;
416         },
417         (void *)asyncContext, &asyncContext->work);
418     bool isCallback = asyncContext->isCallback;
419     FreeWorkIfFail(
420         napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated), env, asyncContext);
421     return isCallback ? NapiUtils::NapiGetNull(env) : promise;
422 }
423 
424 EXTERN_C_START
SystemTimeExport(napi_env env,napi_value exports)425 napi_value SystemTimeExport(napi_env env, napi_value exports)
426 {
427     static napi_property_descriptor desc[] = {
428         DECLARE_NAPI_FUNCTION("setTime", JSSystemTimeSetTime),
429         DECLARE_NAPI_FUNCTION("setDate", JSSystemTimeSetTime),
430         DECLARE_NAPI_FUNCTION("setTimezone", JSSystemTimeSetTimeZone),
431         DECLARE_NAPI_FUNCTION("getCurrentTime", JSSystemTimeGetCurrentTime),
432         DECLARE_NAPI_FUNCTION("getRealActiveTime", JSSystemTimeGetRealActiveTime),
433         DECLARE_NAPI_FUNCTION("getRealTime", JSSystemTimeGetRealTime),
434         DECLARE_NAPI_FUNCTION("getDate", JSSystemTimeGetDate),
435         DECLARE_NAPI_FUNCTION("getTimezone", JSSystemTimeGetTimeZone),
436     };
437     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
438     return exports;
439 }
440 EXTERN_C_END
441 
442 static napi_module system_time_module = { .nm_version = 1,
443     .nm_flags = 0,
444     .nm_filename = nullptr,
445     .nm_register_func = SystemTimeExport,
446     .nm_modname = "systemTime",
447     .nm_priv = ((void *)0),
448     .reserved = { 0 } };
449 
SystemTimeRegister()450 extern "C" __attribute__((constructor)) void SystemTimeRegister()
451 {
452     napi_module_register(&system_time_module);
453 }
454 } // namespace MiscServicesNapi
455 } // namespace OHOS