1 /*
2 * Copyright (c) 2023-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_extension_context.h"
17
18 #include <cstdint>
19
20 #include "ability_manager_client.h"
21 #include "ability_manager_errors.h"
22 #include "event_handler.h"
23 #include "hilog_tag_wrapper.h"
24 #include "hitrace_meter.h"
25 #include "js_extension_context.h"
26 #include "js_error_utils.h"
27 #include "js_data_struct_converter.h"
28 #include "js_runtime.h"
29 #include "js_runtime_utils.h"
30 #include "js_uiservice_uiext_connection.h"
31 #include "js_ui_service_proxy.h"
32 #include "napi/native_api.h"
33 #include "napi_common_ability.h"
34 #include "napi_common_want.h"
35 #include "napi_common_util.h"
36 #include "napi_common_start_options.h"
37 #include "napi_remote_object.h"
38 #include "open_link_options.h"
39 #include "open_link/napi_common_open_link_options.h"
40 #include "start_options.h"
41 #include "uri.h"
42 #include "ui_extension_servicehost_stub_impl.h"
43 #include "ui_service_extension_connection_constants.h"
44
45 namespace OHOS {
46 namespace AbilityRuntime {
47 namespace {
48 constexpr int32_t INDEX_ZERO = 0;
49 constexpr int32_t INDEX_ONE = 1;
50 constexpr int32_t INDEX_TWO = 2;
51 constexpr size_t ARGC_ZERO = 0;
52 constexpr size_t ARGC_ONE = 1;
53 constexpr size_t ARGC_TWO = 2;
54 constexpr size_t ARGC_THREE = 3;
55
56 const std::string ATOMIC_SERVICE_PREFIX = "com.atomicservice.";
57 } // namespace
58
59 static std::map<UIExtensionConnectionKey, sptr<JSUIExtensionConnection>, key_compare> g_connects;
60 static int64_t g_serialNumber = 0;
RemoveConnection(int64_t connectId)61 void RemoveConnection(int64_t connectId)
62 {
63 auto item = std::find_if(g_connects.begin(), g_connects.end(),
64 [&connectId](const auto &obj) {
65 return connectId == obj.first.id;
66 });
67 if (item != g_connects.end()) {
68 TAG_LOGD(AAFwkTag::UI_EXT, "conn ability exists");
69 if (item->second) {
70 item->second->RemoveConnectionObject();
71 }
72 g_connects.erase(item);
73 } else {
74 TAG_LOGD(AAFwkTag::UI_EXT, "conn ability not exists");
75 }
76 }
77
FindConnection(AAFwk::Want & want,sptr<JSUIExtensionConnection> & connection,int64_t & connectId)78 void FindConnection(AAFwk::Want& want, sptr<JSUIExtensionConnection>& connection, int64_t& connectId)
79 {
80 TAG_LOGD(AAFwkTag::UI_EXT, "Disconnect ability enter, connection:%{public}" PRId64, connectId);
81 auto item = std::find_if(g_connects.begin(),
82 g_connects.end(),
83 [&connectId](const auto &obj) {
84 return connectId == obj.first.id;
85 });
86 if (item != g_connects.end()) {
87 // match id
88 want = item->first.want;
89 connection = item->second;
90 TAG_LOGD(AAFwkTag::UI_EXT, "find conn ability exist");
91 }
92 return;
93 }
94
CheckConnectionParam(napi_env env,napi_value value,sptr<JSUIExtensionConnection> & connection,AAFwk::Want & want)95 bool CheckConnectionParam(napi_env env, napi_value value, sptr<JSUIExtensionConnection>& connection, AAFwk::Want& want)
96 {
97 if (!CheckTypeForNapiValue(env, value, napi_object)) {
98 TAG_LOGE(AAFwkTag::UI_EXT, "Failed to get connection object");
99 return false;
100 }
101 connection->SetJsConnectionObject(value);
102 UIExtensionConnectionKey key;
103 key.id = g_serialNumber;
104 key.want = want;
105 connection->SetConnectionId(key.id);
106 g_connects.emplace(key, connection);
107 if (g_serialNumber < INT32_MAX) {
108 g_serialNumber++;
109 } else {
110 g_serialNumber = 0;
111 }
112 TAG_LOGD(AAFwkTag::UI_EXT, "not find connection, create a new connection");
113 return true;
114 }
115
Finalizer(napi_env env,void * data,void * hint)116 void JsUIExtensionContext::Finalizer(napi_env env, void* data, void* hint)
117 {
118 TAG_LOGD(AAFwkTag::UI_EXT, "called");
119 std::unique_ptr<JsUIExtensionContext>(static_cast<JsUIExtensionContext*>(data));
120 }
121
StartAbility(napi_env env,napi_callback_info info)122 napi_value JsUIExtensionContext::StartAbility(napi_env env, napi_callback_info info)
123 {
124 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnStartAbility);
125 }
126
OpenLink(napi_env env,napi_callback_info info)127 napi_value JsUIExtensionContext::OpenLink(napi_env env, napi_callback_info info)
128 {
129 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnOpenLink);
130 }
131
TerminateSelf(napi_env env,napi_callback_info info)132 napi_value JsUIExtensionContext::TerminateSelf(napi_env env, napi_callback_info info)
133 {
134 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnTerminateSelf);
135 }
136
StartAbilityForResult(napi_env env,napi_callback_info info)137 napi_value JsUIExtensionContext::StartAbilityForResult(napi_env env, napi_callback_info info)
138 {
139 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnStartAbilityForResult);
140 }
141
StartAbilityForResultAsCaller(napi_env env,napi_callback_info info)142 napi_value JsUIExtensionContext::StartAbilityForResultAsCaller(napi_env env, napi_callback_info info)
143 {
144 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnStartAbilityForResultAsCaller);
145 }
146
TerminateSelfWithResult(napi_env env,napi_callback_info info)147 napi_value JsUIExtensionContext::TerminateSelfWithResult(napi_env env, napi_callback_info info)
148 {
149 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnTerminateSelfWithResult);
150 }
151
ConnectAbility(napi_env env,napi_callback_info info)152 napi_value JsUIExtensionContext::ConnectAbility(napi_env env, napi_callback_info info)
153 {
154 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnConnectAbility);
155 }
156
DisconnectAbility(napi_env env,napi_callback_info info)157 napi_value JsUIExtensionContext::DisconnectAbility(napi_env env, napi_callback_info info)
158 {
159 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnDisconnectAbility);
160 }
161
ReportDrawnCompleted(napi_env env,napi_callback_info info)162 napi_value JsUIExtensionContext::ReportDrawnCompleted(napi_env env, napi_callback_info info)
163 {
164 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnReportDrawnCompleted);
165 }
166
OpenAtomicService(napi_env env,napi_callback_info info)167 napi_value JsUIExtensionContext::OpenAtomicService(napi_env env, napi_callback_info info)
168 {
169 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnOpenAtomicService);
170 }
171
StartUIServiceExtension(napi_env env,napi_callback_info info)172 napi_value JsUIExtensionContext::StartUIServiceExtension(napi_env env, napi_callback_info info)
173 {
174 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnStartUIServiceExtension);
175 }
176
ConnectUIServiceExtension(napi_env env,napi_callback_info info)177 napi_value JsUIExtensionContext::ConnectUIServiceExtension(napi_env env, napi_callback_info info)
178 {
179 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnConnectUIServiceExtension);
180 }
181
DisconnectUIServiceExtension(napi_env env,napi_callback_info info)182 napi_value JsUIExtensionContext::DisconnectUIServiceExtension(napi_env env, napi_callback_info info)
183 {
184 GET_NAPI_INFO_AND_CALL(env, info, JsUIExtensionContext, OnDisconnectUIServiceExtension);
185 }
186
OnStartAbility(napi_env env,NapiCallbackInfo & info)187 napi_value JsUIExtensionContext::OnStartAbility(napi_env env, NapiCallbackInfo& info)
188 {
189 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
190 TAG_LOGD(AAFwkTag::UI_EXT, "called");
191 if (info.argc < ARGC_ONE) {
192 TAG_LOGE(AAFwkTag::UI_EXT, "invalid argc");
193 ThrowTooFewParametersError(env);
194 return CreateJsUndefined(env);
195 }
196 size_t unwrapArgc = 0;
197 AAFwk::Want want;
198 AAFwk::StartOptions startOptions;
199 if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) {
200 TAG_LOGD(AAFwkTag::UI_EXT, "Failed, input param type invalid");
201 ThrowInvalidParamError(env, "Parse param want failed, want must be Want");
202 return CreateJsUndefined(env);
203 }
204 #ifdef SUPPORT_SCREEN
205 (unwrapArgc == INDEX_ONE) ? InitDisplayId(want) : InitDisplayId(want, startOptions, env, info);
206 #endif
207 NapiAsyncTask::CompleteCallback complete =
208 [weak = context_, want, startOptions, unwrapArgc](napi_env env, NapiAsyncTask& task, int32_t status) {
209 TAG_LOGD(AAFwkTag::UI_EXT, "startAbility begin");
210 auto context = weak.lock();
211 if (!context) {
212 TAG_LOGE(AAFwkTag::UI_EXT, "context is released");
213 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
214 return;
215 }
216 ErrCode innerErrorCode = (unwrapArgc == 1) ? context->StartAbility(want) :
217 context->StartAbility(want, startOptions);
218 if (innerErrorCode == 0) {
219 task.Resolve(env, CreateJsUndefined(env));
220 } else {
221 task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
222 }
223 };
224 napi_value lastParam = (info.argc == unwrapArgc) ? nullptr : info.argv[unwrapArgc];
225 napi_value result = nullptr;
226 NapiAsyncTask::ScheduleHighQos("JSUIExtensionContext OnStartAbility",
227 env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
228 return result;
229 }
230
CheckUrl(std::string & urlValue)231 static bool CheckUrl(std::string &urlValue)
232 {
233 if (urlValue.empty()) {
234 return false;
235 }
236 Uri uri = Uri(urlValue);
237 if (uri.GetScheme().empty() || uri.GetHost().empty()) {
238 return false;
239 }
240
241 return true;
242 }
243
CreateOpenLinkTask(const napi_env & env,const napi_value & lastParam,AAFwk::Want & want,int & requestCode)244 bool JsUIExtensionContext::CreateOpenLinkTask(const napi_env &env, const napi_value &lastParam,
245 AAFwk::Want &want, int &requestCode)
246 {
247 want.SetParam(Want::PARAM_RESV_FOR_RESULT, true);
248 napi_value result = nullptr;
249 std::unique_ptr<NapiAsyncTask> uasyncTask =
250 CreateAsyncTaskWithLastParam(env, lastParam, nullptr, nullptr, &result);
251 std::shared_ptr<NapiAsyncTask> asyncTask = std::move(uasyncTask);
252 RuntimeTask task = [env, asyncTask](int resultCode, const AAFwk::Want& want, bool isInner) {
253 TAG_LOGI(AAFwkTag::UI_EXT, "OnOpenLink async callback is begin");
254 HandleScope handleScope(env);
255 napi_value abilityResult = AppExecFwk::WrapAbilityResult(env, resultCode, want);
256 if (abilityResult == nullptr) {
257 TAG_LOGW(AAFwkTag::UI_EXT, "wrap abilityResult error");
258 asyncTask->Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INNER));
259 return;
260 }
261 if (isInner) {
262 asyncTask->Reject(env, CreateJsErrorByNativeErr(env, resultCode));
263 return;
264 }
265 asyncTask->ResolveWithNoError(env, abilityResult);
266 };
267 auto context = context_.lock();
268 if (context == nullptr) {
269 TAG_LOGW(AAFwkTag::UI_EXT, "context is released");
270 return false;
271 }
272 requestCode = context->GenerateCurRequestCode();
273 context->InsertResultCallbackTask(requestCode, std::move(task));
274 return true;
275 }
276
ParseOpenLinkParams(const napi_env & env,const NapiCallbackInfo & info,std::string & linkValue,AAFwk::OpenLinkOptions & openLinkOptions,AAFwk::Want & want)277 static bool ParseOpenLinkParams(const napi_env &env, const NapiCallbackInfo &info, std::string &linkValue,
278 AAFwk::OpenLinkOptions &openLinkOptions, AAFwk::Want &want)
279 {
280 if (info.argc != ARGC_THREE) {
281 TAG_LOGE(AAFwkTag::UI_EXT, "wrong arguments num");
282 return false;
283 }
284
285 if (!CheckTypeForNapiValue(env, info.argv[ARGC_ZERO], napi_string)) {
286 TAG_LOGE(AAFwkTag::UI_EXT, "link must be string");
287 return false;
288 }
289 if (!ConvertFromJsValue(env, info.argv[ARGC_ZERO], linkValue) || !CheckUrl(linkValue)) {
290 TAG_LOGE(AAFwkTag::UI_EXT, "link parameter invalid");
291 return false;
292 }
293
294 if (CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) {
295 TAG_LOGD(AAFwkTag::UI_EXT, "OpenLinkOptions is used");
296 if (!AppExecFwk::UnwrapOpenLinkOptions(env, info.argv[INDEX_ONE], openLinkOptions, want)) {
297 TAG_LOGE(AAFwkTag::UI_EXT, "openLinkOptions parse failed");
298 return false;
299 }
300 }
301
302 return true;
303 }
304
OnOpenLink(napi_env env,NapiCallbackInfo & info)305 napi_value JsUIExtensionContext::OnOpenLink(napi_env env, NapiCallbackInfo& info)
306 {
307 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
308 TAG_LOGD(AAFwkTag::UI_EXT, "OnOpenLink");
309
310 std::string linkValue("");
311 AAFwk::OpenLinkOptions openLinkOptions;
312 AAFwk::Want want;
313 want.SetParam(AppExecFwk::APP_LINKING_ONLY, false);
314
315 if (!ParseOpenLinkParams(env, info, linkValue, openLinkOptions, want)) {
316 TAG_LOGE(AAFwkTag::UI_EXT, "parse openLink arguments failed");
317 ThrowInvalidParamError(env,
318 "Parse param link or openLinkOptions failed, link must be string, openLinkOptions must be options.");
319 return CreateJsUndefined(env);
320 }
321
322 want.SetUri(linkValue);
323 std::string startTime = std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
324 system_clock::now().time_since_epoch()).count());
325 want.SetParam(Want::PARAM_RESV_START_TIME, startTime);
326
327 int requestCode = -1;
328 if (CheckTypeForNapiValue(env, info.argv[INDEX_TWO], napi_function)) {
329 TAG_LOGD(AAFwkTag::UI_EXT, "completionHandler is used");
330 CreateOpenLinkTask(env, info.argv[INDEX_TWO], want, requestCode);
331 }
332 #ifdef SUPPORT_SCREEN
333 InitDisplayId(want);
334 #endif
335 return OnOpenLinkInner(env, want, requestCode, startTime, linkValue);
336 }
337
OnOpenLinkInner(napi_env env,const AAFwk::Want & want,int requestCode,const std::string & startTime,const std::string & url)338 napi_value JsUIExtensionContext::OnOpenLinkInner(napi_env env, const AAFwk::Want& want,
339 int requestCode, const std::string& startTime, const std::string& url)
340 {
341 auto innerErrorCode = std::make_shared<int>(ERR_OK);
342 NapiAsyncTask::ExecuteCallback execute = [weak = context_, want, innerErrorCode, requestCode]() {
343 auto context = weak.lock();
344 if (!context) {
345 TAG_LOGW(AAFwkTag::UI_EXT, "context is released");
346 *innerErrorCode = static_cast<int>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
347 return;
348 }
349 *innerErrorCode = context->OpenLink(want, requestCode);
350 };
351
352 napi_value result = nullptr;
353 AddFreeInstallObserver(env, want, nullptr, &result, false, true);
354 NapiAsyncTask::CompleteCallback complete = [innerErrorCode, requestCode, startTime, url, weak = context_,
355 observer = freeInstallObserver_](napi_env env, NapiAsyncTask& task, int32_t status) {
356 if (*innerErrorCode == 0) {
357 TAG_LOGI(AAFwkTag::UI_EXT, "OpenLink succeeded");
358 return;
359 }
360 auto context = weak.lock();
361 if (!context) {
362 TAG_LOGW(AAFwkTag::CONTEXT, "null context");
363 return;
364 }
365 if (observer != nullptr) {
366 if (*innerErrorCode == AAFwk::ERR_OPEN_LINK_START_ABILITY_DEFAULT_OK) {
367 TAG_LOGI(AAFwkTag::UI_EXT, "start ability by default succeeded");
368 observer->OnInstallFinishedByUrl(startTime, url, ERR_OK);
369 return;
370 }
371 observer->OnInstallFinishedByUrl(startTime, url, *innerErrorCode);
372 }
373 context->RemoveResultCallbackTask(requestCode);
374 };
375
376 NapiAsyncTask::ScheduleHighQos("JsUIExtensionContext::OnOpenLink", env,
377 CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), nullptr));
378
379 return result;
380 }
381
OnTerminateSelf(napi_env env,NapiCallbackInfo & info)382 napi_value JsUIExtensionContext::OnTerminateSelf(napi_env env, NapiCallbackInfo& info)
383 {
384 TAG_LOGI(AAFwkTag::UI_EXT, "called");
385 NapiAsyncTask::CompleteCallback complete =
386 [weak = context_](napi_env env, NapiAsyncTask& task, int32_t status) {
387 auto context = weak.lock();
388 if (!context) {
389 TAG_LOGE(AAFwkTag::UI_EXT, "context is released");
390 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
391 return;
392 }
393
394 ErrCode innerErrorCode = context->TerminateSelf();
395 if (innerErrorCode == 0) {
396 task.ResolveWithNoError(env, CreateJsUndefined(env));
397 } else {
398 task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
399 }
400 };
401
402 napi_value lastParam = (info.argc == ARGC_ZERO) ? nullptr : info.argv[INDEX_ZERO];
403 napi_value result = nullptr;
404 NapiAsyncTask::ScheduleHighQos("JSUIExtensionContext OnTerminateSelf",
405 env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
406 return result;
407 }
408
OnStartAbilityForResult(napi_env env,NapiCallbackInfo & info)409 napi_value JsUIExtensionContext::OnStartAbilityForResult(napi_env env, NapiCallbackInfo& info)
410 {
411 TAG_LOGD(AAFwkTag::UI_EXT, "called");
412 if (info.argc == ARGC_ZERO) {
413 ThrowTooFewParametersError(env);
414 return CreateJsUndefined(env);
415 }
416 size_t unwrapArgc = 0;
417 AAFwk::Want want;
418 AAFwk::StartOptions startOptions;
419 if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) {
420 ThrowInvalidParamError(env, "Parse param want failed, want must be Want.");
421 TAG_LOGD(AAFwkTag::UI_EXT, "input param type invalid");
422 return CreateJsUndefined(env);
423 }
424 #ifdef SUPPORT_SCREEN
425 (unwrapArgc == INDEX_ONE) ? InitDisplayId(want) : InitDisplayId(want, startOptions, env, info);
426 #endif
427 napi_value lastParam = info.argc > unwrapArgc ? info.argv[unwrapArgc] : nullptr;
428 napi_value result = nullptr;
429 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, lastParam, nullptr, nullptr, &result);
430 std::shared_ptr<NapiAsyncTask> asyncTask = std::move(uasyncTask);
431 RuntimeTask task = [env, asyncTask](int resultCode, const AAFwk::Want &want, bool isInner) {
432 TAG_LOGI(AAFwkTag::UI_EXT, "called");
433 napi_value abilityResult = AppExecFwk::WrapAbilityResult(env, resultCode, want);
434 if (abilityResult == nullptr) {
435 TAG_LOGW(AAFwkTag::UI_EXT, "wrap abilityResult failed");
436 asyncTask->Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INNER));
437 return;
438 }
439 if (isInner) {
440 asyncTask->Reject(env, CreateJsErrorByNativeErr(env, resultCode));
441 return;
442 }
443 asyncTask->Resolve(env, abilityResult);
444 };
445 auto context = context_.lock();
446 if (context == nullptr) {
447 TAG_LOGW(AAFwkTag::UI_EXT, "context is released");
448 asyncTask->Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
449 return result;
450 }
451 want.SetParam(Want::PARAM_RESV_FOR_RESULT, true);
452 int curRequestCode = context->GenerateCurRequestCode();
453 (unwrapArgc == INDEX_ONE) ? context->StartAbilityForResult(want, curRequestCode, std::move(task))
454 : context->StartAbilityForResult(want, startOptions, curRequestCode, std::move(task));
455 TAG_LOGD(AAFwkTag::UI_EXT, "OnStartAbilityForResult end");
456 return result;
457 }
458
OnTerminateSelfWithResult(napi_env env,NapiCallbackInfo & info)459 napi_value JsUIExtensionContext::OnTerminateSelfWithResult(napi_env env, NapiCallbackInfo& info)
460 {
461 TAG_LOGI(AAFwkTag::UI_EXT, "called");
462 if (info.argc == 0) {
463 TAG_LOGE(AAFwkTag::UI_EXT, "Not enough params");
464 ThrowTooFewParametersError(env);
465 return CreateJsUndefined(env);
466 }
467 int32_t resultCode = 0;
468 AAFwk::Want want;
469 if (!AppExecFwk::UnWrapAbilityResult(env, info.argv[INDEX_ZERO], resultCode, want)) {
470 TAG_LOGE(AAFwkTag::UI_EXT, "OnTerminateSelfWithResult Failed to parse ability result");
471 ThrowInvalidParamError(env, "Parse param want failed, want must be Want.");
472 return CreateJsUndefined(env);
473 }
474
475 NapiAsyncTask::CompleteCallback complete;
476 SetCallbackForTerminateWithResult(resultCode, want, complete);
477 napi_value lastParam = (info.argc > ARGC_ONE) ? info.argv[INDEX_ONE] : nullptr;
478 napi_value result = nullptr;
479 NapiAsyncTask::ScheduleHighQos("JsUIExtensionContext::OnTerminateSelfWithResult",
480 env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
481 TAG_LOGD(AAFwkTag::UI_EXT, "end");
482 return result;
483 }
484
OnStartAbilityForResultAsCaller(napi_env env,NapiCallbackInfo & info)485 napi_value JsUIExtensionContext::OnStartAbilityForResultAsCaller(napi_env env, NapiCallbackInfo &info)
486 {
487 TAG_LOGD(AAFwkTag::UI_EXT, "called");
488 if (info.argc == ARGC_ZERO) {
489 ThrowTooFewParametersError(env);
490 return CreateJsUndefined(env);
491 }
492 size_t unwrapArgc = 0;
493 AAFwk::Want want;
494 AAFwk::StartOptions startOptions;
495 if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) {
496 ThrowInvalidParamError(env, "Parse param want failed, want must be Want.");
497 TAG_LOGD(AAFwkTag::UI_EXT, "Input param type invalid");
498 return CreateJsUndefined(env);
499 }
500 #ifdef SUPPORT_SCREEN
501 (unwrapArgc == INDEX_ONE) ? InitDisplayId(want) : InitDisplayId(want, startOptions, env, info);
502 #endif
503 napi_value result = nullptr;
504 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
505 std::shared_ptr<NapiAsyncTask> asyncTask = std::move(uasyncTask);
506 RuntimeTask task = [env, asyncTask](int resultCode, const AAFwk::Want &want, bool isInner) {
507 TAG_LOGI(AAFwkTag::UI_EXT, "called");
508 napi_value abilityResult = AppExecFwk::WrapAbilityResult(env, resultCode, want);
509 if (abilityResult == nullptr) {
510 TAG_LOGW(AAFwkTag::UI_EXT, "Wrap abilityResult failed");
511 asyncTask->Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INNER));
512 return;
513 }
514 if (isInner) {
515 asyncTask->Reject(env, CreateJsErrorByNativeErr(env, resultCode));
516 return;
517 }
518 asyncTask->Resolve(env, abilityResult);
519 };
520 auto context = context_.lock();
521 if (context == nullptr) {
522 TAG_LOGW(AAFwkTag::UI_EXT, "The context is released");
523 asyncTask->Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
524 return result;
525 }
526 want.SetParam(Want::PARAM_RESV_FOR_RESULT, true);
527 int curRequestCode = context->GenerateCurRequestCode();
528 unwrapArgc == INDEX_ONE ?
529 context->StartAbilityForResultAsCaller(want, curRequestCode, std::move(task)) :
530 context->StartAbilityForResultAsCaller(want, startOptions, curRequestCode, std::move(task));
531 TAG_LOGD(AAFwkTag::UI_EXT, "End.");
532 return result;
533 }
534
OnConnectAbility(napi_env env,NapiCallbackInfo & info)535 napi_value JsUIExtensionContext::OnConnectAbility(napi_env env, NapiCallbackInfo& info)
536 {
537 TAG_LOGD(AAFwkTag::UI_EXT, "called");
538 // Check params count
539 if (info.argc < ARGC_TWO) {
540 TAG_LOGE(AAFwkTag::UI_EXT, "invalid argc");
541 ThrowTooFewParametersError(env);
542 return CreateJsUndefined(env);
543 }
544 // Unwrap want and connection
545 auto connection = sptr<JSUIExtensionConnection>::MakeSptr(env);
546 if (connection == nullptr) {
547 TAG_LOGE(AAFwkTag::UI_EXT, "make conn failed");
548 ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER);
549 return CreateJsUndefined(env);
550 }
551 AAFwk::Want want;
552 if (!AppExecFwk::UnwrapWant(env, info.argv[0], want) ||
553 !CheckConnectionParam(env, info.argv[1], connection, want)) {
554 ThrowInvalidParamError(env,
555 "Parse param want or connection failed, want must be Want and connection must be Connection.");
556 return CreateJsUndefined(env);
557 }
558 int64_t connectId = connection->GetConnectionId();
559 NapiAsyncTask::CompleteCallback complete =
560 [weak = context_, want, connection, connectId](napi_env env, NapiAsyncTask& task, int32_t status) {
561 auto context = weak.lock();
562 if (!context) {
563 TAG_LOGE(AAFwkTag::UI_EXT, "context is released");
564 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
565 RemoveConnection(connectId);
566 return;
567 }
568 TAG_LOGD(AAFwkTag::UI_EXT, "ConnectAbility connection:%{public}d", static_cast<int32_t>(connectId));
569 auto innerErrorCode = context->ConnectAbility(want, connection);
570 int32_t errcode = static_cast<int32_t>(AbilityRuntime::GetJsErrorCodeByNativeError(innerErrorCode));
571 if (errcode) {
572 connection->CallJsFailed(errcode);
573 RemoveConnection(connectId);
574 }
575 task.Resolve(env, CreateJsUndefined(env));
576 };
577 napi_value result = nullptr;
578 NapiAsyncTask::ScheduleHighQos("JSUIExtensionConnection::OnConnectAbility",
579 env, CreateAsyncTaskWithLastParam(env, nullptr, nullptr, std::move(complete), &result));
580 return CreateJsValue(env, connectId);
581 }
582
OnDisconnectAbility(napi_env env,NapiCallbackInfo & info)583 napi_value JsUIExtensionContext::OnDisconnectAbility(napi_env env, NapiCallbackInfo& info)
584 {
585 TAG_LOGD(AAFwkTag::UI_EXT, "start");
586 if (info.argc < ARGC_ONE) {
587 TAG_LOGE(AAFwkTag::UI_EXT, "invalid argc");
588 ThrowTooFewParametersError(env);
589 return CreateJsUndefined(env);
590 }
591 int64_t connectId = -1;
592 if (!AppExecFwk::UnwrapInt64FromJS2(env, info.argv[INDEX_ZERO], connectId)) {
593 TAG_LOGE(AAFwkTag::UI_EXT, "Invalid connectId");
594 ThrowInvalidParamError(env, "Parse param connectId failed, connectId must be number");
595 return CreateJsUndefined(env);
596 }
597
598 AAFwk::Want want;
599 sptr<JSUIExtensionConnection> connection = nullptr;
600 FindConnection(want, connection, connectId);
601 // begin disconnect
602 NapiAsyncTask::CompleteCallback complete =
603 [weak = context_, want, connection](
604 napi_env env, NapiAsyncTask& task, int32_t status) {
605 auto context = weak.lock();
606 if (!context) {
607 TAG_LOGW(AAFwkTag::UI_EXT, "context is released");
608 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
609 return;
610 }
611 if (connection == nullptr) {
612 TAG_LOGW(AAFwkTag::UI_EXT, "connection nullptr");
613 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INNER));
614 return;
615 }
616 TAG_LOGD(AAFwkTag::UI_EXT, "context->DisconnectAbility");
617 auto innerErrorCode = context->DisconnectAbility(want, connection);
618 if (innerErrorCode == 0) {
619 task.Resolve(env, CreateJsUndefined(env));
620 } else {
621 task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
622 }
623 };
624
625 napi_value lastParam = (info.argc == ARGC_ONE) ? nullptr : info.argv[INDEX_ONE];
626 napi_value result = nullptr;
627 NapiAsyncTask::Schedule("JSUIExtensionConnection::OnDisconnectAbility",
628 env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
629 return result;
630 }
631
OnStartUIServiceExtension(napi_env env,NapiCallbackInfo & info)632 napi_value JsUIExtensionContext::OnStartUIServiceExtension(napi_env env, NapiCallbackInfo& info)
633 {
634 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
635 TAG_LOGD(AAFwkTag::UI_EXT, "OnStartUIServiceExtension is called");
636 if (info.argc < ARGC_ONE) {
637 TAG_LOGE(AAFwkTag::UI_EXT, "Start UIServiceExtension failed, not enough params.");
638 ThrowTooFewParametersError(env);
639 return CreateJsUndefined(env);
640 }
641
642 size_t unwrapArgc = 0;
643 AAFwk::Want want;
644 AAFwk::StartOptions startOptions;
645 if (!CheckStartAbilityInputParam(env, info, want, startOptions, unwrapArgc)) {
646 TAG_LOGD(AAFwkTag::UI_EXT, "Failed, input param type invalid");
647 ThrowInvalidParamError(env, "Parse param want failed, want must be Want.");
648 return CreateJsUndefined(env);
649 }
650
651 NapiAsyncTask::CompleteCallback complete =
652 [weak = context_, want, startOptions, unwrapArgc](napi_env env, NapiAsyncTask& task, int32_t status) {
653 TAG_LOGD(AAFwkTag::UI_EXT, "StartUIServiceExtension begin");
654 auto context = weak.lock();
655 if (!context) {
656 TAG_LOGE(AAFwkTag::UI_EXT, "context is released");
657 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
658 return;
659 }
660 ErrCode innerErrorCode = ERR_OK;
661 innerErrorCode = context->StartUIServiceExtension(want);
662 if (innerErrorCode == 0) {
663 task.ResolveWithNoError(env, CreateJsUndefined(env));
664 } else {
665 task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
666 }
667 };
668
669 napi_value lastParam = (info.argc == unwrapArgc) ? nullptr : info.argv[unwrapArgc];
670 napi_value result = nullptr;
671 NapiAsyncTask::ScheduleHighQos("JSUIExtensionContext OnStartUIServiceExtension",
672 env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
673 return result;
674 }
675
UnwrapConnectUIServiceExtensionParam(napi_env env,NapiCallbackInfo & info,AAFwk::Want & want)676 bool JsUIExtensionContext::UnwrapConnectUIServiceExtensionParam(napi_env env, NapiCallbackInfo& info, AAFwk::Want& want)
677 {
678 if (info.argc < ARGC_TWO) {
679 TAG_LOGE(AAFwkTag::UISERVC_EXT, "failed, not enough params.");
680 ThrowTooFewParametersError(env);
681 return false;
682 }
683 bool unwrapResult = OHOS::AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want);
684 if (!unwrapResult) {
685 TAG_LOGE(AAFwkTag::UISERVC_EXT, "failed, UnwrapWant failed");
686 ThrowInvalidParamError(env, "parse want error");
687 return false;
688 }
689 TAG_LOGI(AAFwkTag::UISERVC_EXT, "callee:%{public}s.%{public}s", want.GetBundle().c_str(),
690 want.GetElement().GetAbilityName().c_str());
691 if (!CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) {
692 TAG_LOGE(AAFwkTag::UISERVC_EXT, "failed, callback type incorrect");
693 ThrowInvalidParamError(env, "Incorrect parameter types");
694 return false;
695 }
696 return true;
697 }
698
CheckConnectAlreadyExist(napi_env env,AAFwk::Want & want,napi_value callback,napi_value & result)699 bool JsUIExtensionContext::CheckConnectAlreadyExist(napi_env env, AAFwk::Want& want, napi_value callback,
700 napi_value& result)
701 {
702 TAG_LOGI(AAFwkTag::UISERVC_EXT, "called");
703 sptr<JSUIServiceUIExtConnection> connection = nullptr;
704 UIServiceConnection::FindUIServiceExtensionConnection(env, want, callback, connection);
705 if (connection == nullptr) {
706 TAG_LOGE(AAFwkTag::UISERVC_EXT, "connection == nullptr");
707 return false;
708 }
709
710 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
711 napi_value proxy = connection->GetProxyObject();
712 if (proxy == nullptr) {
713 TAG_LOGW(AAFwkTag::UISERVC_EXT, "can't got proxy object, wait for duplicated connect finish");
714 connection->AddDuplicatedPendingTask(uasyncTask);
715 } else {
716 TAG_LOGI(AAFwkTag::UISERVC_EXT, "Resolve, got proxy object");
717 uasyncTask->ResolveWithNoError(env, proxy);
718 }
719 return true;
720 }
721
OnConnectUIServiceExtension(napi_env env,NapiCallbackInfo & info)722 napi_value JsUIExtensionContext::OnConnectUIServiceExtension(napi_env env, NapiCallbackInfo& info)
723 {
724 TAG_LOGI(AAFwkTag::UISERVC_EXT, "called");
725 AAFwk::Want want;
726 bool unwrapResult = UnwrapConnectUIServiceExtensionParam(env, info, want);
727 if (!unwrapResult) {
728 TAG_LOGE(AAFwkTag::UISERVC_EXT, "UnwrapConnectUIServiceExtensionParam failed");
729 return CreateJsUndefined(env);
730 }
731 napi_value callbackObject = nullptr;
732 if (info.argc > ARGC_ONE) {
733 callbackObject = info.argv[INDEX_ONE];
734 }
735 napi_value result = nullptr;
736 bool duplicated = CheckConnectAlreadyExist(env, want, callbackObject, result);
737 if (duplicated) {
738 TAG_LOGE(AAFwkTag::UISERVC_EXT, "duplicated");
739 return result;
740 }
741
742 sptr<JSUIServiceUIExtConnection> connection = sptr<JSUIServiceUIExtConnection>::MakeSptr(env);
743 sptr<UIExtensionServiceHostStubImpl> stub = connection->GetServiceHostStub();
744 want.SetParam(UISERVICEHOSTPROXY_KEY, stub->AsObject());
745
746 result = nullptr;
747 std::unique_ptr<NapiAsyncTask> uasyncTask = CreateAsyncTaskWithLastParam(env, nullptr, nullptr, nullptr, &result);
748 std::shared_ptr<NapiAsyncTask> uasyncTaskShared = std::move(uasyncTask);
749 if (info.argc > ARGC_ONE) {
750 connection->SetJsConnectionObject(callbackObject);
751 }
752 connection->SetNapiAsyncTask(uasyncTaskShared);
753 UIServiceConnection::AddUIServiceExtensionConnection(want, connection);
754 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>(
755 [weak = context_, want, uasyncTaskShared, connection](
756 napi_env env, NapiAsyncTask& taskUseless, int32_t status) {
757 DoConnectUIServiceExtension(env, weak, connection, uasyncTaskShared, want);
758 });
759 napi_ref callback = nullptr;
760 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
761 NapiAsyncTask::ScheduleHighQos("JsUIExtensionContext::OnConnectUIServiceExtension",
762 env, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
763 return result;
764 }
765
DoConnectUIServiceExtension(napi_env env,std::weak_ptr<UIExtensionContext> weakContext,sptr<JSUIServiceUIExtConnection> connection,std::shared_ptr<NapiAsyncTask> uasyncTaskShared,const AAFwk::Want & want)766 void JsUIExtensionContext::DoConnectUIServiceExtension(napi_env env,
767 std::weak_ptr<UIExtensionContext> weakContext, sptr<JSUIServiceUIExtConnection> connection,
768 std::shared_ptr<NapiAsyncTask> uasyncTaskShared, const AAFwk::Want& want)
769 {
770 if (uasyncTaskShared == nullptr) {
771 return;
772 }
773
774 int64_t connectId = connection->GetConnectionId();
775 auto context = weakContext.lock();
776 if (!context) {
777 TAG_LOGE(AAFwkTag::CONTEXT, "Connect ability failed, context is released.");
778 uasyncTaskShared->Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
779 UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
780 return;
781 }
782
783 auto innerErrorCode = context->ConnectUIServiceExtensionAbility(want, connection);
784 AbilityErrorCode errcode = AbilityRuntime::GetJsErrorCodeByNativeError(innerErrorCode);
785 if (errcode != AbilityErrorCode::ERROR_OK) {
786 TAG_LOGE(AAFwkTag::UISERVC_EXT, "ConnectAbility failed, errcode is %{public}d.", errcode);
787 uasyncTaskShared->Reject(env, CreateJsError(env, errcode));
788 UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
789 }
790 }
791
OnDisconnectUIServiceExtension(napi_env env,NapiCallbackInfo & info)792 napi_value JsUIExtensionContext::OnDisconnectUIServiceExtension(napi_env env, NapiCallbackInfo& info)
793 {
794 if (info.argc < ARGC_ONE) {
795 TAG_LOGE(AAFwkTag::UISERVC_EXT, "failed, not enough params.");
796 ThrowTooFewParametersError(env);
797 return CreateJsUndefined(env);
798 }
799 AAFwk::JsUIServiceProxy* proxy = nullptr;
800 napi_status status = napi_unwrap(env, info.argv[INDEX_ZERO], reinterpret_cast<void**>(&proxy));
801 if (status != napi_ok || proxy == nullptr) {
802 TAG_LOGE(AAFwkTag::UISERVC_EXT, "napi_unwrap err or proxy == nullptr");
803 ThrowInvalidParamError(env, "Parameter verification failed");
804 return CreateJsUndefined(env);
805 }
806
807 int64_t connectId = proxy->GetConnectionId();
808 AAFwk::Want want;
809 sptr<JSUIServiceUIExtConnection> connection = nullptr;
810 UIServiceConnection::FindUIServiceExtensionConnection(connectId, want, connection);
811
812 TAG_LOGI(AAFwkTag::UISERVC_EXT, "connection:%{public}d.", static_cast<int32_t>(connectId));
813 NapiAsyncTask::CompleteCallback complete =
814 [weak = context_, want, connectId, connection](
815 napi_env env, NapiAsyncTask& task, int32_t status) {
816 auto context = weak.lock();
817 if (!context) {
818 TAG_LOGW(AAFwkTag::UISERVC_EXT, "OnDisconnectUIServiceExtension context is released");
819 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
820 UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
821 } else if (!connection) {
822 TAG_LOGW(AAFwkTag::UISERVC_EXT, "connection nullptr");
823 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INNER));
824 UIServiceConnection::RemoveUIServiceExtensionConnection(connectId);
825 } else {
826 TAG_LOGI(AAFwkTag::UISERVC_EXT, "context->DisconnectAbility");
827 context->DisconnectAbility(want, connection);
828 task.ResolveWithNoError(env, CreateJsUndefined(env));
829 }
830 };
831
832 napi_value result = nullptr;
833 NapiAsyncTask::Schedule("JsUIExtensionContext::OnDisconnectUIServiceExtension",
834 env, CreateAsyncTaskWithLastParam(env, nullptr, nullptr, std::move(complete), &result));
835 return result;
836 }
837
OnReportDrawnCompleted(napi_env env,NapiCallbackInfo & info)838 napi_value JsUIExtensionContext::OnReportDrawnCompleted(napi_env env, NapiCallbackInfo& info)
839 {
840 TAG_LOGD(AAFwkTag::UI_EXT, "called");
841 auto innerErrorCode = std::make_shared<int32_t>(ERR_OK);
842 NapiAsyncTask::ExecuteCallback execute = [weak = context_, innerErrorCode]() {
843 auto context = weak.lock();
844 if (!context) {
845 TAG_LOGW(AAFwkTag::UI_EXT, "context is released");
846 *innerErrorCode = static_cast<int32_t>(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
847 return;
848 }
849 *innerErrorCode = context->ReportDrawnCompleted();
850 };
851
852 NapiAsyncTask::CompleteCallback complete = [innerErrorCode](napi_env env, NapiAsyncTask& task, int32_t status) {
853 if (*innerErrorCode == ERR_OK) {
854 task.Resolve(env, CreateJsUndefined(env));
855 } else {
856 task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrorCode));
857 }
858 };
859
860 napi_value lastParam = info.argv[INDEX_ZERO];
861 napi_value result = nullptr;
862 NapiAsyncTask::ScheduleHighQos("JsUIExtensionContext::OnReportDrawnCompleted",
863 env, CreateAsyncTaskWithLastParam(env, lastParam, std::move(execute), std::move(complete), &result));
864 return result;
865 }
866
OnOpenAtomicService(napi_env env,NapiCallbackInfo & info)867 napi_value JsUIExtensionContext::OnOpenAtomicService(napi_env env, NapiCallbackInfo& info)
868 {
869 TAG_LOGD(AAFwkTag::UI_EXT, "called");
870 if (info.argc == ARGC_ZERO) {
871 ThrowTooFewParametersError(env);
872 return CreateJsUndefined(env);
873 }
874
875 std::string appId;
876 if (!ConvertFromJsValue(env, info.argv[INDEX_ZERO], appId)) {
877 TAG_LOGE(AAFwkTag::UI_EXT, "parse appId failed");
878 ThrowInvalidParamError(env, "Parse param appId failed, appId must be string.");
879 return CreateJsUndefined(env);
880 }
881
882 decltype(info.argc) unwrapArgc = ARGC_ONE;
883 Want want;
884 AAFwk::StartOptions startOptions;
885 if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) {
886 TAG_LOGD(AAFwkTag::UI_EXT, "atomic service options is used");
887 if (!AppExecFwk::UnwrapStartOptionsAndWant(env, info.argv[INDEX_ONE], startOptions, want)) {
888 TAG_LOGE(AAFwkTag::UI_EXT, "invalid atomic service options");
889 ThrowInvalidParamError(env,
890 "Parse param startOptions failed, startOptions must be StartOption.");
891 return CreateJsUndefined(env);
892 }
893 unwrapArgc++;
894 }
895
896 std::string bundleName = ATOMIC_SERVICE_PREFIX + appId;
897 TAG_LOGD(AAFwkTag::UI_EXT, "bundleName: %{public}s", bundleName.c_str());
898 want.SetBundle(bundleName);
899 #ifdef SUPPORT_SCREEN
900 (unwrapArgc == INDEX_ONE) ? InitDisplayId(want) : InitDisplayId(want, startOptions, env, info);
901 #endif
902 return OpenAtomicServiceInner(env, info, want, startOptions, unwrapArgc);
903 }
904
SetCallbackForTerminateWithResult(int32_t resultCode,AAFwk::Want & want,NapiAsyncTask::CompleteCallback & complete)905 void JsUIExtensionContext::SetCallbackForTerminateWithResult(int32_t resultCode, AAFwk::Want& want,
906 NapiAsyncTask::CompleteCallback& complete)
907 {
908 complete =
909 [weak = context_, want, resultCode](napi_env env, NapiAsyncTask& task, int32_t status) {
910 auto context = weak.lock();
911 if (!context) {
912 TAG_LOGE(AAFwkTag::UI_EXT, "context is released");
913 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
914 return;
915 }
916 auto extensionContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::UIExtensionContext>(context);
917 if (!extensionContext) {
918 TAG_LOGE(AAFwkTag::UI_EXT, "null extensionContext");
919 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM));
920 return;
921 }
922 auto token = extensionContext->GetToken();
923 AAFwk::AbilityManagerClient::GetInstance()->TransferAbilityResultForExtension(token, resultCode, want);
924 sptr<Rosen::Window> uiWindow = context->GetWindow();
925 if (!uiWindow) {
926 TAG_LOGE(AAFwkTag::UI_EXT, "null uiWindow");
927 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM));
928 return;
929 }
930 auto ret = uiWindow->TransferAbilityResult(resultCode, want);
931 if (ret != Rosen::WMError::WM_OK) {
932 task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM));
933 return;
934 }
935 auto errorCode = context->TerminateSelf();
936 if (errorCode == 0) {
937 task.ResolveWithNoError(env, CreateJsUndefined(env));
938 } else {
939 task.Reject(env, CreateJsErrorByNativeErr(env, errorCode));
940 }
941 };
942 }
943
OpenAtomicServiceInner(napi_env env,NapiCallbackInfo & info,Want & want,const AAFwk::StartOptions & options,size_t unwrapArgc)944 napi_value JsUIExtensionContext::OpenAtomicServiceInner(napi_env env, NapiCallbackInfo& info, Want &want,
945 const AAFwk::StartOptions &options, size_t unwrapArgc)
946 {
947 want.AddFlags(Want::FLAG_INSTALL_ON_DEMAND);
948 std::string startTime = std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
949 system_clock::now().time_since_epoch()).count());
950 want.SetParam(Want::PARAM_RESV_START_TIME, startTime);
951 napi_value result = nullptr;
952 auto context = context_.lock();
953 if (context == nullptr) {
954 TAG_LOGW(AAFwkTag::UI_EXT, "context is released");
955 ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT);
956 return CreateJsUndefined(env);
957 }
958 AddFreeInstallObserver(env, want, nullptr, &result, true);
959 RuntimeTask task = [env, element = want.GetElement(), startTime, &observer = freeInstallObserver_](
960 int resultCode, const AAFwk::Want& want, bool isInner) {
961 TAG_LOGD(AAFwkTag::UI_EXT, "start async callback");
962 if (observer == nullptr) {
963 TAG_LOGW(AAFwkTag::UI_EXT, "null observer");
964 return;
965 }
966 HandleScope handleScope(env);
967 std::string bundleName = element.GetBundleName();
968 std::string abilityName = element.GetAbilityName();
969 napi_value abilityResult = AppExecFwk::WrapAbilityResult(env, resultCode, want);
970 if (abilityResult == nullptr) {
971 TAG_LOGW(AAFwkTag::UI_EXT, "wrap abilityResult failed");
972 isInner = true;
973 resultCode = ERR_INVALID_VALUE;
974 }
975 if (isInner) {
976 observer->OnInstallFinished(bundleName, abilityName, startTime, resultCode);
977 } else {
978 observer->OnInstallFinished(bundleName, abilityName, startTime, abilityResult);
979 }
980 };
981 want.SetParam(Want::PARAM_RESV_FOR_RESULT, true);
982 auto curRequestCode = context->GenerateCurRequestCode();
983 context->OpenAtomicService(want, options, curRequestCode, std::move(task));
984 TAG_LOGD(AAFwkTag::UI_EXT, "end");
985 return result;
986 }
987
AddFreeInstallObserver(napi_env env,const AAFwk::Want & want,napi_value callback,napi_value * result,bool isAbilityResult,bool isOpenLink)988 void JsUIExtensionContext::AddFreeInstallObserver(napi_env env, const AAFwk::Want &want, napi_value callback,
989 napi_value *result, bool isAbilityResult, bool isOpenLink)
990 {
991 // adapter free install async return install and start result
992 TAG_LOGD(AAFwkTag::UI_EXT, "called");
993 int ret = 0;
994 if (freeInstallObserver_ == nullptr) {
995 freeInstallObserver_ = new JsFreeInstallObserver(env);
996 auto context = context_.lock();
997 if (!context) {
998 TAG_LOGE(AAFwkTag::UI_EXT, "null context");
999 return;
1000 }
1001 ret = context->AddFreeInstallObserver(freeInstallObserver_);
1002 }
1003
1004 if (ret != ERR_OK) {
1005 TAG_LOGE(AAFwkTag::UI_EXT, "addFreeInstallObserver error");
1006 }
1007 std::string startTime = want.GetStringParam(Want::PARAM_RESV_START_TIME);
1008 if (!isOpenLink) {
1009 TAG_LOGI(AAFwkTag::UI_EXT, "addJsObserver");
1010 std::string bundleName = want.GetElement().GetBundleName();
1011 std::string abilityName = want.GetElement().GetAbilityName();
1012 freeInstallObserver_->AddJsObserverObject(
1013 bundleName, abilityName, startTime, callback, result, isAbilityResult);
1014 }
1015 std::string url = want.GetUriString();
1016 freeInstallObserver_->AddJsObserverObject(startTime, url, callback, result, isAbilityResult);
1017 }
1018
CreateJsUIExtensionContext(napi_env env,std::shared_ptr<UIExtensionContext> context)1019 napi_value JsUIExtensionContext::CreateJsUIExtensionContext(napi_env env,
1020 std::shared_ptr<UIExtensionContext> context)
1021 {
1022 TAG_LOGD(AAFwkTag::UI_EXT, "called");
1023 std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo = nullptr;
1024 if (context) {
1025 abilityInfo = context->GetAbilityInfo();
1026 }
1027 napi_value objValue = CreateJsExtensionContext(env, context, abilityInfo);
1028
1029 std::unique_ptr<JsUIExtensionContext> jsContext = std::make_unique<JsUIExtensionContext>(context);
1030 napi_wrap(env, objValue, jsContext.release(), Finalizer, nullptr, nullptr);
1031
1032 const char *moduleName = "JsUIExtensionContext";
1033 BindNativeFunction(env, objValue, "startAbility", moduleName, StartAbility);
1034 BindNativeFunction(env, objValue, "openLink", moduleName, OpenLink);
1035 BindNativeFunction(env, objValue, "terminateSelf", moduleName, TerminateSelf);
1036 BindNativeFunction(env, objValue, "startAbilityForResult", moduleName, StartAbilityForResult);
1037 BindNativeFunction(env, objValue, "terminateSelfWithResult", moduleName, TerminateSelfWithResult);
1038 BindNativeFunction(env, objValue, "startAbilityForResultAsCaller", moduleName, StartAbilityForResultAsCaller);
1039 BindNativeFunction(env, objValue, "connectServiceExtensionAbility", moduleName, ConnectAbility);
1040 BindNativeFunction(env, objValue, "disconnectServiceExtensionAbility", moduleName, DisconnectAbility);
1041 BindNativeFunction(env, objValue, "reportDrawnCompleted", moduleName, ReportDrawnCompleted);
1042 BindNativeFunction(env, objValue, "openAtomicService", moduleName, OpenAtomicService);
1043 BindNativeFunction(env, objValue, "startUIServiceExtensionAbility", moduleName, StartUIServiceExtension);
1044 BindNativeFunction(env, objValue, "connectUIServiceExtensionAbility", moduleName, ConnectUIServiceExtension);
1045 BindNativeFunction(env, objValue, "disconnectUIServiceExtensionAbility", moduleName, DisconnectUIServiceExtension);
1046
1047 return objValue;
1048 }
1049
CheckStartAbilityInputParam(napi_env env,NapiCallbackInfo & info,AAFwk::Want & want,AAFwk::StartOptions & startOptions,size_t & unwrapArgc) const1050 bool JsUIExtensionContext::CheckStartAbilityInputParam(napi_env env, NapiCallbackInfo& info,
1051 AAFwk::Want& want, AAFwk::StartOptions& startOptions, size_t& unwrapArgc) const
1052 {
1053 if (info.argc < ARGC_ONE) {
1054 return false;
1055 }
1056 unwrapArgc = ARGC_ZERO;
1057 // Check input want
1058 if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) {
1059 return false;
1060 }
1061 if (!want.HasParameter(Want::PARAM_BACK_TO_OTHER_MISSION_STACK)) {
1062 want.SetParam(Want::PARAM_BACK_TO_OTHER_MISSION_STACK, true);
1063 }
1064 ++unwrapArgc;
1065 if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[1], napi_object)) {
1066 AppExecFwk::UnwrapStartOptions(env, info.argv[1], startOptions);
1067 unwrapArgc++;
1068 }
1069 return true;
1070 }
1071
1072 #ifdef SUPPORT_SCREEN
InitDisplayId(AAFwk::Want & want)1073 void JsUIExtensionContext::InitDisplayId(AAFwk::Want &want)
1074 {
1075 auto context = context_.lock();
1076 if (!context) {
1077 TAG_LOGE(AAFwkTag::UI_EXT, "null context");
1078 return;
1079 }
1080
1081 auto window = context->GetWindow();
1082 if (window == nullptr) {
1083 TAG_LOGE(AAFwkTag::UI_EXT, "null window");
1084 return;
1085 }
1086
1087 TAG_LOGI(AAFwkTag::UI_EXT, "window displayId %{public}" PRIu64, window->GetDisplayId());
1088 want.SetParam(AAFwk::Want::PARAM_RESV_DISPLAY_ID, static_cast<int32_t>(window->GetDisplayId()));
1089 }
1090
InitDisplayId(AAFwk::Want & want,AAFwk::StartOptions & startOptions,napi_env & env,NapiCallbackInfo & info)1091 void JsUIExtensionContext::InitDisplayId(AAFwk::Want &want, AAFwk::StartOptions &startOptions,
1092 napi_env &env, NapiCallbackInfo& info)
1093 {
1094 auto context = context_.lock();
1095 if (!context) {
1096 TAG_LOGE(AAFwkTag::UI_EXT, "null context");
1097 return;
1098 }
1099
1100 auto window = context->GetWindow();
1101 if (window == nullptr) {
1102 TAG_LOGE(AAFwkTag::UI_EXT, "null window");
1103 return;
1104 }
1105
1106 int32_t displayId = 0;
1107 if (info.argc > ARGC_ONE && CheckTypeForNapiValue(env, info.argv[1], napi_object)
1108 && AppExecFwk::UnwrapInt32ByPropertyName(env, info.argv[1], "displayId", displayId)) {
1109 TAG_LOGI(AAFwkTag::UI_EXT, "startOption displayId %{public}d", startOptions.GetDisplayID());
1110 return;
1111 }
1112
1113 TAG_LOGI(AAFwkTag::UI_EXT, "window displayId %{public}" PRIu64, window->GetDisplayId());
1114 startOptions.SetDisplayID(window->GetDisplayId());
1115 }
1116 #endif
1117
JSUIExtensionConnection(napi_env env)1118 JSUIExtensionConnection::JSUIExtensionConnection(napi_env env) : env_(env) {}
1119
~JSUIExtensionConnection()1120 JSUIExtensionConnection::~JSUIExtensionConnection()
1121 {
1122 ReleaseNativeReference(jsConnectionObject_.release());
1123 }
1124
ReleaseNativeReference(NativeReference * ref)1125 void JSUIExtensionConnection::ReleaseNativeReference(NativeReference* ref)
1126 {
1127 if (ref == nullptr) {
1128 TAG_LOGE(AAFwkTag::CONTEXT, "ReleaseNativeReference: ref == nullptr");
1129 return;
1130 }
1131 uv_loop_t *loop = nullptr;
1132 napi_get_uv_event_loop(env_, &loop);
1133 if (loop == nullptr) {
1134 TAG_LOGE(AAFwkTag::CONTEXT, "ReleaseNativeReference: failed to get uv loop.");
1135 delete ref;
1136 return;
1137 }
1138 uv_work_t *work = new (std::nothrow) uv_work_t;
1139 if (work == nullptr) {
1140 TAG_LOGE(AAFwkTag::CONTEXT, "ReleaseNativeReference: failed to create work.");
1141 delete ref;
1142 return;
1143 }
1144 work->data = reinterpret_cast<void *>(ref);
1145 int ret = uv_queue_work(loop, work, [](uv_work_t *work) {},
1146 [](uv_work_t *work, int status) {
1147 if (work == nullptr) {
1148 TAG_LOGE(AAFwkTag::CONTEXT, "ReleaseNativeReference: work is nullptr.");
1149 return;
1150 }
1151 if (work->data == nullptr) {
1152 TAG_LOGE(AAFwkTag::CONTEXT, "ReleaseNativeReference: data is nullptr.");
1153 delete work;
1154 work = nullptr;
1155 return;
1156 }
1157 NativeReference *refPtr = reinterpret_cast<NativeReference *>(work->data);
1158 delete refPtr;
1159 refPtr = nullptr;
1160 delete work;
1161 work = nullptr;
1162 });
1163 if (ret != 0) {
1164 delete ref;
1165 if (work != nullptr) {
1166 delete work;
1167 work = nullptr;
1168 }
1169 }
1170 }
1171
SetConnectionId(int64_t id)1172 void JSUIExtensionConnection::SetConnectionId(int64_t id)
1173 {
1174 connectionId_ = id;
1175 }
1176
GetConnectionId()1177 int64_t JSUIExtensionConnection::GetConnectionId()
1178 {
1179 return connectionId_;
1180 }
1181
OnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)1182 void JSUIExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
1183 const sptr<IRemoteObject> &remoteObject, int resultCode)
1184 {
1185 TAG_LOGD(AAFwkTag::UI_EXT, "resultCode:%{public}d", resultCode);
1186 wptr<JSUIExtensionConnection> connection = this;
1187 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
1188 ([connection, element, remoteObject, resultCode](napi_env env, NapiAsyncTask &task, int32_t status) {
1189 sptr<JSUIExtensionConnection> connectionSptr = connection.promote();
1190 if (!connectionSptr) {
1191 TAG_LOGE(AAFwkTag::UI_EXT, "connectionSptr nullptr");
1192 return;
1193 }
1194 connectionSptr->HandleOnAbilityConnectDone(element, remoteObject, resultCode);
1195 });
1196
1197 napi_ref callback = nullptr;
1198 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
1199 NapiAsyncTask::ScheduleHighQos("JSUIExtensionConnection::OnAbilityConnectDone",
1200 env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
1201 }
1202
HandleOnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)1203 void JSUIExtensionConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element,
1204 const sptr<IRemoteObject> &remoteObject, int resultCode)
1205 {
1206 TAG_LOGD(AAFwkTag::UI_EXT, "start, resultCode:%{public}d", resultCode);
1207 // wrap ElementName
1208 napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
1209
1210 // wrap RemoteObject
1211 napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(env_, remoteObject);
1212 napi_value argv[] = {napiElementName, napiRemoteObject};
1213 if (jsConnectionObject_ == nullptr) {
1214 TAG_LOGE(AAFwkTag::UI_EXT, "jsConnectionObject_ null");
1215 return;
1216 }
1217 napi_value obj = jsConnectionObject_->GetNapiValue();
1218 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
1219 TAG_LOGE(AAFwkTag::UI_EXT, "Failed to get object");
1220 return;
1221 }
1222 napi_value methodOnConnect = nullptr;
1223 napi_get_named_property(env_, obj, "onConnect", &methodOnConnect);
1224 if (methodOnConnect == nullptr) {
1225 TAG_LOGE(AAFwkTag::UI_EXT, "Failed to get onConnect from object");
1226 return;
1227 }
1228 napi_call_function(env_, obj, methodOnConnect, ARGC_TWO, argv, nullptr);
1229 }
1230
OnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)1231 void JSUIExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
1232 {
1233 TAG_LOGD(AAFwkTag::UI_EXT, "resultCode:%{public}d", resultCode);
1234 wptr<JSUIExtensionConnection> connection = this;
1235 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
1236 ([connection, element, resultCode](napi_env env, NapiAsyncTask &task, int32_t status) {
1237 sptr<JSUIExtensionConnection> connectionSptr = connection.promote();
1238 if (!connectionSptr) {
1239 TAG_LOGI(AAFwkTag::UI_EXT, "connectionSptr nullptr");
1240 return;
1241 }
1242 connectionSptr->HandleOnAbilityDisconnectDone(element, resultCode);
1243 });
1244 napi_ref callback = nullptr;
1245 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
1246 NapiAsyncTask::Schedule("JSUIExtensionConnection::OnAbilityDisconnectDone",
1247 env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
1248 }
1249
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)1250 void JSUIExtensionConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
1251 int resultCode)
1252 {
1253 TAG_LOGD(AAFwkTag::UI_EXT, "resultCode:%{public}d", resultCode);
1254 napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
1255 napi_value argv[] = {napiElementName};
1256 if (jsConnectionObject_ == nullptr) {
1257 TAG_LOGE(AAFwkTag::UI_EXT, "jsConnectionObject_ nullptr");
1258 return;
1259 }
1260 napi_value obj = jsConnectionObject_->GetNapiValue();
1261 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
1262 TAG_LOGE(AAFwkTag::UI_EXT, "Failed to get object");
1263 return;
1264 }
1265 napi_value method = nullptr;
1266 napi_get_named_property(env_, obj, "onDisconnect", &method);
1267 if (method == nullptr) {
1268 TAG_LOGE(AAFwkTag::UI_EXT, "Failed to get onDisconnect from object");
1269 return;
1270 }
1271
1272 // release connect
1273 RemoveConnection(connectionId_);
1274 napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr);
1275 }
1276
SetJsConnectionObject(napi_value jsConnectionObject)1277 void JSUIExtensionConnection::SetJsConnectionObject(napi_value jsConnectionObject)
1278 {
1279 napi_ref value = nullptr;
1280 napi_create_reference(env_, jsConnectionObject, 1, &value);
1281 jsConnectionObject_ = std::unique_ptr<NativeReference>(reinterpret_cast<NativeReference*>(value));
1282 }
1283
RemoveConnectionObject()1284 void JSUIExtensionConnection::RemoveConnectionObject()
1285 {
1286 jsConnectionObject_.reset();
1287 }
1288
CallJsFailed(int32_t errorCode)1289 void JSUIExtensionConnection::CallJsFailed(int32_t errorCode)
1290 {
1291 TAG_LOGD(AAFwkTag::UI_EXT, "CallJsFailed enter");
1292 if (jsConnectionObject_ == nullptr) {
1293 TAG_LOGE(AAFwkTag::UI_EXT, "jsConnectionObject_ nullptr");
1294 return;
1295 }
1296 napi_value obj = jsConnectionObject_->GetNapiValue();
1297 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
1298 TAG_LOGE(AAFwkTag::UI_EXT, "wrong to get object");
1299 return;
1300 }
1301
1302 napi_value method = nullptr;
1303 napi_get_named_property(env_, obj, "onFailed", &method);
1304 if (method == nullptr) {
1305 TAG_LOGE(AAFwkTag::UI_EXT, "Failed to get onFailed from object");
1306 return;
1307 }
1308 napi_value argv[] = { CreateJsValue(env_, errorCode) };
1309 napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr);
1310 TAG_LOGD(AAFwkTag::UI_EXT, "CallJsFailed end");
1311 }
1312
CallObjectMethod(const char * name,napi_value const * argv,size_t argc)1313 napi_value JSUIExtensionConnection::CallObjectMethod(const char* name, napi_value const *argv, size_t argc)
1314 {
1315 TAG_LOGD(AAFwkTag::CONTEXT, "name:%{public}s", name);
1316 if (!jsConnectionObject_) {
1317 TAG_LOGW(AAFwkTag::CONTEXT, "Not found jsConnectionObject_");
1318 return nullptr;
1319 }
1320
1321 HandleScope handleScope(env_);
1322 napi_value obj = jsConnectionObject_->GetNapiValue();
1323 if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
1324 TAG_LOGE(AAFwkTag::CONTEXT, "Failed to get jsConnectionObject_ object");
1325 return nullptr;
1326 }
1327
1328 napi_value method = nullptr;
1329 napi_get_named_property(env_, obj, name, &method);
1330 if (!CheckTypeForNapiValue(env_, method, napi_function)) {
1331 TAG_LOGE(AAFwkTag::CONTEXT, "Failed to get '%{public}s' from jsConnectionObject_ object", name);
1332 return nullptr;
1333 }
1334 napi_value result = nullptr;
1335 napi_call_function(env_, obj, method, argc, argv, &result);
1336 TAG_LOGD(AAFwkTag::CONTEXT, "CallFunction(%{public}s) ok", name);
1337 return result;
1338 }
1339
1340 } // namespace AbilityRuntime
1341 } // namespace OHOS
1342