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