1 /*
2 * Copyright (c) 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_ui_service_extension_context.h"
17
18 #include <chrono>
19 #include <cstdint>
20 #include "js_service_extension_context.h"
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 "start_options.h"
36 #include "hitrace_meter.h"
37 #include "js_free_install_observer.h"
38
39 namespace OHOS {
40 namespace AbilityRuntime {
41 namespace {
42 constexpr int32_t INDEX_ZERO = 0;
43 constexpr int32_t INDEX_ONE = 1;
44 constexpr int32_t INDEX_TWO = 2;
45 constexpr int32_t ERROR_CODE_ONE = 1;
46 constexpr int32_t ERROR_CODE_TWO = 2;
47 constexpr size_t ARGC_ZERO = 0;
48 constexpr size_t ARGC_ONE = 1;
49 constexpr size_t ARGC_TWO = 2;
50 constexpr size_t ARGC_THREE = 3;
51 constexpr int32_t INVALID_PARAM = static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
52
53 static std::mutex g_connectsMutex;
54 static std::map<ConnectionKey, sptr<JSUIServiceExtensionConnection>, key_compare> g_connects;
55 static int64_t g_serialNumber = 0;
56
57 class JSUIServiceExtensionContext final {
58 public:
JSUIServiceExtensionContext(const std::shared_ptr<UIServiceExtensionContext> & context)59 explicit JSUIServiceExtensionContext(
60 const std::shared_ptr<UIServiceExtensionContext>& context) : context_(context) {}
61 ~JSUIServiceExtensionContext() = default;
62
Finalizer(napi_env env,void * data,void * hint)63 static void Finalizer(napi_env env, void* data, void* hint)
64 {
65 TAG_LOGD(AAFwkTag::UISERVC_EXT, "JsAbilityContext::Finalizer is called");
66 std::unique_ptr<JSUIServiceExtensionContext>(static_cast<JSUIServiceExtensionContext*>(data));
67 }
68
StartAbility(napi_env env,napi_callback_info info)69 static napi_value StartAbility(napi_env env, napi_callback_info info)
70 {
71 GET_NAPI_INFO_AND_CALL(env, info, JSUIServiceExtensionContext, OnStartAbility);
72 }
73
TerminateSelf(napi_env env,napi_callback_info info)74 static napi_value TerminateSelf(napi_env env, napi_callback_info info)
75 {
76 GET_NAPI_INFO_AND_CALL(env, info, JSUIServiceExtensionContext, OnTerminateSelf);
77 }
78
StartAbilityByType(napi_env env,napi_callback_info info)79 static napi_value StartAbilityByType(napi_env env, napi_callback_info info)
80 {
81 GET_NAPI_INFO_AND_CALL(env, info, JSUIServiceExtensionContext, OnStartAbilityByType);
82 }
83
ConnectServiceExtensionAbility(napi_env env,napi_callback_info info)84 static napi_value ConnectServiceExtensionAbility(napi_env env, napi_callback_info info)
85 {
86 GET_NAPI_INFO_AND_CALL(env, info, JSUIServiceExtensionContext, OnConnectServiceExtensionAbility);
87 }
88
DisConnectServiceExtensionAbility(napi_env env,napi_callback_info info)89 static napi_value DisConnectServiceExtensionAbility(napi_env env, napi_callback_info info)
90 {
91 GET_NAPI_INFO_AND_CALL(env, info, JSUIServiceExtensionContext, OnDisConnectServiceExtensionAbility);
92 }
93
94 private:
95 std::weak_ptr<UIServiceExtensionContext> context_;
96
97 #ifdef SUPPORT_SCREEN
InitDisplayId(AAFwk::Want & want,AAFwk::StartOptions & startOptions,napi_env & env,NapiCallbackInfo & info)98 void InitDisplayId(AAFwk::Want &want, AAFwk::StartOptions &startOptions, napi_env &env, NapiCallbackInfo& info)
99 {
100 auto context = context_.lock();
101 if (!context) {
102 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context");
103 return;
104 }
105
106 auto window = context->GetWindow();
107 if (window == nullptr) {
108 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null window");
109 return;
110 }
111
112 int32_t displayId = 0;
113 if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[1], napi_object)
114 && AppExecFwk::UnwrapInt32ByPropertyName(env, info.argv[1], "displayId", displayId)) {
115 TAG_LOGI(AAFwkTag::UISERVC_EXT, "startOption displayId %{public}d", startOptions.GetDisplayID());
116 return;
117 }
118
119 TAG_LOGI(AAFwkTag::UISERVC_EXT, "window displayId %{public}" PRIu64, window->GetDisplayId());
120 startOptions.SetDisplayID(window->GetDisplayId());
121 }
122 #endif
123
OnStartAbility(napi_env env,NapiCallbackInfo & info)124 napi_value OnStartAbility(napi_env env, NapiCallbackInfo& info)
125 {
126 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
127 TAG_LOGI(AAFwkTag::UISERVC_EXT, "Call");
128 if (info.argc < ARGC_ONE) {
129 TAG_LOGE(AAFwkTag::UISERVC_EXT, "invalid argc");
130 ThrowTooFewParametersError(env);
131 return CreateJsUndefined(env);
132 }
133
134 size_t unwrapArgc = 0;
135 AAFwk::Want want;
136 AAFwk::StartOptions startOptions;
137 if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) {
138 ThrowInvalidParamError(env, "Parse param want failed, want must be Want.");
139 return CreateJsUndefined(env);
140 }
141 #ifdef SUPPORT_SCREEN
142 InitDisplayId(want, startOptions, env, info);
143 #endif
144 auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
145 NapiAsyncTask::ExecuteCallback execute = [weak = context_, want, startOptions, unwrapArgc, innerErrCode]() {
146 TAG_LOGD(AAFwkTag::UI_EXT, "JSUIServiceExtensionContext OnStartAbility");
147 auto context = weak.lock();
148 if (!context) {
149 TAG_LOGE(AAFwkTag::UI_EXT, "null context");
150 *innerErrCode = static_cast<int>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
151 return;
152 }
153 *innerErrCode = context->StartAbility(want, startOptions);
154 };
155 NapiAsyncTask::CompleteCallback complete =
156 [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
157 if (*innerErrCode == ERR_OK) {
158 task.ResolveWithNoError(env, CreateJsUndefined(env));
159 } else if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)) {
160 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
161 } else {
162 task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
163 }
164 };
165
166 napi_value lastParam = nullptr;
167 napi_value result = nullptr;
168 NapiAsyncTask::ScheduleHighQos("JSUIServiceExtensionContext::OnStartAbility",
169 env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
170 return result;
171 }
172
CheckStartAbilityInputParam(napi_env env,NapiCallbackInfo & info,AAFwk::Want & want,AAFwk::StartOptions & startOptions,size_t & unwrapArgc) const173 bool CheckStartAbilityInputParam(napi_env env, NapiCallbackInfo& info,
174 AAFwk::Want& want, AAFwk::StartOptions& startOptions, size_t& unwrapArgc) const
175 {
176 if (info.argc < ARGC_ONE) {
177 return false;
178 }
179 unwrapArgc = ARGC_ZERO;
180 // Check input want
181 if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) {
182 return false;
183 }
184 ++unwrapArgc;
185 if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[1], napi_object)) {
186 TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnStartAbility start options is used.");
187 AppExecFwk::UnwrapStartOptions(env, info.argv[1], startOptions);
188 unwrapArgc++;
189 }
190 return true;
191 }
192
OnTerminateSelf(napi_env env,NapiCallbackInfo & info)193 napi_value OnTerminateSelf(napi_env env, NapiCallbackInfo& info)
194 {
195 TAG_LOGI(AAFwkTag::UISERVC_EXT, "Call");
196 auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
197 NapiAsyncTask::ExecuteCallback execute = [weak = context_, innerErrCode]() {
198 auto context = weak.lock();
199 if (!context) {
200 TAG_LOGW(AAFwkTag::UISERVC_EXT, "null context");
201 *innerErrCode = static_cast<int>(ERROR_CODE_ONE);
202 return;
203 }
204 TAG_LOGD(AAFwkTag::UISERVC_EXT, "JSUIServiceExtensionContext OnTerminateSelf");
205 *innerErrCode= context->TerminateSelf();
206 };
207 NapiAsyncTask::CompleteCallback complete = [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
208 if (*innerErrCode == ERR_OK) {
209 task.Resolve(env, CreateJsUndefined(env));
210 } else if (*innerErrCode == ERROR_CODE_ONE) {
211 task.Reject(env, CreateJsError(env, *innerErrCode, "Context is released"));
212 } else {
213 task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
214 }
215 };
216 napi_value lastParam = nullptr;
217 napi_value result = nullptr;
218 NapiAsyncTask::ScheduleHighQos("JSUIServiceExtensionContext::OnTerminateSelf",
219 env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
220 return result;
221 }
222
OnStartAbilityByType(napi_env env,NapiCallbackInfo & info)223 napi_value OnStartAbilityByType(napi_env env, NapiCallbackInfo& info)
224 {
225 TAG_LOGI(AAFwkTag::UISERVC_EXT, "Call");
226 if (info.argc < ARGC_THREE) {
227 TAG_LOGE(AAFwkTag::UISERVC_EXT, "not enough params");
228 ThrowTooFewParametersError(env);
229 return CreateJsUndefined(env);
230 }
231
232 std::string type;
233 if (!ConvertFromJsValue(env, info.argv[INDEX_ZERO], type)) {
234 TAG_LOGE(AAFwkTag::UISERVC_EXT, "parse type failed");
235 ThrowError(env, INVALID_PARAM, "Incorrect parameter types, param type must be a string");
236 return CreateJsUndefined(env);
237 }
238
239 AAFwk::WantParams wantParam;
240 if (!AppExecFwk::UnwrapWantParams(env, info.argv[INDEX_ONE], wantParam)) {
241 TAG_LOGE(AAFwkTag::UISERVC_EXT, "parse wantParam failed");
242 ThrowError(env, INVALID_PARAM, "Parameter error. The type of \"WantParams\" must be array");
243 return CreateJsUndefined(env);
244 }
245
246 std::shared_ptr<JsUIExtensionCallback> callback = std::make_shared<JsUIExtensionCallback>(env);
247 callback->SetJsCallbackObject(info.argv[INDEX_TWO]);
248 auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
249 NapiAsyncTask::ExecuteCallback execute = [weak = context_, type, wantParam, callback, innerErrCode]() mutable {
250 auto context = weak.lock();
251 if (!context) {
252 TAG_LOGW(AAFwkTag::UISERVC_EXT, "null context");
253 *innerErrCode = static_cast<int>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
254 return;
255 }
256 TAG_LOGD(AAFwkTag::UISERVC_EXT, "JSUIServiceExtensionContext OnStartAbilityByType");
257 *innerErrCode = context->StartAbilityByType(type, wantParam, callback);
258 };
259 NapiAsyncTask::CompleteCallback complete =
260 [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) {
261 if (*innerErrCode == ERR_OK) {
262 task.ResolveWithNoError(env, CreateJsUndefined(env));
263 } else if (*innerErrCode == static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)) {
264 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
265 } else {
266 task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
267 }
268 };
269
270 napi_value lastParam = nullptr;
271 napi_value result = nullptr;
272 NapiAsyncTask::ScheduleHighQos("JSUIServiceExtensionContext::OnStartAbilityByType",
273 env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
274 return result;
275 }
276
CheckConnectionParam(napi_env env,napi_value value,sptr<JSUIServiceExtensionConnection> & connection,AAFwk::Want & want,int32_t accountId=-1) const277 bool CheckConnectionParam(napi_env env, napi_value value,
278 sptr<JSUIServiceExtensionConnection>& connection, AAFwk::Want& want, int32_t accountId = -1) const
279 {
280 if (!CheckTypeForNapiValue(env, value, napi_object)) {
281 TAG_LOGE(AAFwkTag::UISERVC_EXT, "get object failed");
282 return false;
283 }
284
285 if (connection == nullptr) {
286 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null connection");
287 return false;
288 }
289 connection->SetJsConnectionObject(value);
290 ConnectionKey key;
291 {
292 std::lock_guard guard(g_connectsMutex);
293 key.id = g_serialNumber;
294 key.want = want;
295 key.accountId = accountId;
296 connection->SetConnectionId(key.id);
297 g_connects.emplace(key, connection);
298 if (g_serialNumber < INT32_MAX) {
299 g_serialNumber++;
300 } else {
301 g_serialNumber = 0;
302 }
303 }
304 TAG_LOGD(AAFwkTag::UISERVC_EXT, "Unable to find connection, make new one");
305 return true;
306 }
307
GetConnectAbilityExecFunc(const AAFwk::Want & want,sptr<JSUIServiceExtensionConnection> connection,int64_t connectId,std::shared_ptr<int> innerErrorCode)308 NapiAsyncTask::ExecuteCallback GetConnectAbilityExecFunc(const AAFwk::Want &want,
309 sptr<JSUIServiceExtensionConnection> connection, int64_t connectId, std::shared_ptr<int> innerErrorCode)
310 {
311 return [weak = context_, want, connection, connectId, innerErrorCode]() {
312 if (innerErrorCode == nullptr) {
313 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null innerErrorCode");
314 return;
315 }
316
317 auto context = weak.lock();
318 if (!context) {
319 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context");
320 *innerErrorCode = static_cast<int>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
321 return;
322 }
323
324 *innerErrorCode = context->ConnectServiceExtensionAbility(want, connection);
325 };
326 }
327
FindConnection(AAFwk::Want & want,sptr<JSUIServiceExtensionConnection> & connection,int64_t & connectId,int32_t & accountId) const328 void FindConnection(AAFwk::Want& want, sptr<JSUIServiceExtensionConnection>& connection, int64_t& connectId,
329 int32_t &accountId) const
330 {
331 TAG_LOGI(AAFwkTag::UISERVC_EXT, "connection:%{public}d", static_cast<int32_t>(connectId));
332 std::lock_guard guard(g_connectsMutex);
333 auto item = std::find_if(g_connects.begin(),
334 g_connects.end(),
335 [&connectId](const auto &obj) {
336 return connectId == obj.first.id;
337 });
338 if (item != g_connects.end()) {
339 // match id
340 want = item->first.want;
341 connection = item->second;
342 accountId = item->first.accountId;
343 TAG_LOGD(AAFwkTag::UISERVC_EXT, "find conn ability exist");
344 }
345 return;
346 }
347
RemoveConnection(int64_t connectId)348 void RemoveConnection(int64_t connectId)
349 {
350 TAG_LOGD(AAFwkTag::UISERVC_EXT, "enter");
351 std::lock_guard guard(g_connectsMutex);
352 auto item = std::find_if(g_connects.begin(), g_connects.end(),
353 [&connectId](const auto &obj) {
354 return connectId == obj.first.id;
355 });
356 if (item != g_connects.end()) {
357 TAG_LOGD(AAFwkTag::UISERVC_EXT, "remove conn ability exist.");
358 if (item->second) {
359 item->second->RemoveConnectionObject();
360 }
361 g_connects.erase(item);
362 } else {
363 TAG_LOGD(AAFwkTag::UISERVC_EXT, "remove conn ability not exist.");
364 }
365 }
366
OnConnectServiceExtensionAbility(napi_env env,NapiCallbackInfo & info)367 napi_value OnConnectServiceExtensionAbility(napi_env env, NapiCallbackInfo& info)
368 {
369 TAG_LOGD(AAFwkTag::UISERVC_EXT, "Connect ServiceExtensionAbility called.");
370 // Check params count
371 if (info.argc < ARGC_TWO) {
372 TAG_LOGE(AAFwkTag::UISERVC_EXT, "not enough params");
373 ThrowTooFewParametersError(env);
374 return CreateJsUndefined(env);
375 }
376 // Unwrap want and connection
377 AAFwk::Want want;
378 sptr<JSUIServiceExtensionConnection> connection = new JSUIServiceExtensionConnection(env);
379 if (!AppExecFwk::UnwrapWant(env, info.argv[0], want)) {
380 ThrowInvalidParamError(env, "Parse param want failed, must be a Want.");
381 return CreateJsUndefined(env);
382 }
383 if (!CheckConnectionParam(env, info.argv[1], connection, want)) {
384 ThrowInvalidParamError(env, "Parse param options failed, must be a ConnectOptions.");
385 return CreateJsUndefined(env);
386 }
387 int64_t connectId = connection->GetConnectionId();
388 auto innerErrorCode = std::make_shared<int32_t>(ERR_OK);
389 auto execute = GetConnectAbilityExecFunc(want, connection, connectId, innerErrorCode);
390 NapiAsyncTask::CompleteCallback complete = [this, connection, connectId, innerErrorCode](napi_env env,
391 NapiAsyncTask& task, int32_t status) {
392 if (*innerErrorCode == 0) {
393 TAG_LOGI(AAFwkTag::UISERVC_EXT, "connect ability success");
394 task.ResolveWithNoError(env, CreateJsUndefined(env));
395 return;
396 }
397
398 TAG_LOGE(AAFwkTag::UISERVC_EXT, "connect ability failed");
399 int32_t errcode = static_cast<int32_t>(AbilityRuntime::GetJsErrorCodeByNativeError(*innerErrorCode));
400 if (errcode) {
401 connection->CallJsFailed(errcode);
402 this->RemoveConnection(connectId);
403 }
404 };
405 napi_value result = nullptr;
406 NapiAsyncTask::ScheduleHighQos("JSUIServiceExtensionContext::OnConnectServiceExtensionAbility",
407 env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result));
408 return CreateJsValue(env, connectId);
409 }
410
OnDisConnectServiceExtensionAbility(napi_env env,NapiCallbackInfo & info)411 napi_value OnDisConnectServiceExtensionAbility(napi_env env, NapiCallbackInfo& info)
412 {
413 TAG_LOGD(AAFwkTag::UISERVC_EXT, "DisConnect ServiceExtensionAbility start");
414 if (info.argc < ARGC_ONE) {
415 TAG_LOGE(AAFwkTag::UISERVC_EXT, "not enough params");
416 ThrowTooFewParametersError(env);
417 return CreateJsUndefined(env);
418 }
419 int64_t connectId = -1;
420 if (!AppExecFwk::UnwrapInt64FromJS2(env, info.argv[INDEX_ZERO], connectId)) {
421 ThrowInvalidParamError(env, "Parse param connection failed, must be a number.");
422 return CreateJsUndefined(env);
423 }
424 AAFwk::Want want;
425 sptr<JSUIServiceExtensionConnection> connection = nullptr;
426 int32_t accountId = -1;
427 FindConnection(want, connection, connectId, accountId);
428 // begin disconnect
429 auto innerErrCode = std::make_shared<ErrCode>(ERR_OK);
430 NapiAsyncTask::ExecuteCallback execute = [weak = context_, want, connection, accountId, innerErrCode]() {
431 auto context = weak.lock();
432 if (!context) {
433 TAG_LOGW(AAFwkTag::UISERVC_EXT, "null context");
434 *innerErrCode = ERROR_CODE_ONE;
435 return;
436 }
437 if (!connection) {
438 TAG_LOGW(AAFwkTag::UISERVC_EXT, "null connection");
439 *innerErrCode = ERROR_CODE_TWO;
440 return;
441 }
442 TAG_LOGD(AAFwkTag::UISERVC_EXT, "context->DisconnectServiceExtensionAbility");
443 *innerErrCode = context->DisConnectServiceExtensionAbility(want, connection, accountId);
444 };
445 NapiAsyncTask::CompleteCallback complete = [innerErrCode](
446 napi_env env, NapiAsyncTask& task, int32_t status) {
447 if (*innerErrCode == ERROR_CODE_ONE) {
448 task.Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
449 return;
450 }
451 if (*innerErrCode == ERROR_CODE_TWO) {
452 task.Reject(env, CreateJsError(env, ERROR_CODE_TWO, "not found connection"));
453 return;
454 }
455 if (*innerErrCode == ERR_OK) {
456 task.ResolveWithNoError(env, CreateJsUndefined(env));
457 } else {
458 task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode));
459 }
460 };
461 napi_value lastParam = (info.argc == ARGC_ONE) ? nullptr : info.argv[INDEX_ONE];
462 napi_value result = nullptr;
463 NapiAsyncTask::Schedule("JSUIServiceExtensionContext::OnDisConnectServiceExtensionAbility",
464 env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
465 return result;
466 }
467 };
468 } // namespace
469
CreateJsUIServiceExtensionContext(napi_env env,std::shared_ptr<UIServiceExtensionContext> context)470 napi_value CreateJsUIServiceExtensionContext(napi_env env, std::shared_ptr<UIServiceExtensionContext> context)
471 {
472 TAG_LOGD(AAFwkTag::UISERVC_EXT, "Call");
473 std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo = nullptr;
474 if (context) {
475 abilityInfo = context->GetAbilityInfo();
476 }
477 napi_value object = CreateJsExtensionContext(env, context, abilityInfo);
478
479 std::unique_ptr<JSUIServiceExtensionContext> jsUIContext =
480 std::make_unique<JSUIServiceExtensionContext>(context);
481 napi_wrap(env, object, jsUIContext.release(), JSUIServiceExtensionContext::Finalizer, nullptr, nullptr);
482
483 const char *moduleName = "JsUIServiceExtensionContext";
484 BindNativeFunction(env, object, "startAbility", moduleName, JSUIServiceExtensionContext::StartAbility);
485 BindNativeFunction(env, object, "terminateSelf", moduleName, JSUIServiceExtensionContext::TerminateSelf);
486 BindNativeFunction(env, object, "startAbilityByType", moduleName,
487 JSUIServiceExtensionContext::StartAbilityByType);
488 BindNativeFunction(env, object, "connectServiceExtensionAbility", moduleName,
489 JSUIServiceExtensionContext::ConnectServiceExtensionAbility);
490 BindNativeFunction(env, object, "disconnectServiceExtensionAbility", moduleName,
491 JSUIServiceExtensionContext::DisConnectServiceExtensionAbility);
492 return object;
493 }
494
JSUIServiceExtensionConnection(napi_env env)495 JSUIServiceExtensionConnection::JSUIServiceExtensionConnection(napi_env env) : env_(env) {}
496
~JSUIServiceExtensionConnection()497 JSUIServiceExtensionConnection::~JSUIServiceExtensionConnection()
498 {
499 if (jsConnectionObject_ == nullptr) {
500 return;
501 }
502
503 uv_loop_t *loop = nullptr;
504 napi_get_uv_event_loop(env_, &loop);
505 if (loop == nullptr) {
506 return;
507 }
508
509 uv_work_t *work = new (std::nothrow) uv_work_t;
510 if (work == nullptr) {
511 return;
512 }
513 work->data = reinterpret_cast<void *>(jsConnectionObject_.release());
514 int ret = uv_queue_work(loop, work, [](uv_work_t *work) {},
515 [](uv_work_t *work, int status) {
516 if (work == nullptr) {
517 return;
518 }
519 if (work->data == nullptr) {
520 delete work;
521 work = nullptr;
522 return;
523 }
524 delete reinterpret_cast<NativeReference *>(work->data);
525 work->data = nullptr;
526 delete work;
527 work = nullptr;
528 });
529 if (ret != 0) {
530 delete reinterpret_cast<NativeReference *>(work->data);
531 work->data = nullptr;
532 delete work;
533 work = nullptr;
534 }
535 }
536
SetConnectionId(int64_t id)537 void JSUIServiceExtensionConnection::SetConnectionId(int64_t id)
538 {
539 connectionId_ = id;
540 }
541
GetConnectionId()542 int64_t JSUIServiceExtensionConnection::GetConnectionId()
543 {
544 return connectionId_;
545 }
546
OnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)547 void JSUIServiceExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
548 const sptr<IRemoteObject> &remoteObject, int resultCode)
549 {
550 TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnAbilityConnectDone, resultCode:%{public}d", resultCode);
551 wptr<JSUIServiceExtensionConnection> connection = this;
552 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
553 ([connection, element, remoteObject, resultCode](napi_env env, NapiAsyncTask &task, int32_t status) {
554 sptr<JSUIServiceExtensionConnection> connectionSptr = connection.promote();
555 if (!connectionSptr) {
556 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null connectionSptr");
557 return;
558 }
559 connectionSptr->HandleOnAbilityConnectDone(element, remoteObject, resultCode);
560 });
561
562 napi_ref callback = nullptr;
563 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
564 NapiAsyncTask::Schedule("JSUIServiceExtensionConnection::OnAbilityConnectDone",
565 env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
566 }
567
HandleOnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)568 void JSUIServiceExtensionConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element,
569 const sptr<IRemoteObject> &remoteObject, int resultCode)
570 {
571 TAG_LOGD(AAFwkTag::UISERVC_EXT, "resultCode:%{public}d", resultCode);
572 // wrap ElementName
573 napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
574
575 // wrap RemoteObject
576 napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(env_, remoteObject);
577 napi_value argv[] = {napiElementName, napiRemoteObject};
578 if (jsConnectionObject_ == nullptr) {
579 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null jsConnectionObject_");
580 return;
581 }
582 napi_value obj = jsConnectionObject_->GetNapiValue();
583 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
584 TAG_LOGE(AAFwkTag::UISERVC_EXT, "get object error");
585 return;
586 }
587 napi_value methodOnConnect = nullptr;
588 napi_get_named_property(env_, obj, "onConnect", &methodOnConnect);
589 if (methodOnConnect == nullptr) {
590 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null methodOnConnect");
591 return;
592 }
593 napi_call_function(env_, obj, methodOnConnect, ARGC_TWO, argv, nullptr);
594 }
595
OnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)596 void JSUIServiceExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
597 {
598 TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone, resultCode:%{public}d", resultCode);
599 wptr<JSUIServiceExtensionConnection> connection = this;
600 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
601 ([connection, element, resultCode](napi_env env, NapiAsyncTask &task, int32_t status) {
602 sptr<JSUIServiceExtensionConnection> connectionSptr = connection.promote();
603 if (!connectionSptr) {
604 TAG_LOGI(AAFwkTag::UISERVC_EXT, "null connectionSptr");
605 return;
606 }
607 connectionSptr->HandleOnAbilityDisconnectDone(element, resultCode);
608 });
609 napi_ref callback = nullptr;
610 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
611 NapiAsyncTask::Schedule("JSUIServiceExtensionConnection::OnAbilityDisconnectDone",
612 env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
613 }
614
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)615 void JSUIServiceExtensionConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
616 int resultCode)
617 {
618 TAG_LOGD(AAFwkTag::UISERVC_EXT, "HandleOnAbilityDisconnectDone, resultCode:%{public}d", resultCode);
619 napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
620 napi_value argv[] = {napiElementName};
621 if (jsConnectionObject_ == nullptr) {
622 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null jsConnectionObject_");
623 return;
624 }
625 napi_value obj = jsConnectionObject_->GetNapiValue();
626 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
627 TAG_LOGE(AAFwkTag::UISERVC_EXT, "get object error");
628 return;
629 }
630
631 napi_value method = nullptr;
632 napi_get_named_property(env_, obj, "onDisconnect", &method);
633 if (method == nullptr) {
634 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null method");
635 return;
636 }
637
638 // release connect
639 {
640 std::lock_guard guard(g_connectsMutex);
641 TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone g_connects.size:%{public}zu", g_connects.size());
642 std::string bundleName = element.GetBundleName();
643 std::string abilityName = element.GetAbilityName();
644 auto item = std::find_if(g_connects.begin(),
645 g_connects.end(),
646 [bundleName, abilityName, connectionId = connectionId_](
647 const auto &obj) {
648 return (bundleName == obj.first.want.GetBundle()) &&
649 (abilityName == obj.first.want.GetElement().GetAbilityName()) &&
650 connectionId == obj.first.id;
651 });
652 if (item != g_connects.end()) {
653 // match bundlename && abilityname
654 g_connects.erase(item);
655 TAG_LOGD(
656 AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone erase g_connects.size:%{public}zu", g_connects.size());
657 }
658 }
659 napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr);
660 }
661
SetJsConnectionObject(napi_value jsConnectionObject)662 void JSUIServiceExtensionConnection::SetJsConnectionObject(napi_value jsConnectionObject)
663 {
664 napi_ref ref = nullptr;
665 napi_create_reference(env_, jsConnectionObject, 1, &ref);
666 jsConnectionObject_ = std::unique_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref));
667 }
668
RemoveConnectionObject()669 void JSUIServiceExtensionConnection::RemoveConnectionObject()
670 {
671 jsConnectionObject_.reset();
672 }
673
CallJsFailed(int32_t errorCode)674 void JSUIServiceExtensionConnection::CallJsFailed(int32_t errorCode)
675 {
676 TAG_LOGD(AAFwkTag::UISERVC_EXT, "CallJsFailed begin");
677 if (jsConnectionObject_ == nullptr) {
678 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null jsConnectionObject_");
679 return;
680 }
681 napi_value obj = jsConnectionObject_->GetNapiValue();
682 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
683 TAG_LOGE(AAFwkTag::UISERVC_EXT, "get object wrong");
684 return;
685 }
686
687 napi_value method = nullptr;
688 napi_get_named_property(env_, obj, "onFailed", &method);
689 if (method == nullptr) {
690 TAG_LOGE(AAFwkTag::UISERVC_EXT, "null method");
691 return;
692 }
693 napi_value argv[] = {CreateJsValue(env_, errorCode)};
694 napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr);
695 TAG_LOGD(AAFwkTag::UISERVC_EXT, "CallJsFailed end");
696 }
697 } // namespace AbilityRuntime
698 } // namespace OHOS
699