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