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