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