1 /*
2 * Copyright (c) 2022-2024 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 "js_feature_ability.h"
17
18 #include "distribute_constants.h"
19 #include "distribute_req_param.h"
20 #include "hilog_tag_wrapper.h"
21 #include "napi_common_util.h"
22 #include "js_error_utils.h"
23
24 namespace OHOS {
25 namespace AbilityRuntime {
26 using namespace OHOS::AppExecFwk;
27 const std::string RESULT_DATA_TAG = "resultData";
28
Finalizer(napi_env env,void * data,void * hint)29 void JsFeatureAbility::Finalizer(napi_env env, void* data, void* hint)
30 {
31 TAG_LOGI(AAFwkTag::FA, "finalizer called");
32 std::unique_ptr<JsFeatureAbility>(static_cast<JsFeatureAbility*>(data));
33 }
34
StartAbility(napi_env env,napi_callback_info info)35 napi_value JsFeatureAbility::StartAbility(napi_env env, napi_callback_info info)
36 {
37 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnStartAbility);
38 }
39
StartAbilityForResult(napi_env env,napi_callback_info info)40 napi_value JsFeatureAbility::StartAbilityForResult(napi_env env, napi_callback_info info)
41 {
42 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnStartAbilityForResult);
43 }
44
FinishWithResult(napi_env env,napi_callback_info info)45 napi_value JsFeatureAbility::FinishWithResult(napi_env env, napi_callback_info info)
46 {
47 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnFinishWithResult);
48 }
49
GetDeviceList(napi_env env,napi_callback_info info)50 napi_value JsFeatureAbility::GetDeviceList(napi_env env, napi_callback_info info)
51 {
52 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnGetDeviceList);
53 }
54
CallAbility(napi_env env,napi_callback_info info)55 napi_value JsFeatureAbility::CallAbility(napi_env env, napi_callback_info info)
56 {
57 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnCallAbility);
58 }
59
ContinueAbility(napi_env env,napi_callback_info info)60 napi_value JsFeatureAbility::ContinueAbility(napi_env env, napi_callback_info info)
61 {
62 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnContinueAbility);
63 }
64
SubscribeAbilityEvent(napi_env env,napi_callback_info info)65 napi_value JsFeatureAbility::SubscribeAbilityEvent(napi_env env, napi_callback_info info)
66 {
67 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnSubscribeAbilityEvent);
68 }
69
UnsubscribeAbilityEvent(napi_env env,napi_callback_info info)70 napi_value JsFeatureAbility::UnsubscribeAbilityEvent(napi_env env, napi_callback_info info)
71 {
72 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnUnsubscribeAbilityEvent);
73 }
74
SendMsg(napi_env env,napi_callback_info info)75 napi_value JsFeatureAbility::SendMsg(napi_env env, napi_callback_info info)
76 {
77 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnSendMsg);
78 }
79
SubscribeMsg(napi_env env,napi_callback_info info)80 napi_value JsFeatureAbility::SubscribeMsg(napi_env env, napi_callback_info info)
81 {
82 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnSubscribeMsg);
83 }
84
UnsubscribeMsg(napi_env env,napi_callback_info info)85 napi_value JsFeatureAbility::UnsubscribeMsg(napi_env env, napi_callback_info info)
86 {
87 GET_NAPI_INFO_AND_CALL(env, info, JsFeatureAbility, OnUnsubscribeMsg);
88 }
89
OnStartAbility(napi_env env,NapiCallbackInfo & info)90 napi_value JsFeatureAbility::OnStartAbility(napi_env env, NapiCallbackInfo& info)
91 {
92 TAG_LOGI(AAFwkTag::FA, "called");
93 if (info.argc != 1) {
94 TAG_LOGE(AAFwkTag::FA, "invalid argc");
95 return CreateJsUndefined(env);
96 }
97
98 Ability* ability = GetAbility(env);
99 if (ability == nullptr) {
100 TAG_LOGE(AAFwkTag::FA, "null ability");
101 return CreateJsUndefined(env);
102 }
103
104 DistributeReqParam requestParam;
105 if (!UnWrapRequestParams(env, info.argv[0], requestParam)) {
106 TAG_LOGE(AAFwkTag::FA, "unwrap request arguments failed");
107 ThrowInvalidParamError(env, "Parse param want failed, want must be Want.");
108 return CreateJsUndefined(env);
109 }
110
111 Want want = GetWant(requestParam);
112 NapiAsyncTask::CompleteCallback complete =
113 [want, ability](napi_env env, NapiAsyncTask &task, int32_t status) {
114 auto errcode = ability->StartAbility(want);
115 task.Resolve(env, JsFeatureAbility::CreateJsResult(env, errcode, "Start Ability failed."));
116 };
117
118 napi_value result = nullptr;
119 NapiAsyncTask::ScheduleHighQos("JSFeatureAbility::OnStartAbility",
120 env, CreateAsyncTaskWithLastParam(env, nullptr, nullptr, std::move(complete), &result));
121 return result;
122 }
123
OnStartAbilityForResult(napi_env env,NapiCallbackInfo & info)124 napi_value JsFeatureAbility::OnStartAbilityForResult(napi_env env, NapiCallbackInfo& info)
125 {
126 TAG_LOGI(AAFwkTag::FA, "called");
127 if (info.argc != 1) {
128 TAG_LOGE(AAFwkTag::FA, "Params not match");
129 return CreateJsUndefined(env);
130 }
131 Ability* ability = GetAbility(env);
132 if (ability == nullptr) {
133 TAG_LOGE(AAFwkTag::FA, "null ability");
134 return CreateJsUndefined(env);
135 }
136
137 DistributeReqParam requestParam;
138 if (!UnWrapRequestParams(env, info.argv[0], requestParam)) {
139 TAG_LOGE(AAFwkTag::FA, "unwrap request params failed");
140 return CreateJsUndefined(env);
141 }
142
143 Want want = GetWant(requestParam);
144 napi_value result = nullptr;
145 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
146
147 std::shared_ptr<NapiAsyncTask> asyncTask = std::move(uasyncTask);
148 FeatureAbilityTask task = [env, asyncTask](int resultCode, const AAFwk::Want& want) {
149 TAG_LOGI(AAFwkTag::FA, "asyncCallback start");
150 std::string data = want.GetStringParam(RESULT_DATA_TAG);
151 napi_value abilityResult = JsFeatureAbility::CreateJsResult(env, resultCode, data);
152 if (abilityResult == nullptr) {
153 TAG_LOGW(AAFwkTag::FA, "wrap abilityResult failed");
154 asyncTask->Reject(env, CreateJsError(env, 1, "failed to get result data!"));
155 } else {
156 asyncTask->Resolve(env, abilityResult);
157 }
158 TAG_LOGI(AAFwkTag::FA, "asyncCallback end");
159 };
160
161 want.SetParam(Want::PARAM_RESV_FOR_RESULT, true);
162 requestCode_ = (requestCode_ == INT_MAX) ? 0 : (requestCode_ + 1);
163 ability->StartFeatureAbilityForResult(want, requestCode_, std::move(task));
164
165 TAG_LOGI(AAFwkTag::FA, "end");
166 return result;
167 }
168
OnFinishWithResult(napi_env env,NapiCallbackInfo & info)169 napi_value JsFeatureAbility::OnFinishWithResult(napi_env env, NapiCallbackInfo& info)
170 {
171 TAG_LOGI(AAFwkTag::FA, "called");
172 if (info.argc != 1) {
173 TAG_LOGE(AAFwkTag::FA, "Params not match");
174 return CreateJsUndefined(env);
175 }
176
177 Ability *ability = GetAbility(env);
178 if (ability == nullptr) {
179 TAG_LOGE(AAFwkTag::FA, "null ability");
180 return CreateJsUndefined(env);
181 }
182
183 if (!IsTypeForNapiValue(env, info.argv[0], napi_object)) {
184 TAG_LOGE(AAFwkTag::FA, "invalid args[PARAM0]");
185 return CreateJsUndefined(env);
186 }
187
188 int32_t code = ERR_OK;
189 if (!UnwrapInt32ByPropertyName(env, info.argv[0], "code", code)) {
190 TAG_LOGE(AAFwkTag::FA, "invalid args[PARAM0]");
191 return CreateJsUndefined(env);
192 }
193
194 napi_value jsResultObj = GetPropertyValueByPropertyName(env, info.argv[0], "result", napi_object);
195 if (jsResultObj == nullptr) {
196 TAG_LOGE(AAFwkTag::FA, "get result failed");
197 return CreateJsUndefined(env);
198 }
199
200 napi_value globalValue = nullptr;
201 napi_get_global(env, &globalValue);
202 napi_value jsonValue;
203 napi_get_named_property(env, globalValue, "JSON", &jsonValue);
204 napi_value stringifyValue = nullptr;
205 napi_get_named_property(env, jsonValue, "stringify", &stringifyValue);
206 napi_value transValue = nullptr;
207 napi_call_function(env, jsonValue, stringifyValue, 1, &jsResultObj, &transValue);
208 std::string resultStr {};
209 resultStr = UnwrapStringFromJS(env, transValue, "");
210
211 TAG_LOGD(AAFwkTag::FA, "code: %{public}d, result:%{public}s", code, resultStr.c_str());
212 Want want;
213 want.SetParam(RESULT_DATA_TAG, resultStr);
214 ability->SetResult(code, want);
215
216 NapiAsyncTask::CompleteCallback complete =
217 [ability](napi_env env, NapiAsyncTask &task, int32_t status) {
218 auto errCode = ability->TerminateAbility();
219 task.Resolve(env, JsFeatureAbility::CreateJsResult(env, errCode, "FinishWithResult failed."));
220 };
221
222 napi_value result = nullptr;
223 NapiAsyncTask::ScheduleHighQos("JSFeatureAbility::OnFinishWithResult",
224 env, CreateAsyncTaskWithLastParam(env, nullptr, nullptr, std::move(complete), &result));
225 return result;
226 }
227
OnGetDeviceList(napi_env env,const NapiCallbackInfo & info)228 napi_value JsFeatureAbility::OnGetDeviceList(napi_env env, const NapiCallbackInfo& info)
229 {
230 TAG_LOGI(AAFwkTag::FA, "called");
231 return CreateJsUndefined(env);
232 }
233
OnCallAbility(napi_env env,const NapiCallbackInfo & info)234 napi_value JsFeatureAbility::OnCallAbility(napi_env env, const NapiCallbackInfo& info)
235 {
236 TAG_LOGI(AAFwkTag::FA, "called");
237 return CreateJsUndefined(env);
238 }
239
OnContinueAbility(napi_env env,const NapiCallbackInfo & info)240 napi_value JsFeatureAbility::OnContinueAbility(napi_env env, const NapiCallbackInfo& info)
241 {
242 TAG_LOGI(AAFwkTag::FA, "called");
243 return CreateJsUndefined(env);
244 }
245
OnSubscribeAbilityEvent(napi_env env,const NapiCallbackInfo & info)246 napi_value JsFeatureAbility::OnSubscribeAbilityEvent(napi_env env, const NapiCallbackInfo& info)
247 {
248 TAG_LOGI(AAFwkTag::FA, "called");
249 return CreateJsUndefined(env);
250 }
251
OnUnsubscribeAbilityEvent(napi_env env,const NapiCallbackInfo & info)252 napi_value JsFeatureAbility::OnUnsubscribeAbilityEvent(napi_env env, const NapiCallbackInfo& info)
253 {
254 TAG_LOGI(AAFwkTag::FA, "called");
255 return CreateJsUndefined(env);
256 }
257
OnSendMsg(napi_env env,const NapiCallbackInfo & info)258 napi_value JsFeatureAbility::OnSendMsg(napi_env env, const NapiCallbackInfo& info)
259 {
260 TAG_LOGI(AAFwkTag::FA, "called");
261 return CreateJsUndefined(env);
262 }
263
OnSubscribeMsg(napi_env env,const NapiCallbackInfo & info)264 napi_value JsFeatureAbility::OnSubscribeMsg(napi_env env, const NapiCallbackInfo& info)
265 {
266 TAG_LOGI(AAFwkTag::FA, "called");
267 return CreateJsUndefined(env);
268 }
269
OnUnsubscribeMsg(napi_env env,const NapiCallbackInfo & info)270 napi_value JsFeatureAbility::OnUnsubscribeMsg(napi_env env, const NapiCallbackInfo& info)
271 {
272 TAG_LOGI(AAFwkTag::FA, "called");
273 return CreateJsUndefined(env);
274 }
275
GetAbility(napi_env env)276 Ability* JsFeatureAbility::GetAbility(napi_env env)
277 {
278 napi_status ret;
279 napi_value global = nullptr;
280 const napi_extended_error_info *errorInfo = nullptr;
281 ret = napi_get_global(env, &global);
282 if (ret != napi_ok) {
283 napi_get_last_error_info(env, &errorInfo);
284 TAG_LOGE(AAFwkTag::FA, "get_global=%{public}d err:%{public}s", ret, errorInfo->error_message);
285 return nullptr;
286 }
287
288 napi_value abilityObj = nullptr;
289 ret = napi_get_named_property(env, global, "ability", &abilityObj);
290 if (ret != napi_ok) {
291 napi_get_last_error_info(env, &errorInfo);
292 TAG_LOGE(AAFwkTag::FA, "get_named_property=%{public}d err:%{public}s",
293 ret, errorInfo->error_message);
294 return nullptr;
295 }
296
297 Ability* ability = nullptr;
298 ret = napi_get_value_external(env, abilityObj, reinterpret_cast<void **>(&ability));
299 if (ret != napi_ok) {
300 napi_get_last_error_info(env, &errorInfo);
301 TAG_LOGE(AAFwkTag::FA, "get_value_external=%{public}d err:%{public}s",
302 ret, errorInfo->error_message);
303 return nullptr;
304 }
305
306 return ability;
307 }
308
GetWant(DistributeReqParam & requestParam)309 Want JsFeatureAbility::GetWant(DistributeReqParam &requestParam)
310 {
311 Want want;
312 Uri parseUri("");
313 if (CheckThenGetDeepLinkUri(requestParam, parseUri)) {
314 want.SetUri(parseUri);
315 want.SetAction(requestParam.GetAction());
316 for (auto entity : requestParam.GetEntities()) {
317 want.AddEntity(entity);
318 }
319 if (!requestParam.GetType().empty()) {
320 want.SetType(requestParam.GetType());
321 }
322 return want;
323 }
324
325 if (requestParam.GetDeviceType() == DistributeConstants::DEVICE_TYPE_DEFAULT) {
326 want.AddFlags(want.FLAG_ABILITYSLICE_MULTI_DEVICE);
327 want.AddFlags(want.FLAG_NOT_OHOS_COMPONENT);
328 want.SetDeviceId(requestParam.GetNetworkId());
329 }
330
331 if (requestParam.GetDeviceType() == DistributeConstants::DEVICE_TYPE_LOCAL) {
332 want.AddFlags(want.FLAG_NOT_OHOS_COMPONENT);
333 }
334 want.AddFlags(requestParam.GetFlag());
335
336 if (!requestParam.GetData().empty()) {
337 want.SetParam(DistributeConstants::START_ABILITY_PARAMS_KEY, requestParam.GetData());
338 }
339
340 if (!requestParam.GetUrl().empty()) {
341 want.SetParam(DistributeConstants::START_ABILITY_URL_KEY, requestParam.GetUrl());
342 want.SetUri(Uri(requestParam.GetUrl()));
343 }
344
345 if (!requestParam.GetType().empty()) {
346 want.SetType(requestParam.GetType());
347 }
348
349 GetExtraParams(requestParam, want);
350
351 if (!requestParam.GetBundleName().empty() && !requestParam.GetAbilityName().empty()) {
352 want.SetElementName(requestParam.GetNetworkId(), requestParam.GetBundleName(),
353 requestParam.GetAbilityName(), requestParam.GetModuleName());
354 } else {
355 want.SetAction(requestParam.GetAction());
356 for (auto entity : requestParam.GetEntities()) {
357 want.AddEntity(entity);
358 }
359 }
360
361 return want;
362 }
363
GetExtraParams(const DistributeReqParam & requestParam,const Want & want)364 void JsFeatureAbility::GetExtraParams(const DistributeReqParam &requestParam, const Want &want)
365 {
366 return;
367 }
368
CheckThenGetDeepLinkUri(const DistributeReqParam & requestParam,Uri & uri)369 bool JsFeatureAbility::CheckThenGetDeepLinkUri(const DistributeReqParam &requestParam, Uri &uri)
370 {
371 std::string url = requestParam.GetUrl();
372 std::string action = requestParam.GetAction();
373 if (url.empty() || action.empty()) {
374 return false;
375 }
376
377 if (action != DistributeConstants::DEEP_LINK_ACTION_NAME) {
378 return false;
379 }
380
381 uri = Uri(url);
382 if (uri.GetScheme().empty() || uri.GetHost().empty()) {
383 return false;
384 }
385
386 return true;
387 }
388
UnWrapRequestParams(napi_env env,napi_value param,DistributeReqParam & requestParam)389 bool JsFeatureAbility::UnWrapRequestParams(napi_env env, napi_value param, DistributeReqParam &requestParam)
390 {
391 TAG_LOGI(AAFwkTag::FA, "called");
392
393 if (!IsTypeForNapiValue(env, param, napi_object)) {
394 TAG_LOGI(AAFwkTag::FA, "invalid params");
395 return false;
396 }
397
398 std::string bundleName;
399 if (UnwrapStringByPropertyName(env, param, "bundleName", bundleName)) {
400 requestParam.SetBundleName(bundleName);
401 }
402
403 std::string abilityName;
404 if (UnwrapStringByPropertyName(env, param, "abilityName", abilityName)) {
405 requestParam.SetAbilityName(abilityName);
406 }
407
408 std::vector<std::string> entities;
409 if (UnwrapStringArrayByPropertyName(env, param, "entities", entities)) {
410 requestParam.SetEntities(entities);
411 }
412
413 std::string action;
414 if (UnwrapStringByPropertyName(env, param, "action", action)) {
415 requestParam.SetAction(action);
416 }
417
418 std::string networkId;
419 if (UnwrapStringByPropertyName(env, param, "networkId", networkId)) {
420 requestParam.SetNetWorkId(networkId);
421 }
422
423 int32_t deviceType = 0;
424 if (UnwrapInt32ByPropertyName(env, param, "deviceType", deviceType)) {
425 requestParam.SetDeviceType(deviceType);
426 }
427
428 std::string data;
429 if (UnwrapStringByPropertyName(env, param, "data", data)) {
430 requestParam.SetData(data);
431 }
432
433 int32_t flag = 0;
434 if (UnwrapInt32ByPropertyName(env, param, "flag", flag)) {
435 requestParam.SetFlag(flag);
436 }
437
438 std::string url;
439 if (UnwrapStringByPropertyName(env, param, "url", url)) {
440 requestParam.SetUrl(url);
441 }
442
443 return true;
444 }
445
CreateJsResult(napi_env env,int32_t errCode,const std::string & message)446 napi_value JsFeatureAbility::CreateJsResult(napi_env env, int32_t errCode, const std::string &message)
447 {
448 napi_value jsResult = nullptr;
449 napi_create_object(env, &jsResult);
450 napi_set_named_property(env, jsResult, "code", CreateJsNumber(env, errCode));
451 if (errCode == 0) {
452 napi_set_named_property(env, jsResult, "data", CreateJsUndefined(env));
453 } else {
454 napi_value dataVal = nullptr;
455 napi_create_string_utf8(env, message.c_str(), message.length(), &dataVal);
456 napi_set_named_property(env, jsResult, "data", dataVal);
457 }
458
459 return jsResult;
460 }
461
CreateJsFeatureAbility(napi_env env)462 napi_value JsFeatureAbility::CreateJsFeatureAbility(napi_env env)
463 {
464 napi_value object = nullptr;
465 napi_create_object(env, &object);
466
467 std::unique_ptr<JsFeatureAbility> jsFeatureAbility = std::make_unique<JsFeatureAbility>();
468 napi_wrap(env, object, jsFeatureAbility.release(), JsFeatureAbility::Finalizer, nullptr, nullptr);
469
470 const char *moduleName = "JsFeatureAbility";
471 BindNativeFunction(env, object, "startAbility", moduleName, JsFeatureAbility::StartAbility);
472 BindNativeFunction(env, object, "startAbilityForResult", moduleName, JsFeatureAbility::StartAbilityForResult);
473 BindNativeFunction(env, object, "finishWithResult", moduleName, JsFeatureAbility::FinishWithResult);
474 BindNativeFunction(env, object, "getDeviceList", moduleName, JsFeatureAbility::GetDeviceList);
475 BindNativeFunction(env, object, "callAbility", moduleName, JsFeatureAbility::CallAbility);
476 BindNativeFunction(env, object, "continueAbility", moduleName, JsFeatureAbility::ContinueAbility);
477 BindNativeFunction(env, object, "subscribeAbilityEvent", moduleName, JsFeatureAbility::SubscribeAbilityEvent);
478 BindNativeFunction(env, object, "unsubscribeAbilityEvent", moduleName,
479 JsFeatureAbility::UnsubscribeAbilityEvent);
480 BindNativeFunction(env, object, "sendMsg", moduleName, JsFeatureAbility::SendMsg);
481 BindNativeFunction(env, object, "subscribeMsg", moduleName, JsFeatureAbility::SubscribeMsg);
482 BindNativeFunction(env, object, "unsubscribeMsg", moduleName, JsFeatureAbility::UnsubscribeMsg);
483
484 return object;
485 }
486
JsFeatureAbilityInit(napi_env env,napi_value exports)487 napi_value JsFeatureAbilityInit(napi_env env, napi_value exports)
488 {
489 TAG_LOGD(AAFwkTag::FA, "called");
490 if (env == nullptr) {
491 TAG_LOGE(AAFwkTag::FA, "null env");
492 return nullptr;
493 }
494
495 napi_value global = nullptr;
496 napi_get_global(env, &global);
497 if (!CheckTypeForNapiValue(env, global, napi_object)) {
498 TAG_LOGE(AAFwkTag::FA, "not NativeObject");
499 return nullptr;
500 }
501
502 napi_set_named_property(env, global, "FeatureAbility", JsFeatureAbility::CreateJsFeatureAbility(env));
503
504 return global;
505 }
506 } // namespace AbilityRuntime
507 } // namespace OHOS
508