• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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_app_service_extension_context.h"
17 #include "js_service_extension_context.h"
18 #include <chrono>
19 #include <cstdint>
20 
21 #include "ability_manager_client.h"
22 #include "ability_runtime/js_caller_complex.h"
23 #include "hilog_tag_wrapper.h"
24 #include "js_extension_context.h"
25 #include "js_error_utils.h"
26 #include "js_data_struct_converter.h"
27 #include "js_runtime.h"
28 #include "js_runtime_utils.h"
29 #include "napi/native_api.h"
30 #include "napi_common_ability.h"
31 #include "napi_common_want.h"
32 #include "napi_common_util.h"
33 #include "napi_remote_object.h"
34 #include "napi_common_start_options.h"
35 #include "open_link_options.h"
36 #include "open_link/napi_common_open_link_options.h"
37 #include "start_options.h"
38 #include "hitrace_meter.h"
39 #include "uri.h"
40 
41 namespace OHOS {
42 namespace AbilityRuntime {
43 namespace {
44 constexpr int32_t INDEX_ZERO = 0;
45 constexpr int32_t INDEX_ONE = 1;
46 constexpr int32_t ERR_INVALID_VALUE = -1;
47 constexpr size_t ARGC_ZERO = 0;
48 constexpr size_t ARGC_ONE = 1;
49 constexpr size_t ARGC_TWO = 2;
50 
51 static std::mutex g_connectsMutex;
52 static std::map<ConnectionKey, sptr<JSAppServiceExtensionConnection>, key_compare> g_connects;
53 static int64_t g_serialNumber = 0;
54 
RemoveConnection(int64_t connectId)55 void RemoveConnection(int64_t connectId)
56 {
57     TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "enter");
58     std::lock_guard guard(g_connectsMutex);
59     auto item = std::find_if(g_connects.begin(), g_connects.end(),
60     [&connectId](const auto &obj) {
61         return connectId == obj.first.id;
62     });
63     if (item != g_connects.end()) {
64         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "remove conn ability exist");
65         if (item->second) {
66             item->second->RemoveConnectionObject();
67         }
68         g_connects.erase(item);
69     } else {
70         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "remove conn ability not exist");
71     }
72 }
73 
74 class JsAppServiceExtensionContext final {
75 public:
JsAppServiceExtensionContext(const std::shared_ptr<AppServiceExtensionContext> & context)76     explicit JsAppServiceExtensionContext(
77         const std::shared_ptr<AppServiceExtensionContext>& context) : context_(context) {}
78     ~JsAppServiceExtensionContext() = default;
79 
Finalizer(napi_env env,void * data,void * hint)80     static void Finalizer(napi_env env, void* data, void* hint)
81     {
82         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "called");
83         std::unique_ptr<JsAppServiceExtensionContext>(static_cast<JsAppServiceExtensionContext*>(data));
84     }
85 
ConnectAbility(napi_env env,napi_callback_info info)86     static napi_value ConnectAbility(napi_env env, napi_callback_info info)
87     {
88         GET_NAPI_INFO_AND_CALL(env, info, JsAppServiceExtensionContext, OnConnectAbility);
89     }
90 
DisconnectAbility(napi_env env,napi_callback_info info)91     static napi_value DisconnectAbility(napi_env env, napi_callback_info info)
92     {
93         GET_NAPI_INFO_AND_CALL(env, info, JsAppServiceExtensionContext, OnDisconnectAbility);
94     }
95 
StartAbility(napi_env env,napi_callback_info info)96     static napi_value StartAbility(napi_env env, napi_callback_info info)
97     {
98         GET_NAPI_INFO_AND_CALL(env, info, JsAppServiceExtensionContext, OnStartAbility);
99     }
100 
TerminateSelf(napi_env env,napi_callback_info info)101     static napi_value TerminateSelf(napi_env env, napi_callback_info info)
102     {
103         GET_NAPI_INFO_AND_CALL(env, info, JsAppServiceExtensionContext, OnTerminateSelf);
104     }
105 
106 private:
107     std::weak_ptr<AppServiceExtensionContext> context_;
108     sptr<JsFreeInstallObserver> freeInstallObserver_ = nullptr;
109 
OnTerminateSelf(napi_env env,NapiCallbackInfo & info)110     napi_value OnTerminateSelf(napi_env env, NapiCallbackInfo& info)
111     {
112         HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
113         TAG_LOGI(AAFwkTag::APP_SERVICE_EXT, "TerminateSelf");
114         auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
115         NapiAsyncTask::ExecuteCallback execute = [weak = context_, innerErrCode]() {
116             auto context = weak.lock();
117             if (!context) {
118                 TAG_LOGW(AAFwkTag::APP_SERVICE_EXT, "context released");
119                 *innerErrCode = static_cast<int32_t>(AAFwk::ERR_INVALID_CONTEXT);
120                 return;
121             }
122             *innerErrCode = context->TerminateSelf();
123         };
124         NapiAsyncTask::CompleteCallback complete =
125             [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
126                 if (*innerErrCode == ERR_OK) {
127                     task.Resolve(env, CreateJsUndefined(env));
128                 } else {
129                     task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
130                 }
131             };
132 
133         napi_value lastParam = (info.argc == ARGC_ZERO) ? nullptr : info.argv[INDEX_ZERO];
134         napi_value result = nullptr;
135         NapiAsyncTask::ScheduleHighQos("JsAppServiceExtensionContext::TerminateSelf",
136             env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
137         return result;
138     }
139 
CheckConnectionParam(napi_env env,napi_value value,sptr<JSAppServiceExtensionConnection> & connection,AAFwk::Want & want,int32_t accountId=-1) const140     bool CheckConnectionParam(napi_env env, napi_value value,
141         sptr<JSAppServiceExtensionConnection>& connection, AAFwk::Want& want, int32_t accountId = -1) const
142     {
143         if (!CheckTypeForNapiValue(env, value, napi_object)) {
144             TAG_LOGE(AAFwkTag::APP_SERVICE_EXT, "get connection obj failed");
145             return false;
146         }
147         connection->SetJsConnectionObject(value);
148         ConnectionKey key;
149         {
150             std::lock_guard guard(g_connectsMutex);
151             key.id = g_serialNumber;
152             key.want = want;
153             key.accountId = accountId;
154             connection->SetConnectionId(key.id);
155             g_connects.emplace(key, connection);
156             if (g_serialNumber < INT32_MAX) {
157                 g_serialNumber++;
158             } else {
159                 g_serialNumber = 0;
160             }
161         }
162         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "Unable to find connection, make new one");
163         return true;
164     }
165 
GetConnectAbilityExecFunc(const AAFwk::Want & want,sptr<JSAppServiceExtensionConnection> connection,int64_t connectId,std::shared_ptr<int> innerErrorCode)166     NapiAsyncTask::ExecuteCallback GetConnectAbilityExecFunc(const AAFwk::Want &want,
167         sptr<JSAppServiceExtensionConnection> connection, int64_t connectId, std::shared_ptr<int> innerErrorCode)
168     {
169         return [weak = context_, want, connection, connectId, innerErrorCode]() {
170             TAG_LOGI(AAFwkTag::APP_SERVICE_EXT, "Connect ability: %{public}d",
171                 static_cast<int32_t>(connectId));
172 
173             auto context = weak.lock();
174             if (!context) {
175                 TAG_LOGE(AAFwkTag::APP_SERVICE_EXT, "context released");
176                 *innerErrorCode = static_cast<int>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
177                 return;
178             }
179 
180             *innerErrorCode = context->ConnectAbility(want, connection);
181         };
182     }
183 
OnConnectAbility(napi_env env,NapiCallbackInfo & info)184     napi_value OnConnectAbility(napi_env env, NapiCallbackInfo& info)
185     {
186         HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
187         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "called");
188         // Check params count
189         if (info.argc < ARGC_TWO) {
190             TAG_LOGE(AAFwkTag::APP_SERVICE_EXT, "invalid argc");
191             ThrowTooFewParametersError(env);
192             return CreateJsUndefined(env);
193         }
194         // Unwrap want and connection
195         AAFwk::Want want;
196         sptr<JSAppServiceExtensionConnection> connection = new JSAppServiceExtensionConnection(env);
197         if (!AppExecFwk::UnwrapWant(env, info.argv[0], want)) {
198             ThrowInvalidParamError(env, "Parse param want failed, must be a Want.");
199             return CreateJsUndefined(env);
200         }
201         if (!CheckConnectionParam(env, info.argv[1], connection, want)) {
202             ThrowInvalidParamError(env, "Parse param options failed, must be a ConnectOptions.");
203             return CreateJsUndefined(env);
204         }
205         int64_t connectId = connection->GetConnectionId();
206         auto innerErrorCode = std::make_shared<int>(ERR_OK);
207         auto execute = GetConnectAbilityExecFunc(want, connection, connectId, innerErrorCode);
208         NapiAsyncTask::CompleteCallback complete = [connection, connectId, innerErrorCode](napi_env env,
209             NapiAsyncTask& task, int32_t status) {
210             if (*innerErrorCode == 0) {
211                 TAG_LOGI(AAFwkTag::APP_SERVICE_EXT, "Connect ability success");
212                 task.ResolveWithNoError(env, CreateJsUndefined(env));
213                 return;
214             }
215 
216             TAG_LOGE(AAFwkTag::APP_SERVICE_EXT, "Connect ability failed");
217             int32_t errcode = static_cast<int32_t>(AbilityRuntime::GetJsErrorCodeByNativeError(*innerErrorCode));
218             if (errcode) {
219                 connection->CallJsFailed(errcode);
220                 RemoveConnection(connectId);
221             }
222         };
223         napi_value result = nullptr;
224         NapiAsyncTask::ScheduleHighQos("JSAppServiceExtensionConnection::OnConnectAbility",
225             env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
226         return CreateJsValue(env, connectId);
227     }
228 
FindConnection(int64_t connectId,AAFwk::Want & want,sptr<JSAppServiceExtensionConnection> & connection,int32_t & accountId) const229     void FindConnection(int64_t connectId, AAFwk::Want& want, sptr<JSAppServiceExtensionConnection>& connection,
230         int32_t &accountId) const
231     {
232         TAG_LOGI(AAFwkTag::APP_SERVICE_EXT, "FindConnection connectId:%{public}" PRIu64 "", connectId);
233         std::lock_guard guard(g_connectsMutex);
234         auto item = std::find_if(g_connects.begin(),
235             g_connects.end(),
236             [&connectId](const auto &obj) {
237                 return connectId == obj.first.id;
238             });
239         if (item != g_connects.end()) {
240             // match id
241             want = item->first.want;
242             connection = item->second;
243             accountId = item->first.accountId;
244             TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "find conn ability exist");
245         }
246         return;
247     }
248 
OnDisconnectAbility(napi_env env,NapiCallbackInfo & info)249     napi_value OnDisconnectAbility(napi_env env, NapiCallbackInfo& info)
250     {
251         HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
252         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "called");
253         if (info.argc < ARGC_ONE) {
254             TAG_LOGE(AAFwkTag::APP_SERVICE_EXT, "invalid argc");
255             ThrowTooFewParametersError(env);
256             return CreateJsUndefined(env);
257         }
258         int64_t connectId = -1;
259         if (!AppExecFwk::UnwrapInt64FromJS2(env, info.argv[INDEX_ZERO], connectId)) {
260             ThrowInvalidParamError(env, "Parse param connection failed, must be a number.");
261             return CreateJsUndefined(env);
262         }
263         AAFwk::Want want;
264         sptr<JSAppServiceExtensionConnection> connection = nullptr;
265         int32_t accountId = -1;
266         FindConnection(connectId, want, connection, accountId);
267         // begin disconnect
268         auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
269         NapiAsyncTask::ExecuteCallback execute = [weak = context_, want, connection, accountId, innerErrCode]() {
270             auto context = weak.lock();
271             if (!context) {
272                 TAG_LOGW(AAFwkTag::APP_SERVICE_EXT, "context released");
273                 *innerErrCode = static_cast<int32_t>(AAFwk::ERR_INVALID_CONTEXT);
274                 return;
275             }
276             if (!connection) {
277                 TAG_LOGW(AAFwkTag::APP_SERVICE_EXT, "null connection");
278                 *innerErrCode = ERR_INVALID_VALUE;
279                 return;
280             }
281             TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "context->DisconnectAbility");
282             *innerErrCode = context->DisconnectAbility(want, connection, accountId);
283         };
284         NapiAsyncTask::CompleteCallback complete =
285             [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
286                 if (*innerErrCode == ERR_OK) {
287                     task.Resolve(env, CreateJsUndefined(env));
288                 } else {
289                     task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
290                 }
291             };
292         napi_value lastParam = (info.argc == ARGC_ONE) ? nullptr : info.argv[INDEX_ONE];
293         napi_value result = nullptr;
294         NapiAsyncTask::Schedule("JSAppServiceExtensionConnection::OnDisconnectAbility",
295             env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
296         return result;
297     }
298 
CheckStartAbilityInputParam(napi_env env,NapiCallbackInfo & info,AAFwk::Want & want,AAFwk::StartOptions & startOptions,size_t & unwrapArgc) const299     bool CheckStartAbilityInputParam(napi_env env, NapiCallbackInfo& info,
300         AAFwk::Want& want, AAFwk::StartOptions& startOptions, size_t& unwrapArgc) const
301     {
302         if (info.argc < ARGC_ONE) {
303             return false;
304         }
305         unwrapArgc = ARGC_ZERO;
306         if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) {
307             return false;
308         }
309         ++unwrapArgc;
310         if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) {
311             AppExecFwk::UnwrapStartOptions(env, info.argv[INDEX_ONE], startOptions);
312             unwrapArgc++;
313         }
314         return true;
315     }
316 
OnStartAbility(napi_env env,NapiCallbackInfo & info)317     napi_value OnStartAbility(napi_env env, NapiCallbackInfo& info)
318     {
319         HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
320         TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "StartAbility");
321 
322         size_t unwrapArgc = 0;
323         AAFwk::Want want;
324         AAFwk::StartOptions startOptions;
325         if (info.argc == ARGC_ZERO) {
326             TAG_LOGE(AAFwkTag::APP_SERVICE_EXT, "invalid arg");
327             ThrowTooFewParametersError(env);
328             return CreateJsUndefined(env);
329         }
330         if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) {
331             TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "Failed, input param type invalid");
332             ThrowInvalidParamError(env, "Parse param want failed, want must be Want");
333             return CreateJsUndefined(env);
334         }
335         auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
336         NapiAsyncTask::ExecuteCallback execute =
337             [weak = context_, want, startOptions, unwrapArgc, innerErrCode]() {
338             auto context = weak.lock();
339             if (!context) {
340                 TAG_LOGI(AAFwkTag::APP_SERVICE_EXT, "null context");
341                 *innerErrCode = static_cast<int>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
342                 return;
343             }
344             *innerErrCode = (unwrapArgc == ARGC_ONE) ? context->StartAbility(want) :
345                 context->StartAbility(want, startOptions);
346         };
347         NapiAsyncTask::CompleteCallback complete =
348             [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
349             if (*innerErrCode == ERR_OK) {
350                 task.ResolveWithNoError(env, CreateJsUndefined(env));
351             } else if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)) {
352                 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
353             } else {
354                 task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
355             }
356         };
357 
358         napi_value lastParam = nullptr;
359         napi_value result = nullptr;
360         NapiAsyncTask::ScheduleHighQos("JSAppServiceExtensionContext::OnStartAbility",
361             env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
362         return result;
363     }
364 };
365 } // namespace
366 
CreateJsAppServiceExtensionContext(napi_env env,std::shared_ptr<AppServiceExtensionContext> context)367 napi_value CreateJsAppServiceExtensionContext(napi_env env, std::shared_ptr<AppServiceExtensionContext> context)
368 {
369     TAG_LOGD(AAFwkTag::APP_SERVICE_EXT, "called");
370     std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo = nullptr;
371     if (context) {
372         abilityInfo = context->GetAbilityInfo();
373     }
374     napi_value object = CreateJsExtensionContext(env, context, abilityInfo);
375 
376     std::unique_ptr<JsAppServiceExtensionContext> jsContext = std::make_unique<JsAppServiceExtensionContext>(context);
377     napi_wrap(env, object, jsContext.release(), JsAppServiceExtensionContext::Finalizer, nullptr, nullptr);
378 
379     const char *moduleName = "JsAppServiceExtensionContext";
380     BindNativeFunction(
381         env, object, "connectServiceExtensionAbility", moduleName, JsAppServiceExtensionContext::ConnectAbility);
382     BindNativeFunction(
383         env, object, "disconnectServiceExtensionAbility", moduleName, JsAppServiceExtensionContext::DisconnectAbility);
384     BindNativeFunction(env, object, "startAbility", moduleName, JsAppServiceExtensionContext::StartAbility);
385     BindNativeFunction(
386         env, object, "terminateSelf", moduleName, JsAppServiceExtensionContext::TerminateSelf);
387     return object;
388 }
389 
390 }  // namespace AbilityRuntime
391 }  // namespace OHOS
392