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