• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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