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