From cfbc6ccba3ebc92a82acd2fb2d3262d70968c795 Mon Sep 17 00:00:00 2001 From: zhangzezhong Date: Thu, 11 Sep 2025 19:11:28 +0800 Subject: [PATCH] add context Signed-off-by: zhangzezhong --- frameworks/ets/ani/BUILD.gn | 3 + .../app/auto_fill_extension_context/BUILD.gn | 69 +++ .../ets_auto_fill_extension_context_module.h | 55 ++ ...ets_auto_fill_extension_context_module.cpp | 466 ++++++++++++++++ .../ets/ani/app/extension_context/BUILD.gn | 67 +++ .../include/ets_extension_context_module.h | 57 ++ .../src/ets_extension_context_module.cpp | 520 ++++++++++++++++++ .../ets/ani/app/ui_extension_context/BUILD.gn | 69 +++ .../include/ets_ui_extension_context_module.h | 55 ++ .../src/ets_ui_extension_context_module.cpp | 459 ++++++++++++++++ .../src/ets_auto_fill_extension_context.cpp | 11 + .../src/ets_ui_extension_context.cpp | 13 + .../application/AutoFillExtensionContext.ets | 23 + .../ets/ets/application/ExtensionContext.ets | 29 +- .../ets/application/UIExtensionContext.ets | 22 + .../ability_runtime/js_extension_context.cpp | 3 + .../js_auto_fill_extension_context.cpp | 3 + .../js_ui_extension_context.cpp | 3 + 18 files changed, 1924 insertions(+), 3 deletions(-) create mode 100644 frameworks/ets/ani/app/auto_fill_extension_context/BUILD.gn create mode 100644 frameworks/ets/ani/app/auto_fill_extension_context/include/ets_auto_fill_extension_context_module.h create mode 100644 frameworks/ets/ani/app/auto_fill_extension_context/src/ets_auto_fill_extension_context_module.cpp create mode 100644 frameworks/ets/ani/app/extension_context/BUILD.gn create mode 100644 frameworks/ets/ani/app/extension_context/include/ets_extension_context_module.h create mode 100644 frameworks/ets/ani/app/extension_context/src/ets_extension_context_module.cpp create mode 100644 frameworks/ets/ani/app/ui_extension_context/BUILD.gn create mode 100644 frameworks/ets/ani/app/ui_extension_context/include/ets_ui_extension_context_module.h create mode 100644 frameworks/ets/ani/app/ui_extension_context/src/ets_ui_extension_context_module.cpp diff --git a/frameworks/ets/ani/BUILD.gn b/frameworks/ets/ani/BUILD.gn index 07051afaec8..b52c58e5125 100644 --- a/frameworks/ets/ani/BUILD.gn +++ b/frameworks/ets/ani/BUILD.gn @@ -30,7 +30,10 @@ group("ani_packages") { "${ability_runtime_path}/frameworks/ets/ani/app/ability_context:ability_context_ani_kit", "${ability_runtime_path}/frameworks/ets/ani/app/ability_stage_context:ability_stage_context_ani_kit", "${ability_runtime_path}/frameworks/ets/ani/app/application_context:application_context_ani_kit", + "${ability_runtime_path}/frameworks/ets/ani/app/auto_fill_extension_context:auto_fill_extension_context_ani_kit", "${ability_runtime_path}/frameworks/ets/ani/app/context:context_ani_kit", + "${ability_runtime_path}/frameworks/ets/ani/app/extension_context:extension_context_ani_kit", + "${ability_runtime_path}/frameworks/ets/ani/app/ui_extension_context:ui_extension_context_ani_kit", "${ability_runtime_path}/frameworks/ets/ani/auto_fill_manager:ability_auto_fill_manager_ani_kit", "${ability_runtime_path}/frameworks/ets/ani/business_ability_router:businessabilityrouter_ani", "${ability_runtime_path}/frameworks/ets/ani/caller_complex:caller_complex_ani", diff --git a/frameworks/ets/ani/app/auto_fill_extension_context/BUILD.gn b/frameworks/ets/ani/app/auto_fill_extension_context/BUILD.gn new file mode 100644 index 00000000000..415c1892e55 --- /dev/null +++ b/frameworks/ets/ani/app/auto_fill_extension_context/BUILD.gn @@ -0,0 +1,69 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_shared_library("auto_fill_extension_context_ani_kit") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + + include_dirs = [ + "./include", + "${ability_runtime_path}/interfaces/kits/native/ability/native", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime", + "${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime", + "${ability_runtime_path}/interfaces/kits/native/ability/native/auto_fill_extension_ability", + "${ability_runtime_path}/frameworks/ets/ani/auto_fill_extension_ability/include", + ] + + configs = [] + + public_configs = [] + + sources = [ "./src/ets_auto_fill_extension_context_module.cpp" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ani_base_context:ani_base_context", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/ability/native:auto_fill_extension", + "${ability_runtime_native_path}/ability/native:auto_fill_extension_ani", + "${ability_runtime_native_path}/ability/native:extensionkit_native", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "hitrace:hitrace_meter", + "napi:ace_napi", + "runtime_core:ani", + "runtime_core:ani_helpers", + ] + + innerapi_tags = [ "platformsdk" ] + subsystem_name = "ability" + part_name = "ability_runtime" +} diff --git a/frameworks/ets/ani/app/auto_fill_extension_context/include/ets_auto_fill_extension_context_module.h b/frameworks/ets/ani/app/auto_fill_extension_context/include/ets_auto_fill_extension_context_module.h new file mode 100644 index 00000000000..977dfca7cf6 --- /dev/null +++ b/frameworks/ets/ani/app/auto_fill_extension_context/include/ets_auto_fill_extension_context_module.h @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_EXTENSION_CONTEXT_MODULE_H +#define OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_EXTENSION_CONTEXT_MODULE_H + +#include + +#include "ani.h" +#include "auto_fill_extension_context.h" +#include "native_engine/native_engine.h" + +namespace OHOS { +namespace AbilityRuntime { +class EtsAutoFillExtensionContextModule { +public: + EtsAutoFillExtensionContextModule() = default; + ~EtsAutoFillExtensionContextModule() = default; + + EtsAutoFillExtensionContextModule(const EtsAutoFillExtensionContextModule&) = delete; + EtsAutoFillExtensionContextModule(EtsAutoFillExtensionContextModule&&) = delete; + EtsAutoFillExtensionContextModule& operator=(const EtsAutoFillExtensionContextModule&) = delete; + EtsAutoFillExtensionContextModule& operator=(EtsAutoFillExtensionContextModule&&) = delete; + + static ani_object NativeTransferStatic(ani_env *aniEnv, ani_object aniObj, ani_object input, ani_object type); + static ani_object NativeTransferDynamic(ani_env *aniEnv, ani_class aniCls, ani_object input); + static napi_value GetOrCreateDynamicObject(napi_env napiEnv, + std::shared_ptr autoFillExtensionContext); + static void RegisterContextObjectCreator(); + +private: + static bool IsInstanceOf(ani_env *aniEnv, ani_object aniObj); + static std::unique_ptr CreateNativeReference(napi_env napiEnv, + std::shared_ptr autoFillExtensionContext); + static ani_object CreateDynamicObject(ani_env *aniEnv, ani_class aniCls, + std::shared_ptr autoFillExtensionContext); + static napi_value AttachAutoFillExtensionContext(napi_env env, void *value, void *hint); +}; + +void EtsAutoFillExtensionContextModuleInit(ani_env *aniEnv); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_EXTENSION_CONTEXT_MODULE_H diff --git a/frameworks/ets/ani/app/auto_fill_extension_context/src/ets_auto_fill_extension_context_module.cpp b/frameworks/ets/ani/app/auto_fill_extension_context/src/ets_auto_fill_extension_context_module.cpp new file mode 100644 index 00000000000..18ca4589138 --- /dev/null +++ b/frameworks/ets/ani/app/auto_fill_extension_context/src/ets_auto_fill_extension_context_module.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ets_auto_fill_extension_context_module.h" + +#include +#include +#include + +#include "ani_base_context.h" +#include "ani_common_util.h" +#include "context_transfer.h" +#include "ets_auto_fill_extension_context.h" +#include "ets_context_utils.h" +#include "ets_error_utils.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "interop_js/arkts_esvalue.h" +#include "interop_js/arkts_interop_js_api.h" +#include "interop_js/hybridgref_ani.h" +#include "interop_js/hybridgref_napi.h" +#include "js_auto_fill_extension_context.h" +#include "js_runtime_utils.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char *ETS_AUTO_FILL_EXTENSION_CONTEXT_CLASS_NAME = + "Lapplication/AutoFillExtensionContext/AutoFillExtensionContext;"; +} // namespace + +ani_object EtsAutoFillExtensionContextModule::NativeTransferStatic(ani_env *aniEnv, ani_object, ani_object input, + ani_object type) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "transfer static AutoFillExtensionContext"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null aniEnv"); + return nullptr; + } + + void *unwrapResult = nullptr; + bool success = arkts_esvalue_unwrap(aniEnv, input, &unwrapResult); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "failed to unwrap"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + if (unwrapResult == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null unwrapResult"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto context = reinterpret_cast *>(unwrapResult)->lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null AutoFillExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto autoFillExtensionContext = Context::ConvertTo(context); + if (autoFillExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid autoFillExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto &bindingObj = autoFillExtensionContext->GetBindingObject(); + if (bindingObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null bindingObj"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto staticContext = bindingObj->Get(); + if (staticContext != nullptr) { + TAG_LOGI(AAFwkTag::CONTEXT, "there exist a staticContext"); + return reinterpret_cast(*staticContext); + } + + // if not exist, create a new one + std::string contextType; + if (!AppExecFwk::GetStdString(aniEnv, reinterpret_cast(type), contextType)) { + TAG_LOGE(AAFwkTag::JSNAPI, "GetStdString failed"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + TAG_LOGD(AAFwkTag::CONTEXT, "contextType %{public}s", contextType.c_str()); + + auto newContext = ContextTransfer::GetInstance().GetStaticObject(contextType, aniEnv, context); + if (newContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create autoFillExtensionContext failed"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + return newContext; +} + +std::unique_ptr EtsAutoFillExtensionContextModule::CreateNativeReference(napi_env napiEnv, + std::shared_ptr autoFillExtensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (napiEnv == nullptr || autoFillExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null param"); + return nullptr; + } + + auto value = JsAutoFillExtensionContext::CreateJsAutoFillExtensionContext(napiEnv, autoFillExtensionContext); + auto systemModule = JsRuntime::LoadSystemModuleByEngine(napiEnv, "application.AutoFillExtensionContext", &value, + 1); + if (systemModule == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null systemModule"); + return nullptr; + } + + napi_value object = systemModule->GetNapiValue(); + if (!CheckTypeForNapiValue(napiEnv, object, napi_object)) { + TAG_LOGE(AAFwkTag::CONTEXT, "check type failed"); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(autoFillExtensionContext); + if (workContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null workContext"); + return nullptr; + } + auto status = napi_coerce_to_native_binding_object(napiEnv, object, DetachCallbackFunc, + AttachAutoFillExtensionContext, workContext, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "coerce AutoFillExtensionContext failed: %{public}d", status); + delete workContext; + return nullptr; + } + + status = napi_wrap(napiEnv, object, workContext, + [](napi_env, void *data, void *) { + TAG_LOGD(AAFwkTag::CONTEXT, "finalizer for weak_ptr AutoFillExtensionContext"); + delete static_cast *>(data); + }, nullptr, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "wrap failed: %{public}d", status); + delete workContext; + return nullptr; + } + + return systemModule; +} + +napi_value EtsAutoFillExtensionContextModule::GetOrCreateDynamicObject(napi_env napiEnv, + std::shared_ptr autoFillExtensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (napiEnv == nullptr || autoFillExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null param"); + return nullptr; + } + + // if sub-thread, create a new autoFillExtensionContext and return + if (getpid() != syscall(SYS_gettid)) { + auto subThreadObj = static_cast( + autoFillExtensionContext->GetSubThreadObject(static_cast(napiEnv))); + if (subThreadObj != nullptr) { + return subThreadObj->Get(); + } + auto subThreadRef = CreateNativeReference(napiEnv, autoFillExtensionContext); + if (subThreadRef == nullptr) { + return nullptr; + } + auto newObject = subThreadRef->Get(); + autoFillExtensionContext->BindSubThreadObject( + static_cast(napiEnv), static_cast(subThreadRef.release())); + return newObject; + } + + // if main-thread, get bindingObj firstly + auto &bindingObj = autoFillExtensionContext->GetBindingObject(); + if (bindingObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null bindingObj"); + return nullptr; + } + + // if main-thread bindingObj exist, return it directly + auto dynamicContext = bindingObj->Get(); + if (dynamicContext != nullptr) { + TAG_LOGI(AAFwkTag::UIABILITY, "there exist a dynamicContext"); + return dynamicContext->Get(); + } + + // if main-thread bindingObj didn't exist, create and bind + auto nativeRef = CreateNativeReference(napiEnv, autoFillExtensionContext); + if (nativeRef == nullptr) { + return nullptr; + } + + auto object = nativeRef->Get(); + autoFillExtensionContext->Bind(nativeRef.release()); + return object; +} + +ani_object EtsAutoFillExtensionContextModule::NativeTransferDynamic(ani_env *aniEnv, ani_class aniCls, + ani_object input) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "transfer dynamic AutoFillExtensionContext"); + if (!IsInstanceOf(aniEnv, input)) { + TAG_LOGE(AAFwkTag::CONTEXT, "not AutoFillExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto context = AbilityRuntime::GetStageModeContext(aniEnv, input); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + std::shared_ptr autoFillExtensionContext = + Context::ConvertTo(context); + if (autoFillExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid autoFillExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + ani_object object = CreateDynamicObject(aniEnv, aniCls, autoFillExtensionContext); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid object"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + return object; +} + +ani_object EtsAutoFillExtensionContextModule::CreateDynamicObject(ani_env *aniEnv, ani_class aniCls, + std::shared_ptr autoFillExtensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + std::string contextType; + if (!AppExecFwk::GetStaticFieldString(aniEnv, aniCls, "contextType", contextType)) { + TAG_LOGE(AAFwkTag::CONTEXT, "get context type failed"); + return nullptr; + } + TAG_LOGD(AAFwkTag::CONTEXT, "contextType %{public}s", contextType.c_str()); + + // get napiEnv from aniEnv + napi_env napiEnv = {}; + if (!arkts_napi_scope_open(aniEnv, &napiEnv)) { + TAG_LOGE(AAFwkTag::CONTEXT, "arkts_napi_scope_open failed"); + return nullptr; + } + + // create normal AutoFillExtensionContext + auto contextObj = ContextTransfer::GetInstance().GetDynamicObject(contextType, napiEnv, autoFillExtensionContext); + if (contextObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create AutoFillExtensionContext failed"); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + hybridgref ref = nullptr; + bool success = hybridgref_create_from_napi(napiEnv, contextObj, &ref); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "hybridgref_create_from_napi failed"); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + ani_object result = nullptr; + success = hybridgref_get_esvalue(aniEnv, ref, &result); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "hybridgref_get_esvalue failed"); + hybridgref_delete_from_napi(napiEnv, ref); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + hybridgref_delete_from_napi(napiEnv, ref); + + if (!arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr)) { + TAG_LOGE(AAFwkTag::CONTEXT, "arkts_napi_scope_close_n failed"); + return nullptr; + } + + return result; +} + +bool EtsAutoFillExtensionContextModule::IsInstanceOf(ani_env *aniEnv, ani_object aniObj) +{ + ani_class cls {}; + ani_status status = ANI_ERROR; + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null aniEnv"); + return false; + } + if ((status = aniEnv->FindClass(ETS_AUTO_FILL_EXTENSION_CONTEXT_CLASS_NAME, &cls)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass status: %{public}d", status); + return false; + } + ani_boolean isInstanceOf = false; + if ((status = aniEnv->Object_InstanceOf(aniObj, cls, &isInstanceOf)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_InstanceOf status: %{public}d", status); + return false; + } + return isInstanceOf; +} + +void EtsAutoFillExtensionContextModule::RegisterContextObjectCreator() +{ + ContextTransfer::GetInstance().RegisterStaticObjectCreator("AutoFillExtensionContext", + [](ani_env *aniEnv, std::shared_ptr context) -> ani_object { + auto autoFillExtensionContext = Context::ConvertTo(context); + if (autoFillExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid autoFillExtensionContext"); + return nullptr; + } + auto newContext = EtsAutoFillExtensionContext::CreateEtsAutoFillExtensionContext(aniEnv, + autoFillExtensionContext); + if (newContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create autoFillExtensionContext failed"); + return nullptr; + } + return newContext; + }); + + ContextTransfer::GetInstance().RegisterDynamicObjectCreator("AutoFillExtensionContext", + [](napi_env napiEnv, std::shared_ptr context) -> napi_value { + auto autoFillExtensionContext = Context::ConvertTo(context); + if (autoFillExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid autoFillExtensionContext"); + return nullptr; + } + + auto object = EtsAutoFillExtensionContextModule::GetOrCreateDynamicObject(napiEnv, + autoFillExtensionContext); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "get or create object failed"); + return nullptr; + } + return object; + }); +} + +napi_value EtsAutoFillExtensionContextModule::AttachAutoFillExtensionContext(napi_env env, void *value, void *hint) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "attach autoFillExtensionContext"); + if (env == nullptr || value == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid params"); + return nullptr; + } + auto ptr = reinterpret_cast *>(value)->lock(); + if (ptr == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ptr"); + return nullptr; + } + + auto object = JsAutoFillExtensionContext::CreateJsAutoFillExtensionContext(env, ptr); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null object"); + return nullptr; + } + auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.AutoFillExtensionContext", &object, 1); + if (systemModule == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null systemModule"); + return nullptr; + } + auto contextObj = systemModule->GetNapiValue(); + if (!CheckTypeForNapiValue(env, contextObj, napi_object)) { + TAG_LOGE(AAFwkTag::CONTEXT, "not napi object"); + return nullptr; + } + + auto status = napi_coerce_to_native_binding_object( + env, contextObj, DetachCallbackFunc, AttachAutoFillExtensionContext, value, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "coerce autoFillExtensionContext failed: %{public}d", status); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(ptr); + status = napi_wrap(env, contextObj, workContext, + [](napi_env, void *data, void *) { + TAG_LOGD(AAFwkTag::CONTEXT, "finalizer for weak_ptr autoFillExtensionContext"); + delete static_cast *>(data); + }, + nullptr, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "wrap autoFillExtensionContext failed: %{public}d", status); + delete workContext; + return nullptr; + } + + return contextObj; +} + +void EtsAutoFillExtensionContextModuleInit(ani_env *aniEnv) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "Init AutoFillExtensionContext kit"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ani env"); + return; + } + + ani_class autoFillExtensionContextCls = nullptr; + auto status = aniEnv->FindClass(ETS_AUTO_FILL_EXTENSION_CONTEXT_CLASS_NAME, &autoFillExtensionContextCls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass AutoFillExtensionContext failed status: %{public}d", status); + return; + } + + std::array nativeFuncs = { + ani_native_function { "nativeTransferStatic", "Lstd/interop/ESValue;Lstd/core/String;:Lstd/core/Object;", + reinterpret_cast(EtsAutoFillExtensionContextModule::NativeTransferStatic) }, + ani_native_function { "nativeTransferDynamic", "Lstd/core/Object;:Lstd/interop/ESValue;", + reinterpret_cast(EtsAutoFillExtensionContextModule::NativeTransferDynamic) }, + }; + status = aniEnv->Class_BindStaticNativeMethods(autoFillExtensionContextCls, nativeFuncs.data(), + nativeFuncs.size()); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Class_BindStaticNativeMethods failed status: %{public}d", status); + return; + } + + EtsAutoFillExtensionContextModule::RegisterContextObjectCreator(); + TAG_LOGD(AAFwkTag::CONTEXT, "Init AutoFillExtensionContext kit end"); +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "ANI_Constructor"); + if (vm == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null vm"); + return ANI_ERROR; + } + + ani_env *aniEnv = nullptr; + ani_status status = vm->GetEnv(ANI_VERSION_1, &aniEnv); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetEnv failed status: %{public}d", status); + return ANI_NOT_FOUND; + } + + EtsAutoFillExtensionContextModuleInit(aniEnv); + *result = ANI_VERSION_1; + TAG_LOGD(AAFwkTag::CONTEXT, "ANI_Constructor finish"); + return ANI_OK; +} +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/app/extension_context/BUILD.gn b/frameworks/ets/ani/app/extension_context/BUILD.gn new file mode 100644 index 00000000000..695b0dfdb94 --- /dev/null +++ b/frameworks/ets/ani/app/extension_context/BUILD.gn @@ -0,0 +1,67 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_shared_library("extension_context_ani_kit") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + + include_dirs = [ + "./include", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime", + "${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime", + "${ability_runtime_path}/frameworks/ets/ani/ani_common/include", + ] + + configs = [] + + public_configs = [] + + sources = [ "./src/ets_extension_context_module.cpp" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ani_base_context:ani_base_context", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/ability:ability_context_native", + "${ability_runtime_native_path}/ability/native:extensionkit_native", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/appkit:app_context_utils", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "hitrace:hitrace_meter", + "napi:ace_napi", + "runtime_core:ani", + "runtime_core:ani_helpers", + ] + + innerapi_tags = [ "platformsdk" ] + subsystem_name = "ability" + part_name = "ability_runtime" +} diff --git a/frameworks/ets/ani/app/extension_context/include/ets_extension_context_module.h b/frameworks/ets/ani/app/extension_context/include/ets_extension_context_module.h new file mode 100644 index 00000000000..d2be1cfcfc0 --- /dev/null +++ b/frameworks/ets/ani/app/extension_context/include/ets_extension_context_module.h @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef OHOS_ABILITY_RUNTIME_ETS_EXTENSION_CONTEXT_MODULE_H +#define OHOS_ABILITY_RUNTIME_ETS_EXTENSION_CONTEXT_MODULE_H + +#include + +#include "ani.h" +#include "extension_context.h" +#include "native_engine/native_engine.h" + +namespace OHOS { +namespace AbilityRuntime { +class EtsExtensionContextModule { +public: + EtsExtensionContextModule() = default; + ~EtsExtensionContextModule() = default; + + EtsExtensionContextModule(const EtsExtensionContextModule&) = delete; + EtsExtensionContextModule(EtsExtensionContextModule&&) = delete; + EtsExtensionContextModule& operator=(const EtsExtensionContextModule&) = delete; + EtsExtensionContextModule& operator=(EtsExtensionContextModule&&) = delete; + + static ani_object NativeTransferStatic(ani_env *aniEnv, ani_object aniObj, ani_object input, ani_object type); + static ani_object NativeTransferDynamic(ani_env *aniEnv, ani_class aniCls, ani_object input); + static napi_value GetOrCreateDynamicObject(napi_env napiEnv, + std::shared_ptr extensionContext); + static void RegisterContextObjectCreator(); + +private: + static bool LoadTargetModule(ani_env *aniEnv, const std::string &className); + static std::unique_ptr CreateNativeReference(napi_env napiEnv, + std::shared_ptr extensionContext); + static ani_object CreateStaticObject(ani_env *aniEnv, ani_object type, + std::shared_ptr extensionContext); + static ani_object CreateDynamicObject(ani_env *aniEnv, ani_class aniCls, + std::shared_ptr extensionContext); + static napi_value AttachExtensionContext(napi_env env, void *value, void *hint); +}; + +void EtsExtensionContextModuleInit(ani_env *aniEnv); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_EXTENSION_CONTEXT_MODULE_H diff --git a/frameworks/ets/ani/app/extension_context/src/ets_extension_context_module.cpp b/frameworks/ets/ani/app/extension_context/src/ets_extension_context_module.cpp new file mode 100644 index 00000000000..b0007c6d1de --- /dev/null +++ b/frameworks/ets/ani/app/extension_context/src/ets_extension_context_module.cpp @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ets_extension_context_module.h" + +#include +#include +#include + +#include "ani_base_context.h" +#include "ani_common_util.h" +#include "context_transfer.h" +#include "ets_context_utils.h" +#include "ets_error_utils.h" +#include "ets_extension_context.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "interop_js/arkts_esvalue.h" +#include "interop_js/arkts_interop_js_api.h" +#include "interop_js/hybridgref_ani.h" +#include "interop_js/hybridgref_napi.h" +#include "js_extension_context.h" +#include "js_runtime_utils.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char *ETS_EXTENSION_CONTEXT_CLASS_NAME = "Lapplication/ExtensionContext/ExtensionContext;"; + +std::string GetClassNameByContextType(const std::string &contextType) +{ + std::string className; + static const std::unordered_map mapping = { + {"ExtensionContext", "Lapplication/ExtensionContext/ExtensionContext;"}, + {"UIExtensionContext", "Lapplication/UIExtensionContext/UIExtensionContext;"}, + {"AutoFillExtensionContext", "Lapplication/AutoFillExtensionContext/AutoFillExtensionContext;"} + }; + auto it = mapping.find(contextType); + if (it != mapping.end()) { + className = it->second; + } + return className; +} +} // namespace + +ani_object EtsExtensionContextModule::NativeTransferStatic(ani_env *aniEnv, ani_object, ani_object input, + ani_object type) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "transfer static ExtensionContext"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null aniEnv"); + return nullptr; + } + + void *unwrapResult = nullptr; + bool success = arkts_esvalue_unwrap(aniEnv, input, &unwrapResult); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "failed to unwrap"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + if (unwrapResult == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null unwrapResult"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto context = reinterpret_cast *>(unwrapResult)->lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto extensionContext = Context::ConvertTo(context); + if (extensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid extensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto &bindingObj = extensionContext->GetBindingObject(); + if (bindingObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null bindingObj"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto staticContext = bindingObj->Get(); + if (staticContext != nullptr) { + TAG_LOGI(AAFwkTag::CONTEXT, "there exist a staticContext"); + return reinterpret_cast(*staticContext); + } + + auto extensionContextObj = CreateStaticObject(aniEnv, type, extensionContext); + if (extensionContextObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "extensionContextObj invalid"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + return extensionContextObj; +} + +ani_object EtsExtensionContextModule::CreateStaticObject(ani_env *aniEnv, ani_object type, + std::shared_ptr extensionContext) +{ + std::string contextType; + if (!AppExecFwk::GetStdString(aniEnv, reinterpret_cast(type), contextType)) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetStdString failed"); + return nullptr; + } + TAG_LOGD(AAFwkTag::CONTEXT, "contextType %{public}s", contextType.c_str()); + + if (!ContextTransfer::GetInstance().IsStaticCreatorExist(contextType)) { + std::string className = GetClassNameByContextType(contextType); + if (!LoadTargetModule(aniEnv, className)) { + return nullptr; + } + } + + auto extensionContextObj = ContextTransfer::GetInstance().GetStaticObject(contextType, aniEnv, extensionContext); + if (extensionContextObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "extensionContextObj invalid"); + return nullptr; + } + + return extensionContextObj; +} + +bool EtsExtensionContextModule::LoadTargetModule(ani_env *aniEnv, const std::string &className) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + ani_class contextCls = nullptr; + auto status = aniEnv->FindClass(className.c_str(), &contextCls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass %{public}s failed status: %{public}d", className.c_str(), status); + return false; + } + + std::string gotContextType; + if (!AppExecFwk::GetStaticFieldString(aniEnv, contextCls, "contextType", gotContextType)) { + TAG_LOGE(AAFwkTag::CONTEXT, "get context type failed"); + return false; + } + + TAG_LOGD(AAFwkTag::CONTEXT, "gotContext %{public}s", gotContextType.c_str()); + return true; +} + +std::unique_ptr EtsExtensionContextModule::CreateNativeReference(napi_env napiEnv, + std::shared_ptr extensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (napiEnv == nullptr || extensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null param"); + return nullptr; + } + + auto value = CreateJsExtensionContext(napiEnv, extensionContext, extensionContext->GetAbilityInfo()); + auto systemModule = JsRuntime::LoadSystemModuleByEngine(napiEnv, "application.ExtensionContext", &value, 1); + if (systemModule == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null systemModule"); + return nullptr; + } + + napi_value object = systemModule->GetNapiValue(); + if (!CheckTypeForNapiValue(napiEnv, object, napi_object)) { + TAG_LOGE(AAFwkTag::CONTEXT, "check type failed"); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(extensionContext); + if (workContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null workContext"); + return nullptr; + } + auto status = napi_coerce_to_native_binding_object(napiEnv, object, DetachCallbackFunc, AttachExtensionContext, + workContext, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "coerce ExtensionContext failed: %{public}d", status); + delete workContext; + return nullptr; + } + + status = napi_wrap(napiEnv, object, workContext, + [](napi_env, void *data, void *) { + TAG_LOGD(AAFwkTag::CONTEXT, "finalizer for weak_ptr ExtensionContext"); + delete static_cast *>(data); + }, nullptr, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "wrap failed: %{public}d", status); + delete workContext; + return nullptr; + } + + return systemModule; +} + +napi_value EtsExtensionContextModule::GetOrCreateDynamicObject(napi_env napiEnv, + std::shared_ptr extensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (napiEnv == nullptr || extensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null param"); + return nullptr; + } + + // if sub-thread, create a new extensionContext and return + if (getpid() != syscall(SYS_gettid)) { + auto subThreadObj = static_cast( + extensionContext->GetSubThreadObject(static_cast(napiEnv))); + if (subThreadObj != nullptr) { + return subThreadObj->Get(); + } + auto subThreadRef = CreateNativeReference(napiEnv, extensionContext); + if (subThreadRef == nullptr) { + return nullptr; + } + auto newObject = subThreadRef->Get(); + extensionContext->BindSubThreadObject( + static_cast(napiEnv), static_cast(subThreadRef.release())); + return newObject; + } + + // if main-thread, get bindingObj firstly + auto &bindingObj = extensionContext->GetBindingObject(); + if (bindingObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null bindingObj"); + return nullptr; + } + + // if main-thread bindingObj exist, return it directly + auto dynamicContext = bindingObj->Get(); + if (dynamicContext != nullptr) { + TAG_LOGI(AAFwkTag::UIABILITY, "there exist a dynamicContext"); + return dynamicContext->Get(); + } + + // if main-thread bindingObj didn't exist, create and bind + auto nativeRef = CreateNativeReference(napiEnv, extensionContext); + if (nativeRef == nullptr) { + return nullptr; + } + + auto object = nativeRef->Get(); + extensionContext->Bind(nativeRef.release()); + return object; +} + +ani_object EtsExtensionContextModule::NativeTransferDynamic(ani_env *aniEnv, ani_class aniCls, ani_object input) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "transfer dynamic ExtensionContext"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null aniEnv"); + return nullptr; + } + + auto context = AbilityRuntime::GetStageModeContext(aniEnv, input); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + std::shared_ptr extensionContext = Context::ConvertTo(context); + if (extensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid extensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + ani_object object = CreateDynamicObject(aniEnv, aniCls, extensionContext); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid object"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + return object; +} + +ani_object EtsExtensionContextModule::CreateDynamicObject(ani_env *aniEnv, ani_class aniCls, + std::shared_ptr extensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + std::string contextType; + if (!AppExecFwk::GetStaticFieldString(aniEnv, aniCls, "contextType", contextType)) { + TAG_LOGE(AAFwkTag::CONTEXT, "get context type failed"); + return nullptr; + } + TAG_LOGD(AAFwkTag::CONTEXT, "contextType %{public}s", contextType.c_str()); + + if (!ContextTransfer::GetInstance().IsDynamicCreatorExist(contextType)) { + std::string className = GetClassNameByContextType(contextType); + if (!LoadTargetModule(aniEnv, className)) { + return nullptr; + } + } + + // get napiEnv from aniEnv + napi_env napiEnv = {}; + if (!arkts_napi_scope_open(aniEnv, &napiEnv)) { + TAG_LOGE(AAFwkTag::CONTEXT, "arkts_napi_scope_open failed"); + return nullptr; + } + + // create normal ExtensionContext + auto contextObj = ContextTransfer::GetInstance().GetDynamicObject(contextType, napiEnv, extensionContext); + if (contextObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create ExtensionContext failed"); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + hybridgref ref = nullptr; + bool success = hybridgref_create_from_napi(napiEnv, contextObj, &ref); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "hybridgref_create_from_napi failed"); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + ani_object result = nullptr; + success = hybridgref_get_esvalue(aniEnv, ref, &result); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "hybridgref_get_esvalue failed"); + hybridgref_delete_from_napi(napiEnv, ref); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + hybridgref_delete_from_napi(napiEnv, ref); + + if (!arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr)) { + TAG_LOGE(AAFwkTag::CONTEXT, "arkts_napi_scope_close_n failed"); + return nullptr; + } + + return result; +} + +void EtsExtensionContextModule::RegisterContextObjectCreator() +{ + ContextTransfer::GetInstance().RegisterStaticObjectCreator("ExtensionContext", + [](ani_env *aniEnv, std::shared_ptr context) -> ani_object { + ani_class cls {}; + ani_status status = ANI_ERROR; + if ((status = aniEnv->FindClass("Lapplication/Context/Context;", &cls)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return nullptr; + } + auto newContext = ContextUtil::CreateContextObject(aniEnv, cls, context); + if (newContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create context failed"); + return nullptr; + } + auto extensionContext = Context::ConvertTo(context); + if (extensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid extensionContext"); + return nullptr; + } + if ((status = aniEnv->FindClass("Lapplication/ExtensionContext/ExtensionContext;", &cls)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return nullptr; + } + CreateEtsExtensionContext(aniEnv, cls, newContext, extensionContext, extensionContext->GetAbilityInfo()); + ani_ref *contextGlobalRef = new (std::nothrow) ani_ref; + if (contextGlobalRef == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "new contextGlobalRef failed"); + return nullptr; + } + if ((status = aniEnv->GlobalReference_Create(newContext, contextGlobalRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "GlobalReference_Create failed status: %{public}d", status); + delete contextGlobalRef; + return nullptr; + } + extensionContext->Bind(contextGlobalRef); + return newContext; + }); + + ContextTransfer::GetInstance().RegisterDynamicObjectCreator("ExtensionContext", + [](napi_env napiEnv, std::shared_ptr context) -> napi_value { + auto extensionContext = Context::ConvertTo(context); + if (extensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid extensionContext"); + return nullptr; + } + + auto object = EtsExtensionContextModule::GetOrCreateDynamicObject(napiEnv, extensionContext); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "get or create object failed"); + return nullptr; + } + return object; + }); +} + +napi_value EtsExtensionContextModule::AttachExtensionContext(napi_env env, void *value, void *hint) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "attach extension context"); + if (env == nullptr || value == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid params"); + return nullptr; + } + auto ptr = reinterpret_cast *>(value)->lock(); + if (ptr == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ptr"); + return nullptr; + } + + std::shared_ptr abilityInfo = ptr->GetAbilityInfo(); + auto object = CreateJsExtensionContext(env, ptr, abilityInfo); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null object"); + return nullptr; + } + auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.ExtensionContext", &object, 1); + if (systemModule == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null systemModule"); + return nullptr; + } + auto contextObj = systemModule->GetNapiValue(); + if (!CheckTypeForNapiValue(env, contextObj, napi_object)) { + TAG_LOGE(AAFwkTag::CONTEXT, "not napi object"); + return nullptr; + } + + auto status = napi_coerce_to_native_binding_object( + env, contextObj, DetachCallbackFunc, AttachExtensionContext, value, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "coerce extension context failed: %{public}d", status); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(ptr); + status = napi_wrap(env, contextObj, workContext, + [](napi_env, void *data, void *) { + TAG_LOGD(AAFwkTag::CONTEXT, "finalizer for weak_ptr extension context"); + delete static_cast *>(data); + }, + nullptr, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "wrap extension context failed: %{public}d", status); + delete workContext; + return nullptr; + } + + return contextObj; +} + +void EtsExtensionContextModuleInit(ani_env *aniEnv) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "Init ExtensionContext kit"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ani env"); + return; + } + + ani_class extensionContextCls = nullptr; + auto status = aniEnv->FindClass(ETS_EXTENSION_CONTEXT_CLASS_NAME, &extensionContextCls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass ExtensionContext failed status: %{public}d", status); + return; + } + + std::array nativeFuncs = { + ani_native_function { "nativeTransferStatic", "Lstd/interop/ESValue;Lstd/core/String;:Lstd/core/Object;", + reinterpret_cast(EtsExtensionContextModule::NativeTransferStatic) }, + ani_native_function { "nativeTransferDynamic", "Lstd/core/Object;:Lstd/interop/ESValue;", + reinterpret_cast(EtsExtensionContextModule::NativeTransferDynamic) }, + }; + status = aniEnv->Class_BindStaticNativeMethods(extensionContextCls, nativeFuncs.data(), nativeFuncs.size()); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Class_BindStaticNativeMethods failed status: %{public}d", status); + return; + } + + EtsExtensionContextModule::RegisterContextObjectCreator(); + TAG_LOGD(AAFwkTag::CONTEXT, "Init ExtensionContext kit end"); +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "ANI_Constructor"); + if (vm == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null vm"); + return ANI_ERROR; + } + + ani_env *aniEnv = nullptr; + ani_status status = vm->GetEnv(ANI_VERSION_1, &aniEnv); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetEnv failed status: %{public}d", status); + return ANI_NOT_FOUND; + } + + EtsExtensionContextModuleInit(aniEnv); + *result = ANI_VERSION_1; + TAG_LOGD(AAFwkTag::CONTEXT, "ANI_Constructor finish"); + return ANI_OK; +} +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/app/ui_extension_context/BUILD.gn b/frameworks/ets/ani/app/ui_extension_context/BUILD.gn new file mode 100644 index 00000000000..03583be7e83 --- /dev/null +++ b/frameworks/ets/ani/app/ui_extension_context/BUILD.gn @@ -0,0 +1,69 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_shared_library("ui_extension_context_ani_kit") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + + include_dirs = [ + "./include", + "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/include", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime", + "${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime", + "${ability_runtime_path}/interfaces/kits/native/ability/native", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", + "${ability_runtime_path}/interfaces/inner_api/app_manager/include/appmgr", + ] + + configs = [] + + public_configs = [] + + sources = [ "./src/ets_ui_extension_context_module.cpp" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ani_base_context:ani_base_context", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/ability/native:ui_extension", + "${ability_runtime_native_path}/ability/native:ui_extension_ani", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "hitrace:hitrace_meter", + "napi:ace_napi", + "runtime_core:ani", + "runtime_core:ani_helpers", + ] + + innerapi_tags = [ "platformsdk" ] + subsystem_name = "ability" + part_name = "ability_runtime" +} diff --git a/frameworks/ets/ani/app/ui_extension_context/include/ets_ui_extension_context_module.h b/frameworks/ets/ani/app/ui_extension_context/include/ets_ui_extension_context_module.h new file mode 100644 index 00000000000..3bba3f78a70 --- /dev/null +++ b/frameworks/ets/ani/app/ui_extension_context/include/ets_ui_extension_context_module.h @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef OHOS_ABILITY_RUNTIME_ETS_UI_EXTENSION_CONTEXT_MODULE_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_EXTENSION_CONTEXT_MODULE_H + +#include + +#include "ani.h" +#include "native_engine/native_engine.h" +#include "ui_extension_context.h" + +namespace OHOS { +namespace AbilityRuntime { +class EtsUIExtensionContextModule { +public: + EtsUIExtensionContextModule() = default; + ~EtsUIExtensionContextModule() = default; + + EtsUIExtensionContextModule(const EtsUIExtensionContextModule&) = delete; + EtsUIExtensionContextModule(EtsUIExtensionContextModule&&) = delete; + EtsUIExtensionContextModule& operator=(const EtsUIExtensionContextModule&) = delete; + EtsUIExtensionContextModule& operator=(EtsUIExtensionContextModule&&) = delete; + + static ani_object NativeTransferStatic(ani_env *aniEnv, ani_object aniObj, ani_object input, ani_object type); + static ani_object NativeTransferDynamic(ani_env *aniEnv, ani_class aniCls, ani_object input); + static napi_value GetOrCreateDynamicObject(napi_env napiEnv, + std::shared_ptr uiExtensionContext); + static void RegisterContextObjectCreator(); + +private: + static bool IsInstanceOf(ani_env *aniEnv, ani_object aniObj); + static std::unique_ptr CreateNativeReference(napi_env napiEnv, + std::shared_ptr uiExtensionContext); + static ani_object CreateDynamicObject(ani_env *aniEnv, ani_class aniCls, + std::shared_ptr uiExtensionContext); + static napi_value AttachUIExtensionContext(napi_env env, void *value, void *hint); +}; + +void EtsUIExtensionContextModuleInit(ani_env *aniEnv); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_EXTENSION_CONTEXT_MODULE_H diff --git a/frameworks/ets/ani/app/ui_extension_context/src/ets_ui_extension_context_module.cpp b/frameworks/ets/ani/app/ui_extension_context/src/ets_ui_extension_context_module.cpp new file mode 100644 index 00000000000..decdc2626f1 --- /dev/null +++ b/frameworks/ets/ani/app/ui_extension_context/src/ets_ui_extension_context_module.cpp @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ets_ui_extension_context_module.h" + +#include +#include +#include + +#include "ani_base_context.h" +#include "ani_common_util.h" +#include "context_transfer.h" +#include "ets_context_utils.h" +#include "ets_error_utils.h" +#include "ets_ui_extension_context.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "interop_js/arkts_esvalue.h" +#include "interop_js/arkts_interop_js_api.h" +#include "interop_js/hybridgref_ani.h" +#include "interop_js/hybridgref_napi.h" +#include "js_runtime_utils.h" +#include "js_ui_extension_context.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char *ETS_UI_EXTENSION_CONTEXT_CLASS_NAME = "Lapplication/UIExtensionContext/UIExtensionContext;"; +} // namespace + +ani_object EtsUIExtensionContextModule::NativeTransferStatic(ani_env *aniEnv, ani_object, ani_object input, + ani_object type) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "transfer static UIExtensionContext"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null aniEnv"); + return nullptr; + } + + void *unwrapResult = nullptr; + bool success = arkts_esvalue_unwrap(aniEnv, input, &unwrapResult); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "failed to unwrap"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + if (unwrapResult == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null unwrapResult"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto context = reinterpret_cast *>(unwrapResult)->lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null UIExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto uiExtensionContext = Context::ConvertTo(context); + if (uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid uiExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto &bindingObj = uiExtensionContext->GetBindingObject(); + if (bindingObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null bindingObj"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto staticContext = bindingObj->Get(); + if (staticContext != nullptr) { + TAG_LOGI(AAFwkTag::CONTEXT, "there exist a staticContext"); + return reinterpret_cast(*staticContext); + } + + // if not exist, create a new one + std::string contextType; + if (!AppExecFwk::GetStdString(aniEnv, reinterpret_cast(type), contextType)) { + TAG_LOGE(AAFwkTag::JSNAPI, "GetStdString failed"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + TAG_LOGD(AAFwkTag::CONTEXT, "contextType %{public}s", contextType.c_str()); + + auto newContext = ContextTransfer::GetInstance().GetStaticObject(contextType, aniEnv, context); + if (newContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create uiExtensionContext failed"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + return newContext; +} + +std::unique_ptr EtsUIExtensionContextModule::CreateNativeReference(napi_env napiEnv, + std::shared_ptr uiExtensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (napiEnv == nullptr || uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null param"); + return nullptr; + } + + auto value = JsUIExtensionContext::CreateJsUIExtensionContext(napiEnv, uiExtensionContext); + auto systemModule = JsRuntime::LoadSystemModuleByEngine(napiEnv, "application.UIExtensionContext", &value, 1); + if (systemModule == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null systemModule"); + return nullptr; + } + + napi_value object = systemModule->GetNapiValue(); + if (!CheckTypeForNapiValue(napiEnv, object, napi_object)) { + TAG_LOGE(AAFwkTag::CONTEXT, "check type failed"); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(uiExtensionContext); + if (workContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null workContext"); + return nullptr; + } + auto status = napi_coerce_to_native_binding_object(napiEnv, object, DetachCallbackFunc, AttachUIExtensionContext, + workContext, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "coerce UIExtensionContext failed: %{public}d", status); + delete workContext; + return nullptr; + } + + status = napi_wrap(napiEnv, object, workContext, + [](napi_env, void *data, void *) { + TAG_LOGD(AAFwkTag::CONTEXT, "finalizer for weak_ptr UIExtensionContext"); + delete static_cast *>(data); + }, nullptr, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "wrap failed: %{public}d", status); + delete workContext; + return nullptr; + } + + return systemModule; +} + +napi_value EtsUIExtensionContextModule::GetOrCreateDynamicObject(napi_env napiEnv, + std::shared_ptr uiExtensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (napiEnv == nullptr || uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null param"); + return nullptr; + } + + // if sub-thread, create a new uiExtensionContext and return + if (getpid() != syscall(SYS_gettid)) { + auto subThreadObj = static_cast( + uiExtensionContext->GetSubThreadObject(static_cast(napiEnv))); + if (subThreadObj != nullptr) { + return subThreadObj->Get(); + } + auto subThreadRef = CreateNativeReference(napiEnv, uiExtensionContext); + if (subThreadRef == nullptr) { + return nullptr; + } + auto newObject = subThreadRef->Get(); + uiExtensionContext->BindSubThreadObject( + static_cast(napiEnv), static_cast(subThreadRef.release())); + return newObject; + } + + // if main-thread, get bindingObj firstly + auto &bindingObj = uiExtensionContext->GetBindingObject(); + if (bindingObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null bindingObj"); + return nullptr; + } + + // if main-thread bindingObj exist, return it directly + auto dynamicContext = bindingObj->Get(); + if (dynamicContext != nullptr) { + TAG_LOGI(AAFwkTag::UIABILITY, "there exist a dynamicContext"); + return dynamicContext->Get(); + } + + // if main-thread bindingObj didn't exist, create and bind + auto nativeRef = CreateNativeReference(napiEnv, uiExtensionContext); + if (nativeRef == nullptr) { + return nullptr; + } + + auto object = nativeRef->Get(); + uiExtensionContext->Bind(nativeRef.release()); + return object; +} + +ani_object EtsUIExtensionContextModule::NativeTransferDynamic(ani_env *aniEnv, ani_class aniCls, ani_object input) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "transfer dynamic UIExtensionContext"); + if (!IsInstanceOf(aniEnv, input)) { + TAG_LOGE(AAFwkTag::CONTEXT, "not UIExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + auto context = AbilityRuntime::GetStageModeContext(aniEnv, input); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + std::shared_ptr uiExtensionContext = Context::ConvertTo(context); + if (uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid uiExtensionContext"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + ani_object object = CreateDynamicObject(aniEnv, aniCls, uiExtensionContext); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid object"); + EtsErrorUtil::ThrowEtsTransferClassError(aniEnv); + return nullptr; + } + + return object; +} + +ani_object EtsUIExtensionContextModule::CreateDynamicObject(ani_env *aniEnv, ani_class aniCls, + std::shared_ptr uiExtensionContext) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + std::string contextType; + if (!AppExecFwk::GetStaticFieldString(aniEnv, aniCls, "contextType", contextType)) { + TAG_LOGE(AAFwkTag::CONTEXT, "get context type failed"); + return nullptr; + } + TAG_LOGD(AAFwkTag::CONTEXT, "contextType %{public}s", contextType.c_str()); + + // get napiEnv from aniEnv + napi_env napiEnv = {}; + if (!arkts_napi_scope_open(aniEnv, &napiEnv)) { + TAG_LOGE(AAFwkTag::CONTEXT, "arkts_napi_scope_open failed"); + return nullptr; + } + + // create normal UIExtensionContext + auto contextObj = ContextTransfer::GetInstance().GetDynamicObject(contextType, napiEnv, uiExtensionContext); + if (contextObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create UIExtensionContext failed"); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + hybridgref ref = nullptr; + bool success = hybridgref_create_from_napi(napiEnv, contextObj, &ref); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "hybridgref_create_from_napi failed"); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + ani_object result = nullptr; + success = hybridgref_get_esvalue(aniEnv, ref, &result); + if (!success) { + TAG_LOGE(AAFwkTag::CONTEXT, "hybridgref_get_esvalue failed"); + hybridgref_delete_from_napi(napiEnv, ref); + arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr); + return nullptr; + } + + hybridgref_delete_from_napi(napiEnv, ref); + + if (!arkts_napi_scope_close_n(napiEnv, 0, nullptr, nullptr)) { + TAG_LOGE(AAFwkTag::CONTEXT, "arkts_napi_scope_close_n failed"); + return nullptr; + } + + return result; +} + +bool EtsUIExtensionContextModule::IsInstanceOf(ani_env *aniEnv, ani_object aniObj) +{ + ani_class cls {}; + ani_status status = ANI_ERROR; + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null aniEnv"); + return false; + } + if ((status = aniEnv->FindClass(ETS_UI_EXTENSION_CONTEXT_CLASS_NAME, &cls)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass status: %{public}d", status); + return false; + } + ani_boolean isInstanceOf = false; + if ((status = aniEnv->Object_InstanceOf(aniObj, cls, &isInstanceOf)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_InstanceOf status: %{public}d", status); + return false; + } + return isInstanceOf; +} + +void EtsUIExtensionContextModule::RegisterContextObjectCreator() +{ + ContextTransfer::GetInstance().RegisterStaticObjectCreator("UIExtensionContext", + [](ani_env *aniEnv, std::shared_ptr context) -> ani_object { + auto uiExtensionContext = Context::ConvertTo(context); + if (uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid uiExtensionContext"); + return nullptr; + } + auto newContext = CreateEtsUIExtensionContext(aniEnv, uiExtensionContext); + if (newContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "create uiExtensionContext failed"); + return nullptr; + } + return newContext; + }); + + ContextTransfer::GetInstance().RegisterDynamicObjectCreator("UIExtensionContext", + [](napi_env napiEnv, std::shared_ptr context) -> napi_value { + auto uiExtensionContext = Context::ConvertTo(context); + if (uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid uiExtensionContext"); + return nullptr; + } + + auto object = EtsUIExtensionContextModule::GetOrCreateDynamicObject(napiEnv, uiExtensionContext); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "get or create object failed"); + return nullptr; + } + return object; + }); +} + +napi_value EtsUIExtensionContextModule::AttachUIExtensionContext(napi_env env, void *value, void *hint) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "attach uiExtensionContext"); + if (env == nullptr || value == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "invalid params"); + return nullptr; + } + auto ptr = reinterpret_cast *>(value)->lock(); + if (ptr == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ptr"); + return nullptr; + } + + auto object = JsUIExtensionContext::CreateJsUIExtensionContext(env, ptr); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null object"); + return nullptr; + } + auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "application.UIExtensionContext", &object, 1); + if (systemModule == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null systemModule"); + return nullptr; + } + auto contextObj = systemModule->GetNapiValue(); + if (!CheckTypeForNapiValue(env, contextObj, napi_object)) { + TAG_LOGE(AAFwkTag::CONTEXT, "not napi object"); + return nullptr; + } + + auto status = napi_coerce_to_native_binding_object( + env, contextObj, DetachCallbackFunc, AttachUIExtensionContext, value, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "coerce uiExtensionContext failed: %{public}d", status); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(ptr); + status = napi_wrap(env, contextObj, workContext, + [](napi_env, void *data, void *) { + TAG_LOGD(AAFwkTag::CONTEXT, "finalizer for weak_ptr uiExtensionContext"); + delete static_cast *>(data); + }, + nullptr, nullptr); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::CONTEXT, "wrap uiExtensionContext failed: %{public}d", status); + delete workContext; + return nullptr; + } + + return contextObj; +} + +void EtsUIExtensionContextModuleInit(ani_env *aniEnv) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "Init UIExtensionContext kit"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null ani env"); + return; + } + + ani_class uiExtensionContextCls = nullptr; + auto status = aniEnv->FindClass(ETS_UI_EXTENSION_CONTEXT_CLASS_NAME, &uiExtensionContextCls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass UIExtensionContext failed status: %{public}d", status); + return; + } + + std::array nativeFuncs = { + ani_native_function { "nativeTransferStatic", "Lstd/interop/ESValue;Lstd/core/String;:Lstd/core/Object;", + reinterpret_cast(EtsUIExtensionContextModule::NativeTransferStatic) }, + ani_native_function { "nativeTransferDynamic", "Lstd/core/Object;:Lstd/interop/ESValue;", + reinterpret_cast(EtsUIExtensionContextModule::NativeTransferDynamic) }, + }; + status = aniEnv->Class_BindStaticNativeMethods(uiExtensionContextCls, nativeFuncs.data(), nativeFuncs.size()); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Class_BindStaticNativeMethods failed status: %{public}d", status); + return; + } + + EtsUIExtensionContextModule::RegisterContextObjectCreator(); + TAG_LOGD(AAFwkTag::CONTEXT, "Init UIExtensionContext kit end"); +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "ANI_Constructor"); + if (vm == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null vm"); + return ANI_ERROR; + } + + ani_env *aniEnv = nullptr; + ani_status status = vm->GetEnv(ANI_VERSION_1, &aniEnv); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetEnv failed status: %{public}d", status); + return ANI_NOT_FOUND; + } + + EtsUIExtensionContextModuleInit(aniEnv); + *result = ANI_VERSION_1; + TAG_LOGD(AAFwkTag::CONTEXT, "ANI_Constructor finish"); + return ANI_OK; +} +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/auto_fill_extension_ability/src/ets_auto_fill_extension_context.cpp b/frameworks/ets/ani/auto_fill_extension_ability/src/ets_auto_fill_extension_context.cpp index 634c62e6621..eff9495ef1d 100644 --- a/frameworks/ets/ani/auto_fill_extension_ability/src/ets_auto_fill_extension_context.cpp +++ b/frameworks/ets/ani/auto_fill_extension_ability/src/ets_auto_fill_extension_context.cpp @@ -207,6 +207,17 @@ ani_object EtsAutoFillExtensionContext::CreateEtsAutoFillExtensionContext(ani_en } AbilityRuntime::ContextUtil::CreateEtsBaseContext(env, cls, contextObj, context); AbilityRuntime::CreateEtsExtensionContext(env, cls, contextObj, context, context->GetAbilityInfo()); + ani_ref *contextGlobalRef = new (std::nothrow) ani_ref; + if (contextGlobalRef == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "new contextGlobalRef failed"); + return nullptr; + } + if ((status = env->GlobalReference_Create(contextObj, contextGlobalRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILL_EXT, "GlobalReference_Create failed status: %{public}d", status); + delete contextGlobalRef; + return nullptr; + } + context->Bind(contextGlobalRef); return contextObj; } } diff --git a/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_context.cpp b/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_context.cpp index b6a642b218d..ed0636e44d4 100644 --- a/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_context.cpp +++ b/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_context.cpp @@ -1180,6 +1180,19 @@ ani_object CreateEtsUIExtensionContext(ani_env *env, std::shared_ptrGetAbilityInfo()); + ani_ref *contextGlobalRef = new (std::nothrow) ani_ref; + if (contextGlobalRef == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "new contextGlobalRef failed"); + delete workContext; + return nullptr; + } + if ((status = env->GlobalReference_Create(contextObj, contextGlobalRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "GlobalReference_Create failed status: %{public}d", status); + delete contextGlobalRef; + delete workContext; + return nullptr; + } + context->Bind(contextGlobalRef); return contextObj; } diff --git a/frameworks/ets/ets/application/AutoFillExtensionContext.ets b/frameworks/ets/ets/application/AutoFillExtensionContext.ets index 39f06b3b415..5376b6a50af 100644 --- a/frameworks/ets/ets/application/AutoFillExtensionContext.ets +++ b/frameworks/ets/ets/application/AutoFillExtensionContext.ets @@ -34,6 +34,10 @@ let destroyRegister = new FinalizationRegistry(callback); let unregisterToken = new object(); export default class AutoFillExtensionContext extends ExtensionContext { + static { + loadLibrary("auto_fill_extension_context_ani_kit.z"); + } + autoFillExtensionContextPtr: long = 0; private cleaner: Cleaner | null = null; constructor(ptr: long) { @@ -69,4 +73,23 @@ export default class AutoFillExtensionContext extends ExtensionContext { }); return p; } + + private static native nativeTransferStatic(input: ESValue, type: string): Object; + private static native nativeTransferDynamic(input: Object): ESValue; + private static contextType: string = 'AutoFillExtensionContext'; + + static transferStatic(input: Any): Object { + let type: string = ''; + try { + type = ESValue.wrap(input).getProperty('__context_impl__').getProperty('contextType').toString(); + } catch (err) { + console.log('transferStatic err: ' + err.toString()); + } + console.log('transferStatic type: ' + type); + return AutoFillExtensionContext.nativeTransferStatic(ESValue.wrap(input), type); + } + + static transferDynamic(input: Object): Any { + return AutoFillExtensionContext.nativeTransferDynamic(input).unwrap(); + } } \ No newline at end of file diff --git a/frameworks/ets/ets/application/ExtensionContext.ets b/frameworks/ets/ets/application/ExtensionContext.ets index f8c788f8160..d8d82502e78 100644 --- a/frameworks/ets/ets/application/ExtensionContext.ets +++ b/frameworks/ets/ets/application/ExtensionContext.ets @@ -19,7 +19,30 @@ import { Configuration } from '@ohos.app.ability.Configuration' import { HapModuleInfo } from 'bundleManager.HapModuleInfo' export default class ExtensionContext extends Context { - extensionAbilityInfo!: ExtensionAbilityInfo; - config!: Configuration; - currentHapModuleInfo!: HapModuleInfo; + static { + loadLibrary("extension_context_ani_kit.z"); + } + + extensionAbilityInfo!: ExtensionAbilityInfo; + config!: Configuration; + currentHapModuleInfo!: HapModuleInfo; + + private static native nativeTransferStatic(input: ESValue, type: string): Object; + private static native nativeTransferDynamic(input: Object): ESValue; + private static contextType: string = 'ExtensionContext'; + + static transferStatic(input: Any): Object { + let type: string = ''; + try { + type = ESValue.wrap(input).getProperty('__context_impl__').getProperty('contextType').toString(); + } catch (err) { + console.log('transferStatic err: ' + err.toString()); + } + console.log('transferStatic type: ' + type); + return ExtensionContext.nativeTransferStatic(ESValue.wrap(input), type); + } + + static transferDynamic(input: Object): Any { + return ExtensionContext.nativeTransferDynamic(input).unwrap(); + } } \ No newline at end of file diff --git a/frameworks/ets/ets/application/UIExtensionContext.ets b/frameworks/ets/ets/application/UIExtensionContext.ets index 6f2d70e1c4e..d7c2db1ae45 100644 --- a/frameworks/ets/ets/application/UIExtensionContext.ets +++ b/frameworks/ets/ets/application/UIExtensionContext.ets @@ -39,6 +39,10 @@ let destroyRegister = new FinalizationRegistry(Cleaner.callback) let unregisterToken = new object() export default class UIExtensionContext extends ExtensionContext { + static { + loadLibrary("ui_extension_context_ani_kit.z"); + } + nativeExtensionContext:long = 0; private cleaner: Cleaner | null = null; registerCleaner(ptr: long): void { @@ -75,6 +79,9 @@ export default class UIExtensionContext extends ExtensionContext { options?: OpenLinkOptions, callback?: AsyncCallbackWrapper): void; private native nativeOpenAtomicService(appId: string, syncCallback: AsyncCallbackWrapper, options?: AtomicServiceOptions): void; + private static native nativeTransferStatic(input: ESValue, type: string): Object; + private static native nativeTransferDynamic(input: Object): ESValue; + private static contextType: string = 'UIExtensionContext'; terminateSelf(callback:AsyncCallback): void { let myCall = new AsyncCallbackWrapper(callback); @@ -328,4 +335,19 @@ export default class UIExtensionContext extends ExtensionContext { }); }); } + + static transferStatic(input: Any): Object { + let type: string = ''; + try { + type = ESValue.wrap(input).getProperty('__context_impl__').getProperty('contextType').toString(); + } catch (err) { + console.log('transferStatic err: ' + err.toString()); + } + console.log('transferStatic type: ' + type); + return UIExtensionContext.nativeTransferStatic(ESValue.wrap(input), type); + } + + static transferDynamic(input: Object): Any { + return UIExtensionContext.nativeTransferDynamic(input).unwrap(); + } } \ No newline at end of file diff --git a/frameworks/native/ability/ability_runtime/js_extension_context.cpp b/frameworks/native/ability/ability_runtime/js_extension_context.cpp index 1f1c4f2e0d7..e709ef0f8b1 100644 --- a/frameworks/native/ability/ability_runtime/js_extension_context.cpp +++ b/frameworks/native/ability/ability_runtime/js_extension_context.cpp @@ -81,6 +81,9 @@ napi_value CreateJsExtensionContext(napi_env env, const std::shared_ptr