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