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