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 "napi_system_timer.h"
17 
18 #include "securec.h"
19 #include "napi_utils.h"
20 #include "time_hilog.h"
21 #include "timer_type.h"
22 
23 using namespace OHOS::MiscServices;
24 
25 namespace OHOS {
26 namespace MiscServices {
27 namespace Time {
ITimerInfoInstance()28 ITimerInfoInstance::ITimerInfoInstance() : callbackInfo_{}
29 {
30 }
31 
~ITimerInfoInstance()32 ITimerInfoInstance::~ITimerInfoInstance()
33 {
34     auto *callback = new (std::nothrow) CallbackInfo(callbackInfo_.env, callbackInfo_.ref);
35     if (callback == nullptr) {
36         return;
37     }
38     ITimerInfoInstance::Call(callbackInfo_.env, reinterpret_cast<void *>(callback));
39 }
40 
Call(napi_env env,void * data)41 void ITimerInfoInstance::Call(napi_env env, void *data)
42 {
43     auto task = [data]() {
44         auto *callback = reinterpret_cast<CallbackInfo *>(data);
45         if (callback != nullptr) {
46             napi_delete_reference(callback->env, callback->ref);
47             delete callback;
48         }
49     };
50     auto ret = napi_send_event(env, task, napi_eprio_immediate);
51     if (ret != 0) {
52         delete static_cast<CallbackInfo *>(data);
53         TIME_HILOGE(TIME_MODULE_JS_NAPI, "napi_send_event failed retCode:%{public}d", ret);
54     }
55 }
56 
OnTrigger()57 void ITimerInfoInstance::OnTrigger()
58 {
59     if (callbackInfo_.ref == nullptr) {
60         return;
61     }
62     auto callbackInfo = callbackInfo_;
63 
64     auto task = [callbackInfo]() {
65         TIME_HILOGD(TIME_MODULE_JS_NAPI, "timerCallback success");
66         napi_value undefined = nullptr;
67         napi_get_undefined(callbackInfo.env, &undefined);
68         napi_value callback = nullptr;
69         napi_get_reference_value(callbackInfo.env, callbackInfo.ref, &callback);
70         napi_call_function(callbackInfo.env, undefined, callback, ARGC_ZERO, &undefined, &undefined);
71     };
72     auto ret = napi_send_event(callbackInfo.env, task, napi_eprio_immediate);
73     if (ret != 0) {
74         TIME_HILOGE(TIME_MODULE_JS_NAPI, "napi_send_event failed retCode:%{public}d", ret);
75     }
76 }
77 
SetCallbackInfo(const napi_env & env,const napi_ref & ref)78 void ITimerInfoInstance::SetCallbackInfo(const napi_env &env, const napi_ref &ref)
79 {
80     callbackInfo_.env = env;
81     callbackInfo_.ref = ref;
82 }
83 
SetType(const int & _type)84 void ITimerInfoInstance::SetType(const int &_type)
85 {
86     type = _type;
87 }
88 
SetRepeat(bool _repeat)89 void ITimerInfoInstance::SetRepeat(bool _repeat)
90 {
91     repeat = _repeat;
92 }
SetInterval(const uint64_t & _interval)93 void ITimerInfoInstance::SetInterval(const uint64_t &_interval)
94 {
95     interval = _interval;
96 }
SetWantAgent(std::shared_ptr<OHOS::AbilityRuntime::WantAgent::WantAgent> _wantAgent)97 void ITimerInfoInstance::SetWantAgent(std::shared_ptr<OHOS::AbilityRuntime::WantAgent::WantAgent> _wantAgent)
98 {
99     wantAgent = _wantAgent;
100 }
101 
SystemTimerInit(napi_env env,napi_value exports)102 napi_value NapiSystemTimer::SystemTimerInit(napi_env env, napi_value exports)
103 {
104     napi_property_descriptor descriptors[] = {
105         DECLARE_NAPI_STATIC_FUNCTION("createTimer", CreateTimer),
106         DECLARE_NAPI_STATIC_FUNCTION("startTimer", StartTimer),
107         DECLARE_NAPI_STATIC_FUNCTION("stopTimer", StopTimer),
108         DECLARE_NAPI_STATIC_FUNCTION("destroyTimer", DestroyTimer),
109         DECLARE_NAPI_PROPERTY("TIMER_TYPE_REALTIME", NapiUtils::CreateNapiNumber(env, 1 << TIMER_TYPE_REALTIME)),
110         DECLARE_NAPI_PROPERTY("TIMER_TYPE_WAKEUP", NapiUtils::CreateNapiNumber(env, 1 << TIMER_TYPE_WAKEUP)),
111         DECLARE_NAPI_PROPERTY("TIMER_TYPE_EXACT", NapiUtils::CreateNapiNumber(env, 1 << TIMER_TYPE_EXACT)),
112         DECLARE_NAPI_PROPERTY("TIMER_TYPE_IDLE", NapiUtils::CreateNapiNumber(env, 1 << TIMER_TYPE_IDLE)),
113     };
114 
115     napi_status status =
116         napi_define_properties(env, exports, sizeof(descriptors) / sizeof(napi_property_descriptor), descriptors);
117     if (status != napi_ok) {
118         TIME_HILOGE(TIME_MODULE_JS_NAPI, "define manager properties failed, status=%{public}d", status);
119         return NapiUtils::GetUndefinedValue(env);
120     }
121     return exports;
122 }
123 
124 std::map<std::string, napi_valuetype> PARA_NAPI_TYPE_MAP = {
125     { "type", napi_number },
126     { "repeat", napi_boolean },
127     { "interval", napi_number },
128     { "wantAgent", napi_object },
129     { "callback", napi_function },
130 };
131 
132 std::map<std::string, std::string> NAPI_TYPE_STRING_MAP = {
133     { "type", "number" },
134     { "repeat", "boolean" },
135     { "interval", "number" },
136     { "wantAgent", "object" },
137     { "callback", "function" },
138 };
139 
ParseTimerOptions(napi_env env,ContextBase * context,std::string paraType,const napi_value & value,std::shared_ptr<ITimerInfoInstance> & iTimerInfoInstance)140 void ParseTimerOptions(napi_env env, ContextBase *context, std::string paraType,
141     const napi_value &value, std::shared_ptr<ITimerInfoInstance> &iTimerInfoInstance)
142 {
143     napi_value result = nullptr;
144     OHOS::AbilityRuntime::WantAgent::WantAgent *wantAgent = nullptr;
145     napi_valuetype valueType = napi_undefined;
146     napi_get_named_property(env, value, paraType.c_str(), &result);
147     napi_typeof(env, result, &valueType);
148     CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, valueType == PARA_NAPI_TYPE_MAP[paraType],
149         paraType + ": incorrect parameter types, must be " + NAPI_TYPE_STRING_MAP[paraType],
150         JsErrorCode::PARAMETER_ERROR);
151     if (paraType == "type") {
152         int type = 0;
153         napi_get_value_int32(env, result, &type);
154         iTimerInfoInstance->SetType(type);
155     } else if (paraType == "repeat") {
156         bool repeat = false;
157         napi_get_value_bool(env, result, &repeat);
158         iTimerInfoInstance->SetRepeat(repeat);
159     } else if (paraType == "interval") {
160         int64_t interval = 0;
161         napi_get_value_int64(env, result, &interval);
162         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, interval >= 0,
163             "interval number must >= 0.", JsErrorCode::PARAMETER_ERROR);
164         iTimerInfoInstance->SetInterval((uint64_t)interval);
165     } else if (paraType == "wantAgent") {
166         napi_unwrap(env, result, (void **)&wantAgent);
167         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, wantAgent != nullptr,
168             "wantAgent can not be nullptr.", JsErrorCode::PARAMETER_ERROR);
169         std::shared_ptr<OHOS::AbilityRuntime::WantAgent::WantAgent> sWantAgent =
170             std::make_shared<OHOS::AbilityRuntime::WantAgent::WantAgent>(*wantAgent);
171         iTimerInfoInstance->SetWantAgent(sWantAgent);
172     } else if (paraType == "callback") {
173         napi_ref onTriggerCallback;
174         napi_create_reference(env, result, 1, &onTriggerCallback);
175         iTimerInfoInstance->SetCallbackInfo(env, onTriggerCallback);
176     }
177 }
178 
GetTimerOptions(const napi_env & env,ContextBase * context,const napi_value & value,std::shared_ptr<ITimerInfoInstance> & iTimerInfoInstance)179 void NapiSystemTimer::GetTimerOptions(const napi_env &env, ContextBase *context,
180     const napi_value &value, std::shared_ptr<ITimerInfoInstance> &iTimerInfoInstance)
181 {
182     bool hasProperty = false;
183 
184     // type: number
185     napi_has_named_property(env, value, "type", &hasProperty);
186     CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, hasProperty,
187         "Mandatory parameters are left unspecified, type expected.", JsErrorCode::PARAMETER_ERROR);
188     ParseTimerOptions(env, context, "type", value, iTimerInfoInstance);
189     CHECK_STATUS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, context->errMessage, JsErrorCode::PARAMETER_ERROR);
190 
191     // repeat: boolean
192     napi_has_named_property(env, value, "repeat", &hasProperty);
193     CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, hasProperty,
194         "Mandatory parameters are left unspecified, repeat expected.", JsErrorCode::PARAMETER_ERROR);
195     ParseTimerOptions(env, context, "repeat", value, iTimerInfoInstance);
196     CHECK_STATUS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, context->errMessage, JsErrorCode::PARAMETER_ERROR);
197 
198     // interval?: number
199     napi_has_named_property(env, value, "interval", &hasProperty);
200     if (hasProperty) {
201         ParseTimerOptions(env, context, "interval", value, iTimerInfoInstance);
202         CHECK_STATUS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, context->errMessage, JsErrorCode::PARAMETER_ERROR);
203     }
204 
205     // wantAgent?: WantAgent
206     napi_has_named_property(env, value, "wantAgent", &hasProperty);
207     if (hasProperty) {
208         ParseTimerOptions(env, context, "wantAgent", value, iTimerInfoInstance);
209         CHECK_STATUS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, context->errMessage, JsErrorCode::PARAMETER_ERROR);
210     }
211 
212     // callback?: () => void
213     napi_has_named_property(env, value, "callback", &hasProperty);
214     if (hasProperty) {
215         ParseTimerOptions(env, context, "callback", value, iTimerInfoInstance);
216         CHECK_STATUS_RETURN_VOID(TIME_MODULE_JS_NAPI, context, context->errMessage, JsErrorCode::PARAMETER_ERROR);
217     }
218 }
219 
CreateTimer(napi_env env,napi_callback_info info)220 napi_value NapiSystemTimer::CreateTimer(napi_env env, napi_callback_info info)
221 {
222     struct CreateTimerContext : public ContextBase {
223         uint64_t timerId = 0;
224         std::shared_ptr<ITimerInfoInstance> iTimerInfoInstance = std::make_shared<ITimerInfoInstance>();
225     };
226     CreateTimerContext *createTimerContext = new CreateTimerContext();
227     auto inputParser = [env, createTimerContext](size_t argc, napi_value *argv) {
228         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, createTimerContext, argc >= ARGC_ONE,
229             "Mandatory parameters are left unspecified", JsErrorCode::PARAMETER_ERROR);
230         GetTimerOptions(env, createTimerContext, argv[ARGV_FIRST], createTimerContext->iTimerInfoInstance);
231         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, createTimerContext, createTimerContext->status == napi_ok,
232             createTimerContext->errMessage, JsErrorCode::PARAMETER_ERROR);
233         createTimerContext->status = napi_ok;
234     };
235     createTimerContext->GetCbInfo(env, info, inputParser);
236     auto executor = [createTimerContext]() {
237         auto innerCode = TimeServiceClient::GetInstance()->CreateTimerV9(createTimerContext->iTimerInfoInstance,
238             createTimerContext->timerId);
239         if (innerCode != JsErrorCode::ERROR_OK) {
240             createTimerContext->errCode = innerCode;
241             createTimerContext->status = napi_generic_failure;
242         }
243     };
244     auto complete = [createTimerContext](napi_value &output) {
245         uint64_t timerId = static_cast<uint64_t>(createTimerContext->timerId);
246         createTimerContext->status = napi_create_int64(createTimerContext->env, timerId, &output);
247         CHECK_STATUS_RETURN_VOID(TIME_MODULE_JS_NAPI, createTimerContext,
248             "convert native object to javascript object failed", ERROR);
249     };
250     return NapiWork::AsyncEnqueue(env, createTimerContext, "SetTime", executor, complete);
251 }
252 
StartTimer(napi_env env,napi_callback_info info)253 napi_value NapiSystemTimer::StartTimer(napi_env env, napi_callback_info info)
254 {
255     struct StartTimerContext : public ContextBase {
256         uint64_t timerId = 0;
257         uint64_t triggerTime = 0;
258     };
259     StartTimerContext *startTimerContext = new StartTimerContext();
260     auto inputParser = [env, startTimerContext](size_t argc, napi_value *argv) {
261         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, startTimerContext, argc >= ARGC_TWO,
262             "Mandatory parameters are left unspecified", JsErrorCode::PARAMETER_ERROR);
263         int64_t timerId = 0;
264         startTimerContext->status = napi_get_value_int64(env, argv[ARGV_FIRST], &timerId);
265         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, startTimerContext, startTimerContext->status == napi_ok,
266             "The type of 'timerId' must be number", JsErrorCode::PARAMETER_ERROR);
267         startTimerContext->timerId = static_cast<uint64_t>(timerId);
268         int64_t triggerTime = 0;
269         startTimerContext->status = napi_get_value_int64(env, argv[ARGV_SECOND], &triggerTime);
270         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, startTimerContext, startTimerContext->status == napi_ok,
271             "The type of 'triggerTime' must be number", JsErrorCode::PARAMETER_ERROR);
272         startTimerContext->triggerTime = static_cast<uint64_t>(triggerTime);
273         startTimerContext->status = napi_ok;
274     };
275     startTimerContext->GetCbInfo(env, info, inputParser);
276     auto executor = [startTimerContext]() {
277         auto innerCode =
278             TimeServiceClient::GetInstance()->StartTimerV9(startTimerContext->timerId, startTimerContext->triggerTime);
279         if (innerCode != JsErrorCode::ERROR_OK) {
280             startTimerContext->errCode = innerCode;
281             startTimerContext->status = napi_generic_failure;
282         }
283     };
284     auto complete = [env](napi_value &output) { output = NapiUtils::GetUndefinedValue(env); };
285     return NapiWork::AsyncEnqueue(env, startTimerContext, "StartTimer", executor, complete);
286 }
287 
StopTimer(napi_env env,napi_callback_info info)288 napi_value NapiSystemTimer::StopTimer(napi_env env, napi_callback_info info)
289 {
290     struct StopTimerContext : public ContextBase {
291         uint64_t timerId = 0;
292     };
293     StopTimerContext *stopTimerContext = new StopTimerContext();
294     auto inputParser = [env, stopTimerContext](size_t argc, napi_value *argv) {
295         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, stopTimerContext, argc >= ARGC_ONE,
296             "Mandatory parameters are left unspecified", JsErrorCode::PARAMETER_ERROR);
297         int64_t timerId = 0;
298         stopTimerContext->status = napi_get_value_int64(env, argv[ARGV_FIRST], &timerId);
299         stopTimerContext->timerId = static_cast<uint64_t>(timerId);
300         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, stopTimerContext, stopTimerContext->status == napi_ok,
301             "The type of 'timerId' must be number", JsErrorCode::PARAMETER_ERROR);
302         stopTimerContext->status = napi_ok;
303     };
304     stopTimerContext->GetCbInfo(env, info, inputParser);
305     auto executor = [stopTimerContext]() {
306         auto innerCode = TimeServiceClient::GetInstance()->StopTimerV9(stopTimerContext->timerId);
307         if (innerCode != JsErrorCode::ERROR_OK) {
308             stopTimerContext->errCode = innerCode;
309             stopTimerContext->status = napi_generic_failure;
310         }
311     };
312     auto complete = [env](napi_value &output) { output = NapiUtils::GetUndefinedValue(env); };
313     return NapiWork::AsyncEnqueue(env, stopTimerContext, "StopTimer", executor, complete);
314 }
315 
DestroyTimer(napi_env env,napi_callback_info info)316 napi_value NapiSystemTimer::DestroyTimer(napi_env env, napi_callback_info info)
317 {
318     struct DestroyTimerContext : public ContextBase {
319         uint64_t timerId = 0;
320     };
321     DestroyTimerContext *destroyTimerContext = new DestroyTimerContext();
322     auto inputParser = [env, destroyTimerContext](size_t argc, napi_value *argv) {
323         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, destroyTimerContext, argc == ARGC_ONE,
324             "Mandatory parameters are left unspecified", JsErrorCode::PARAMETER_ERROR);
325         int64_t timerId = 0;
326         destroyTimerContext->status = napi_get_value_int64(env, argv[ARGV_FIRST], &timerId);
327         destroyTimerContext->timerId = static_cast<uint64_t>(timerId);
328         CHECK_ARGS_RETURN_VOID(TIME_MODULE_JS_NAPI, destroyTimerContext, destroyTimerContext->status == napi_ok,
329             "The type of 'timerId' must be number", JsErrorCode::PARAMETER_ERROR);
330         destroyTimerContext->status = napi_ok;
331     };
332     destroyTimerContext->GetCbInfo(env, info, inputParser);
333     auto executor = [destroyTimerContext]() {
334         auto innerCode = TimeServiceClient::GetInstance()->DestroyTimerV9(destroyTimerContext->timerId);
335         if (innerCode != ERROR_OK) {
336             destroyTimerContext->errCode = innerCode;
337             destroyTimerContext->status = napi_generic_failure;
338         }
339     };
340     auto complete = [env](napi_value &output) { output = NapiUtils::GetUndefinedValue(env); };
341 
342     return NapiWork::AsyncEnqueue(env, destroyTimerContext, "DestroyTimer", executor, complete);
343 }
344 } // namespace Time
345 } // namespace MiscServices
346 } // namespace OHOS