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 "bg_continuous_task_napi_module.h"
17
18 #include "ability.h"
19 #include "want_agent.h"
20 #include "napi_base_context.h"
21
22 #include "background_mode.h"
23 #include "background_task_mgr_helper.h"
24 #include "bgtaskmgr_inner_errors.h"
25 #include "continuous_task_log.h"
26 #include "continuous_task_param.h"
27
28 namespace OHOS {
29 namespace BackgroundTaskMgr {
30 namespace {
31 static constexpr uint32_t MAX_START_BG_RUNNING_PARAMS = 4;
32 static constexpr uint32_t MAX_STOP_BG_RUNNING_PARAMS = 2;
33 static constexpr uint32_t CALLBACK_RESULT_PARAMS_NUM = 2;
34 static constexpr int32_t BG_MODE_ID_BEGIN = 1;
35 static constexpr int32_t BG_MODE_ID_END = 9;
36 }
37
38 struct AsyncCallbackInfo {
39 napi_env env {nullptr};
40 napi_ref callback {nullptr};
41 napi_async_work asyncWork {nullptr};
42 napi_deferred deferred {nullptr};
43 std::shared_ptr<AbilityRuntime::AbilityContext> abilityContext {nullptr};
44 int32_t bgMode {0};
45 AbilityRuntime::WantAgent::WantAgent *wantAgent {nullptr};
46 int errCode {0};
47 };
48
WrapVoidToJS(napi_env env)49 napi_value WrapVoidToJS(napi_env env)
50 {
51 napi_value result = nullptr;
52 NAPI_CALL(env, napi_get_null(env, &result));
53 return result;
54 }
55
WrapUndefinedToJS(napi_env env)56 napi_value WrapUndefinedToJS(napi_env env)
57 {
58 napi_value result = nullptr;
59 NAPI_CALL(env, napi_get_undefined(env, &result));
60 return result;
61 }
62
GetCallbackErrorValue(napi_env env,int errCode)63 napi_value GetCallbackErrorValue(napi_env env, int errCode)
64 {
65 napi_value jsObject = nullptr;
66 napi_value jsValue = nullptr;
67 NAPI_CALL(env, napi_create_int32(env, errCode, &jsValue));
68 NAPI_CALL(env, napi_create_object(env, &jsObject));
69 NAPI_CALL(env, napi_set_named_property(env, jsObject, "code", jsValue));
70 return jsObject;
71 }
72
GetAbilityContext(const napi_env & env,const napi_value & value,std::shared_ptr<AbilityRuntime::AbilityContext> & abilityContext)73 napi_value GetAbilityContext(const napi_env &env, const napi_value &value,
74 std::shared_ptr<AbilityRuntime::AbilityContext> &abilityContext)
75 {
76 bool stageMode = false;
77 napi_status status = OHOS::AbilityRuntime::IsStageContext(env, value, stageMode);
78 BGTASK_LOGI("is stage mode: %{public}s", stageMode ? "true" : "false");
79
80 if (status != napi_ok || !stageMode) {
81 BGTASK_LOGI("Getting context with FA model");
82 auto ability = OHOS::AbilityRuntime::GetCurrentAbility(env);
83 if (!ability) {
84 BGTASK_LOGE("Failed to get native ability instance");
85 return nullptr;
86 }
87 abilityContext = ability->GetAbilityContext();
88 if (!abilityContext) {
89 BGTASK_LOGE("get FA model ability context failed");
90 return nullptr;
91 }
92 return WrapVoidToJS(env);
93 } else {
94 BGTASK_LOGI("Getting context with stage model");
95 auto context = AbilityRuntime::GetStageModeContext(env, value);
96 if (!context) {
97 BGTASK_LOGE("get context failed");
98 return nullptr;
99 }
100 abilityContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::AbilityContext>(context);
101 if (!abilityContext) {
102 BGTASK_LOGE("get Stage model ability context failed");
103 return nullptr;
104 }
105 return WrapVoidToJS(env);
106 }
107 }
108
StartBackgroundRunningExecuteCB(napi_env env,void * data)109 void StartBackgroundRunningExecuteCB(napi_env env, void *data)
110 {
111 BGTASK_LOGI("begin");
112 AsyncCallbackInfo *asyncCallbackInfo = (AsyncCallbackInfo *)data;
113 if (asyncCallbackInfo == nullptr) {
114 BGTASK_LOGE("asyncCallbackInfo is nullptr");
115 return;
116 }
117 if (asyncCallbackInfo->errCode != ERR_OK) {
118 BGTASK_LOGE("input params parse failed");
119 return;
120 }
121 if (asyncCallbackInfo->abilityContext == nullptr) {
122 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
123 BGTASK_LOGE("abilityContext is null");
124 return;
125 }
126 const std::shared_ptr<AppExecFwk::AbilityInfo> info = asyncCallbackInfo->abilityContext->GetAbilityInfo();
127 if (info == nullptr) {
128 BGTASK_LOGE("ability info is null");
129 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
130 return;
131 }
132
133 if (asyncCallbackInfo->wantAgent == nullptr) {
134 BGTASK_LOGE("wantAgent param is nullptr");
135 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
136 return;
137 }
138
139 sptr<IRemoteObject> token = asyncCallbackInfo->abilityContext->GetToken();
140 if (!token) {
141 BGTASK_LOGE("get ability token info failed");
142 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
143 return;
144 }
145
146 if (asyncCallbackInfo->bgMode < BG_MODE_ID_BEGIN || asyncCallbackInfo->bgMode > BG_MODE_ID_END) {
147 BGTASK_LOGE("request background mode id: %{public}d out of range", asyncCallbackInfo->bgMode);
148 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
149 return;
150 }
151
152 ContinuousTaskParam taskParam = ContinuousTaskParam(true, asyncCallbackInfo->bgMode,
153 std::make_shared<AbilityRuntime::WantAgent::WantAgent>(*asyncCallbackInfo->wantAgent), info->name, token);
154 asyncCallbackInfo->errCode = BackgroundTaskMgrHelper::RequestStartBackgroundRunning(taskParam);
155
156 BGTASK_LOGI("end");
157 }
158
CallbackCompletedCB(napi_env env,napi_status status,void * data)159 void CallbackCompletedCB(napi_env env, napi_status status, void *data)
160 {
161 BGTASK_LOGI("begin");
162 AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
163 napi_value callback = 0;
164 napi_value undefined = 0;
165 napi_value result[CALLBACK_RESULT_PARAMS_NUM] = {0};
166 napi_value callResult = 0;
167 napi_get_undefined(env, &undefined);
168 if (asyncCallbackInfo->errCode == ERR_OK) {
169 result[0] = WrapUndefinedToJS(env);
170 napi_create_int32(env, 0, &result[1]);
171 } else {
172 result[1] = WrapUndefinedToJS(env);
173 result[0] = GetCallbackErrorValue(env, asyncCallbackInfo->errCode);
174 }
175
176 napi_get_reference_value(env, asyncCallbackInfo->callback, &callback);
177 napi_call_function(env, undefined, callback, CALLBACK_RESULT_PARAMS_NUM, result, &callResult);
178
179 if (asyncCallbackInfo->callback != nullptr) {
180 napi_delete_reference(env, asyncCallbackInfo->callback);
181 }
182 napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
183 delete asyncCallbackInfo;
184 asyncCallbackInfo = nullptr;
185 BGTASK_LOGI("end");
186 }
187
PromiseCompletedCB(napi_env env,napi_status status,void * data)188 void PromiseCompletedCB(napi_env env, napi_status status, void *data)
189 {
190 BGTASK_LOGI("begin");
191 AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
192 napi_value result = 0;
193 if (asyncCallbackInfo->errCode == ERR_OK) {
194 napi_create_int32(env, 0, &result);
195 napi_resolve_deferred(env, asyncCallbackInfo->deferred, result);
196 } else {
197 result = GetCallbackErrorValue(env, asyncCallbackInfo->errCode);
198 napi_reject_deferred(env, asyncCallbackInfo->deferred, result);
199 }
200
201 napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
202 delete asyncCallbackInfo;
203 asyncCallbackInfo = nullptr;
204 BGTASK_LOGI("end");
205 }
206
StartBackgroundRunningAsync(napi_env env,napi_value * argv,const size_t argCallback,AsyncCallbackInfo * asyncCallbackInfo)207 napi_value StartBackgroundRunningAsync(
208 napi_env env, napi_value *argv, const size_t argCallback, AsyncCallbackInfo *asyncCallbackInfo)
209 {
210 BGTASK_LOGI("begin");
211 if (argv == nullptr || asyncCallbackInfo == nullptr) {
212 BGTASK_LOGE("param is nullptr");
213 return nullptr;
214 }
215 napi_value resourceName = 0;
216 NAPI_CALL(env, napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName));
217
218 napi_valuetype valuetype = napi_undefined;
219 NAPI_CALL(env, napi_typeof(env, argv[argCallback], &valuetype));
220 NAPI_ASSERT(env, valuetype == napi_function, "Wrong argument type. Function expected.");
221 NAPI_CALL(env, napi_create_reference(env, argv[argCallback], 1, &asyncCallbackInfo->callback));
222
223 NAPI_CALL(env, napi_create_async_work(env,
224 nullptr,
225 resourceName,
226 StartBackgroundRunningExecuteCB,
227 CallbackCompletedCB,
228 (void *)asyncCallbackInfo,
229 &asyncCallbackInfo->asyncWork));
230 NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));
231
232 BGTASK_LOGI("end");
233 return WrapVoidToJS(env);
234 }
235
StartBackgroundRunningPromise(napi_env env,AsyncCallbackInfo * asyncCallbackInfo)236 napi_value StartBackgroundRunningPromise(napi_env env, AsyncCallbackInfo *asyncCallbackInfo)
237 {
238 BGTASK_LOGI("begin");
239 if (asyncCallbackInfo == nullptr) {
240 BGTASK_LOGE("param is nullptr");
241 return nullptr;
242 }
243 napi_value resourceName;
244 NAPI_CALL(env, napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName));
245 napi_deferred deferred;
246 napi_value promise = 0;
247 NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
248 asyncCallbackInfo->deferred = deferred;
249
250 NAPI_CALL(env, napi_create_async_work(env,
251 nullptr,
252 resourceName,
253 StartBackgroundRunningExecuteCB,
254 PromiseCompletedCB,
255 (void *)asyncCallbackInfo,
256 &asyncCallbackInfo->asyncWork));
257 NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));
258 BGTASK_LOGI("end");
259 return promise;
260 }
261
GetBackgroundMode(const napi_env & env,const napi_value & value,int32_t & bgMode)262 napi_value GetBackgroundMode(const napi_env &env, const napi_value &value, int32_t &bgMode)
263 {
264 BGTASK_LOGI("begin");
265
266 napi_valuetype valuetype = napi_undefined;
267 NAPI_CALL(env, napi_typeof(env, value, &valuetype));
268 NAPI_ASSERT(env, valuetype == napi_number, "Wrong argument type. Number expected.");
269 napi_get_value_int32(env, value, &bgMode);
270
271 BGTASK_LOGI("get bgmode info: %{public}d", bgMode);
272 return WrapVoidToJS(env);
273 }
274
GetWantAgent(const napi_env & env,const napi_value & value,AbilityRuntime::WantAgent::WantAgent * & wantAgent)275 napi_value GetWantAgent(const napi_env &env, const napi_value &value, AbilityRuntime::WantAgent::WantAgent *&wantAgent)
276 {
277 BGTASK_LOGI("begin");
278 napi_valuetype valuetype = napi_undefined;
279 NAPI_CALL(env, napi_typeof(env, value, &valuetype));
280 NAPI_ASSERT(env, valuetype == napi_object, "Wrong argument type. Object expected.");
281 napi_unwrap(env, value, (void **)&wantAgent);
282
283 BGTASK_LOGI("end");
284 return WrapVoidToJS(env);
285 }
286
StartBackgroundRunning(napi_env env,napi_callback_info info)287 napi_value StartBackgroundRunning(napi_env env, napi_callback_info info)
288 {
289 BGTASK_LOGI("begin");
290 AsyncCallbackInfo *asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo {.env = env};
291 if (asyncCallbackInfo == nullptr) {
292 BGTASK_LOGE("asyncCallbackInfo == nullpter");
293 return WrapVoidToJS(env);
294 }
295
296 size_t argc = MAX_START_BG_RUNNING_PARAMS;
297 napi_value argv[MAX_START_BG_RUNNING_PARAMS] = {nullptr};
298 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
299 if (argc > MAX_START_BG_RUNNING_PARAMS) {
300 BGTASK_LOGE("wrong param nums");
301 delete asyncCallbackInfo;
302 asyncCallbackInfo = nullptr;
303 return nullptr;
304 }
305
306 // argv[0] : context : AbilityContext
307 if (GetAbilityContext(env, argv[0], asyncCallbackInfo->abilityContext) == nullptr) {
308 BGTASK_LOGE("Get ability context failed");
309 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
310 }
311
312 // argv[1] : bgMode : BackgroundMode
313 if (GetBackgroundMode(env, argv[1], asyncCallbackInfo->bgMode) == nullptr) {
314 BGTASK_LOGE("input bgmode param not number");
315 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
316 }
317
318 // argv[2] : wantAgent: WantAgent
319 if (GetWantAgent(env, argv[2], asyncCallbackInfo->wantAgent) == nullptr) {
320 BGTASK_LOGE("input wantAgent param is not object");
321 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
322 }
323
324 napi_value ret = 0;
325
326 if (argc == MAX_START_BG_RUNNING_PARAMS) {
327 ret = StartBackgroundRunningAsync(env, argv, MAX_START_BG_RUNNING_PARAMS - 1, asyncCallbackInfo);
328 } else {
329 ret = StartBackgroundRunningPromise(env, asyncCallbackInfo);
330 }
331
332 if (ret == nullptr) {
333 BGTASK_LOGE("ret is nullpter");
334 if (asyncCallbackInfo != nullptr) {
335 delete asyncCallbackInfo;
336 asyncCallbackInfo = nullptr;
337 }
338 ret = WrapVoidToJS(env);
339 }
340 BGTASK_LOGI("end");
341 return ret;
342 }
343
StopBackgroundRunningExecuteCB(napi_env env,void * data)344 void StopBackgroundRunningExecuteCB(napi_env env, void *data)
345 {
346 BGTASK_LOGI("begin");
347 AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
348 if (asyncCallbackInfo == nullptr) {
349 BGTASK_LOGE("asyncCallbackInfo is nullptr");
350 return;
351 }
352 if (asyncCallbackInfo->abilityContext == nullptr) {
353 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
354 BGTASK_LOGE("ability context is null");
355 return;
356 }
357 const std::shared_ptr<AppExecFwk::AbilityInfo> info = asyncCallbackInfo->abilityContext->GetAbilityInfo();
358 if (info == nullptr) {
359 BGTASK_LOGE("abilityInfo is null");
360 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
361 return;
362 }
363
364 sptr<IRemoteObject> token = asyncCallbackInfo->abilityContext->GetToken();
365 if (!token) {
366 BGTASK_LOGE("get ability token info failed");
367 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
368 return;
369 }
370 asyncCallbackInfo->errCode = BackgroundTaskMgrHelper::RequestStopBackgroundRunning(info->name, token);
371 }
372
StopBackgroundRunningAsync(napi_env env,napi_value * argv,const size_t argCallback,AsyncCallbackInfo * asyncCallbackInfo)373 napi_value StopBackgroundRunningAsync(napi_env env, napi_value *argv,
374 const size_t argCallback, AsyncCallbackInfo *asyncCallbackInfo)
375 {
376 BGTASK_LOGI("begin");
377 if (argv == nullptr || asyncCallbackInfo == nullptr) {
378 BGTASK_LOGE("param is nullptr");
379 return nullptr;
380 }
381 napi_value resourceName = 0;
382 NAPI_CALL(env, napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName));
383
384 napi_valuetype valuetype = napi_undefined;
385 NAPI_CALL(env, napi_typeof(env, argv[argCallback], &valuetype));
386 if (valuetype == napi_function) {
387 NAPI_CALL(env, napi_create_reference(env, argv[argCallback], 1, &asyncCallbackInfo->callback));
388 }
389
390 NAPI_CALL(env, napi_create_async_work(env,
391 nullptr,
392 resourceName,
393 StopBackgroundRunningExecuteCB,
394 CallbackCompletedCB,
395 (void *)asyncCallbackInfo,
396 &asyncCallbackInfo->asyncWork));
397 NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));
398 BGTASK_LOGI("end");
399 return WrapVoidToJS(env);
400 }
401
StopBackgroundRunningPromise(napi_env env,AsyncCallbackInfo * asyncCallbackInfo)402 napi_value StopBackgroundRunningPromise(napi_env env, AsyncCallbackInfo *asyncCallbackInfo)
403 {
404 BGTASK_LOGI("begin");
405 if (asyncCallbackInfo == nullptr) {
406 BGTASK_LOGE("param is nullptr");
407 return nullptr;
408 }
409 napi_value resourceName = 0;
410 napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
411 napi_deferred deferred;
412 napi_value promise = 0;
413 napi_create_promise(env, &deferred, &promise);
414
415 asyncCallbackInfo->deferred = deferred;
416
417 napi_create_async_work(
418 env,
419 nullptr,
420 resourceName,
421 StopBackgroundRunningExecuteCB,
422 PromiseCompletedCB,
423 (void *)asyncCallbackInfo,
424 &asyncCallbackInfo->asyncWork);
425 napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
426 BGTASK_LOGI("end");
427 return promise;
428 }
429
StopBackgroundRunning(napi_env env,napi_callback_info info)430 napi_value StopBackgroundRunning(napi_env env, napi_callback_info info)
431 {
432 BGTASK_LOGI("begin");
433 AsyncCallbackInfo *asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo {.env = env};
434 if (asyncCallbackInfo == nullptr) {
435 BGTASK_LOGE("asyncCallbackInfo is nullpter");
436 return WrapVoidToJS(env);
437 }
438
439 size_t argc = MAX_STOP_BG_RUNNING_PARAMS;
440 napi_value argv[MAX_STOP_BG_RUNNING_PARAMS] = {nullptr};
441
442 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
443 if (argc > MAX_STOP_BG_RUNNING_PARAMS) {
444 BGTASK_LOGE("wrong param nums");
445 delete asyncCallbackInfo;
446 asyncCallbackInfo = nullptr;
447 return nullptr;
448 }
449
450 // argv[0] : context : AbilityContext
451 if (GetAbilityContext(env, argv[0], asyncCallbackInfo->abilityContext) == nullptr) {
452 BGTASK_LOGE("Get ability context failed");
453 asyncCallbackInfo->errCode = ERR_BGTASK_INVALID_PARAM;
454 }
455
456 napi_value ret = 0;
457 if (argc == MAX_STOP_BG_RUNNING_PARAMS) {
458 ret = StopBackgroundRunningAsync(env, argv, MAX_STOP_BG_RUNNING_PARAMS - 1, asyncCallbackInfo);
459 } else {
460 ret = StopBackgroundRunningPromise(env, asyncCallbackInfo);
461 }
462
463 if (ret == nullptr) {
464 BGTASK_LOGE("ret is nullpter");
465 if (asyncCallbackInfo != nullptr) {
466 delete asyncCallbackInfo;
467 asyncCallbackInfo = nullptr;
468 }
469 ret = WrapVoidToJS(env);
470 }
471 BGTASK_LOGI("end");
472 return ret;
473 }
474 } // namespace BackgroundTaskMgr
475 }