From cf7cbb99239301eabd254c1e17c5bc827039259e Mon Sep 17 00:00:00 2001 From: Nathan Yang Date: Sat, 2 Aug 2025 16:11:35 +0800 Subject: [PATCH] feat: InteropAbilityLifecycleCallback Signed-off-by: Nathan Yang --- .../include/ets_application_context_utils.h | 6 + .../src/ets_application_context_utils.cpp | 129 +++++++ .../ets/ani/ui_ability/src/ets_ui_ability.cpp | 26 ++ ...bility.InteropAbilityLifecycleCallback.ets | 61 +++ frameworks/ets/ets/BUILD.gn | 18 + .../ets/application/ApplicationContext.ets | 9 + .../native/ability_runtime/js_ui_ability.cpp | 28 +- frameworks/native/appkit/BUILD.gn | 5 + .../context/application_context.cpp | 121 ++++++ ...ets_interop_ability_lifecycle_callback.cpp | 357 ++++++++++++++++++ .../context/interop_object.cpp | 120 ++++++ .../context/js_application_context_utils.cpp | 67 +++- .../js_interop_ability_lifecycle_callback.cpp | 196 ++++++++++ .../context/application_context.h | 14 + .../ets_interop_ability_lifecycle_callback.h | 66 ++++ .../interop_ability_lifecycle_callback.h | 39 ++ .../ability_runtime/context/interop_object.h | 50 +++ .../context/js_application_context_utils.h | 4 + .../js_interop_ability_lifecycle_callback.h | 56 +++ 19 files changed, 1370 insertions(+), 2 deletions(-) create mode 100644 frameworks/ets/ets/@ohos.app.ability.InteropAbilityLifecycleCallback.ets create mode 100644 frameworks/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.cpp create mode 100644 frameworks/native/appkit/ability_runtime/context/interop_object.cpp create mode 100644 frameworks/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.cpp create mode 100644 interfaces/kits/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.h create mode 100644 interfaces/kits/native/appkit/ability_runtime/context/interop_ability_lifecycle_callback.h create mode 100644 interfaces/kits/native/appkit/ability_runtime/context/interop_object.h create mode 100644 interfaces/kits/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.h diff --git a/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h b/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h index 6404b808e51..fe9bf6764cd 100644 --- a/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h +++ b/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h @@ -58,6 +58,10 @@ public: ani_object callback); static void NativeOffLifecycleCallbackSync(ani_env *env, ani_object aniObj, ani_string type, ani_int callbackId, ani_object callback); + static void NativeOnInteropLifecycleCallbackSync(ani_env *env, ani_object aniObj, ani_string type, + ani_object callback); + static void NativeOffInteropLifecycleCallbackSync(ani_env *env, ani_object aniObj, ani_string type, + ani_object callback); static void NativeOffApplicationStateChangeSync(ani_env *env, ani_object aniObj, ani_object callback); static void NativeOnApplicationStateChangeSync(ani_env *env, ani_object aniObj, ani_object callback); static void NativeOffEnvironmentSync(ani_env *env, ani_object aniObj, ani_int callbackId, ani_object callback); @@ -67,6 +71,8 @@ protected: private: ani_int RegisterAbilityLifecycleCallback(ani_env *env, ani_object callback); void UnregisterAbilityLifecycleCallback(ani_env *env, int32_t callbackId, ani_object callback); + void RegisterInteropAbilityLifecycleCallback(ani_env *env, ani_object callback); + void UnregisterInteropAbilityLifecycleCallback(ani_env *env, ani_object callback); void OnRestartApp(ani_env *env, ani_object aniObj, ani_object wantObj); void OnSetFont(ani_env *env, ani_object aniObj, ani_string font); void OnSetColorMode(ani_env *env, ani_object aniObj, ani_enum_item colorMode); diff --git a/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp b/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp index be8d6629027..8bd084483b9 100644 --- a/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp +++ b/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp @@ -14,11 +14,14 @@ */ #include "ets_application_context_utils.h" +#include + #include "ani_enum_convert.h" #include "application_context_manager.h" #include "ets_ability_lifecycle_callback.h" #include "ets_context_utils.h" #include "ets_error_utils.h" +#include "ets_interop_ability_lifecycle_callback.h" #include "ets_native_reference.h" #include "hilog_tag_wrapper.h" @@ -34,9 +37,13 @@ constexpr double ERROR_CODE_NULL_CALLBACK = -2; constexpr double ERROR_CODE_NULL_CONTEXT = -3; constexpr double ERROR_CODE_INVALID_PARAM = -4; const std::string TYPE_ABILITY_LIFECYCLE = "abilityLifecycle"; +const std::string TYPE_INTEROP_ABILITY_LIFECYCLE = "interopAbilityLifecycle"; } std::shared_ptr abilityLifecycleCallback_ = nullptr; +std::mutex g_interopAbilityLifecycleCallbackLock; +std::shared_ptr interopAbilityLifecycleCallback_ = nullptr; + void EtsApplicationContextUtils::Clean(ani_env *env, ani_object object) { TAG_LOGD(AAFwkTag::APPKIT, "Clean Call"); @@ -598,6 +605,33 @@ ani_int EtsApplicationContextUtils::NativeOnLifecycleCallbackSync(ani_env *env, return ani_int(ERROR_CODE_INVALID_PARAM); } +void EtsApplicationContextUtils::NativeOnInteropLifecycleCallbackSync(ani_env *env, + ani_object aniObj, ani_string type, ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "NativeOnInteropLifecycleCallbackSync Call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "env is nullptr"); + return; + } + auto etsContext = GeApplicationContext(env, aniObj); + if (etsContext == nullptr) { + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + std::string stdType; + if (!AppExecFwk::GetStdString(env, type, stdType)) { + TAG_LOGE(AAFwkTag::APPKIT, "parse type failed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Failed to parse param type. Type must be a string."); + return; + } + TAG_LOGD(AAFwkTag::APPKIT, "type=%{public}s", stdType.c_str()); + if (stdType == TYPE_INTEROP_ABILITY_LIFECYCLE) { + etsContext->RegisterInteropAbilityLifecycleCallback(env, callback); + return; + } + EtsErrorUtil::ThrowInvalidParamError(env, "Unknown type."); +} + ani_int EtsApplicationContextUtils::RegisterAbilityLifecycleCallback(ani_env *env, ani_object callback) { TAG_LOGI(AAFwkTag::APPKIT, "call RegisterAbilityLifecycleCallback"); @@ -627,6 +661,35 @@ ani_int EtsApplicationContextUtils::RegisterAbilityLifecycleCallback(ani_env *en return ani_int(callbackId); } +void EtsApplicationContextUtils::RegisterInteropAbilityLifecycleCallback(ani_env *env, ani_object callback) +{ + TAG_LOGI(AAFwkTag::APPKIT, "call RegisterInteropAbilityLifecycleCallback"); + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "applicationContext is null"); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + std::lock_guard lock(g_interopAbilityLifecycleCallbackLock); + if (interopAbilityLifecycleCallback_ != nullptr) { + auto result = interopAbilityLifecycleCallback_->Register(callback); + if (result < 0) { + TAG_LOGE(AAFwkTag::APPKIT, "register failed: %{public}d", result); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + } + return; + } + + interopAbilityLifecycleCallback_ = std::make_shared(env); + auto result = interopAbilityLifecycleCallback_->Register(callback); + if (result < 0) { + TAG_LOGE(AAFwkTag::APPKIT, "register failed: %{public}d", result); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + applicationContext->RegisterInteropAbilityLifecycleCallback(interopAbilityLifecycleCallback_); +} + void EtsApplicationContextUtils::NativeOffLifecycleCallbackSync(ani_env *env, ani_object aniObj, ani_string type, ani_int callbackId, ani_object callback) { @@ -661,6 +724,34 @@ void EtsApplicationContextUtils::NativeOffLifecycleCallbackSync(ani_env *env, (ani_int)AbilityErrorCode::ERROR_CODE_INVALID_PARAM, "Unknown type."), nullptr); } +void EtsApplicationContextUtils::NativeOffInteropLifecycleCallbackSync(ani_env *env, + ani_object aniObj, ani_string type, ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "NativeOffInteropLifecycleCallbackSync Call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "env is nullptr"); + return; + } + auto etsContext = GeApplicationContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "applicationContext is null"); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + std::string stdType; + if (!AppExecFwk::GetStdString(env, type, stdType)) { + TAG_LOGE(AAFwkTag::APPKIT, "parse type failed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Failed to parse param type. Type must be a string."); + return; + } + TAG_LOGD(AAFwkTag::APPKIT, "type=%{public}s", stdType.c_str()); + if (stdType == TYPE_INTEROP_ABILITY_LIFECYCLE) { + etsContext->UnregisterInteropAbilityLifecycleCallback(env, callback); + return; + } + EtsErrorUtil::ThrowInvalidParamError(env, "Unknown type."); +} + void EtsApplicationContextUtils::UnregisterAbilityLifecycleCallback(ani_env *env, int32_t callbackId, ani_object callback) { @@ -689,6 +780,38 @@ void EtsApplicationContextUtils::UnregisterAbilityLifecycleCallback(ani_env *env (ani_int)AbilityErrorCode::ERROR_CODE_INNER, "failed to unregister"), nullptr); } +void EtsApplicationContextUtils::UnregisterInteropAbilityLifecycleCallback(ani_env *env, ani_object callback) +{ + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "applicationContext is null"); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + + std::lock_guard lock(g_interopAbilityLifecycleCallbackLock); + if (interopAbilityLifecycleCallback_ == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "interopAbilityLifecycleCallback_ is null"); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + + ani_boolean isUndefined = true; + env->Reference_IsUndefined(callback, &isUndefined); + if (isUndefined) { + interopAbilityLifecycleCallback_->Unregister(); + } else if (!interopAbilityLifecycleCallback_->Unregister(callback)) { + TAG_LOGE(AAFwkTag::APPKIT, "Unregister failed"); + EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + + applicationContext->UnregisterInteropAbilityLifecycleCallback(interopAbilityLifecycleCallback_); + if (interopAbilityLifecycleCallback_->Empty()) { + interopAbilityLifecycleCallback_.reset(); + } +} + void EtsApplicationContextUtils::killAllProcesses(ani_env *env, ani_object aniObj, ani_boolean clearPageStack, ani_object callback) { @@ -931,6 +1054,12 @@ void EtsApplicationContextUtils::BindApplicationContextFunc(ani_env* aniEnv) reinterpret_cast(EtsApplicationContextUtils::GetCurrentInstanceKey)}, ani_native_function {"nativegetCurrentAppCloneIndex", ":I", reinterpret_cast(EtsApplicationContextUtils::GetCurrentAppCloneIndex)}, + ani_native_function {"nativeOnInteropLifecycleCallbackSync", + "Lstd/core/String;Lstd/core/Object;:V", + reinterpret_cast(EtsApplicationContextUtils::NativeOnInteropLifecycleCallbackSync)}, + ani_native_function {"nativeOffInteropLifecycleCallbackSync", + "Lstd/core/String;Lstd/core/Object;:V", + reinterpret_cast(EtsApplicationContextUtils::NativeOffInteropLifecycleCallbackSync)}, }; if ((status = aniEnv->Class_BindNativeMethods(contextClass, applicationContextFunctions.data(), applicationContextFunctions.size())) != ANI_OK) { diff --git a/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp b/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp index 3a62412911b..053109df5c3 100644 --- a/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp +++ b/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp @@ -420,6 +420,9 @@ void EtsUIAbility::OnStart(const Want &want, sptr sessionInf if (applicationContext != nullptr) { TAG_LOGD(AAFwkTag::UIABILITY, "call DispatchOnAbilityCreate"); applicationContext->DispatchOnAbilityCreate(etsAbilityObj_); + std::shared_ptr interopObject = std::make_shared(env, + etsAbilityObj_->aniRef); + applicationContext->DispatchOnAbilityCreate(interopObject); } TAG_LOGD(AAFwkTag::UIABILITY, "OnStart end"); } @@ -506,6 +509,9 @@ void EtsUIAbility::OnStopCallback() if (applicationContext != nullptr) { TAG_LOGD(AAFwkTag::UIABILITY, "call DispatchOnAbilityDestroy"); applicationContext->DispatchOnAbilityDestroy(etsAbilityObj_); + std::shared_ptr interopObject = std::make_shared( + etsRuntime_.GetAniEnv(), etsAbilityObj_->aniRef); + applicationContext->DispatchOnAbilityDestroy(interopObject); } } @@ -543,6 +549,11 @@ void EtsUIAbility::OnSceneCreated() if (applicationContext != nullptr) { TAG_LOGD(AAFwkTag::UIABILITY, "call DispatchOnWindowStageCreate"); applicationContext->DispatchOnWindowStageCreate(etsAbilityObj_, etsWindowStageObj_); + std::shared_ptr interopAbility = std::make_shared( + etsRuntime_.GetAniEnv(), etsAbilityObj_->aniRef); + std::shared_ptr interopWindowStage = std::make_shared( + etsRuntime_.GetAniEnv(), etsWindowStageObj_->aniRef); + applicationContext->DispatchOnWindowStageCreate(interopAbility, interopWindowStage); } TAG_LOGD(AAFwkTag::UIABILITY, "OnSceneCreated end"); } @@ -608,7 +619,16 @@ void EtsUIAbility::onSceneDestroyed() auto applicationContext = AbilityRuntime::Context::GetApplicationContext(); if (applicationContext != nullptr) { TAG_LOGD(AAFwkTag::UIABILITY, "call DispatchOnWindowStageDestroy"); + if (etsAbilityObj_ == nullptr || etsWindowStageObj_ == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null ability or window stage"); + return; + } applicationContext->DispatchOnWindowStageDestroy(etsAbilityObj_, etsWindowStageObj_); + std::shared_ptr interopAbility = std::make_shared( + etsRuntime_.GetAniEnv(), etsAbilityObj_->aniRef); + std::shared_ptr interopWindowStage = std::make_shared( + etsRuntime_.GetAniEnv(), etsWindowStageObj_->aniRef); + applicationContext->DispatchOnWindowStageDestroy(interopAbility, interopWindowStage); } TAG_LOGD(AAFwkTag::UIABILITY, "onSceneDestroyed end"); } @@ -658,6 +678,9 @@ void EtsUIAbility::CallOnForegroundFunc(const Want &want) if (applicationContext != nullptr) { TAG_LOGD(AAFwkTag::UIABILITY, "call DispatchOnAbilityForeground"); applicationContext->DispatchOnAbilityForeground(etsAbilityObj_); + std::shared_ptr interopObject = std::make_shared(env, + etsAbilityObj_->aniRef); + applicationContext->DispatchOnAbilityForeground(interopObject); } TAG_LOGD(AAFwkTag::UIABILITY, "CallOnForegroundFunc end"); } @@ -684,6 +707,9 @@ void EtsUIAbility::OnBackground() } TAG_LOGD(AAFwkTag::UIABILITY, "call DispatchOnAbilityBackground"); applicationContext->DispatchOnAbilityBackground(etsAbilityObj_); + std::shared_ptr interopObject = std::make_shared(env, + etsAbilityObj_->aniRef); + applicationContext->DispatchOnAbilityBackground(interopObject); } TAG_LOGD(AAFwkTag::UIABILITY, "OnBackground end"); } diff --git a/frameworks/ets/ets/@ohos.app.ability.InteropAbilityLifecycleCallback.ets b/frameworks/ets/ets/@ohos.app.ability.InteropAbilityLifecycleCallback.ets new file mode 100644 index 00000000000..8ed33751108 --- /dev/null +++ b/frameworks/ets/ets/@ohos.app.ability.InteropAbilityLifecycleCallback.ets @@ -0,0 +1,61 @@ +/* + * 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 window from '@ohos.window'; +import transfer from '@ohos.transfer'; + +type AbilityCallbackFn = (ability: Any) => void; +type WindowStageCallbackFn = (ability: Any, windowStage: window.WindowStage) => void; + +interface InteropAbilityLifecycleCallback { + onAbilityCreate: AbilityCallbackFn; + onAbilityForeground: AbilityCallbackFn; + onAbilityBackground: AbilityCallbackFn; + onAbilityDestroy: AbilityCallbackFn; + onWindowStageCreate: WindowStageCallbackFn; + onWindowStageDestroy: WindowStageCallbackFn; +} + +namespace InteropAbilityLifecycle { + function onAbilityCreateInner(ability: ESValue, callback: InteropAbilityLifecycleCallback): void { + callback.onAbilityCreate(ability.unwrap()); + }; + + function onAbilityForegroundInner(ability: ESValue, callback: InteropAbilityLifecycleCallback): void { + callback.onAbilityForeground(ability.unwrap()); + }; + + function onAbilityBackgroundInner(ability: ESValue, callback: InteropAbilityLifecycleCallback): void { + callback.onAbilityBackground(ability.unwrap()); + }; + + function onAbilityDestroyInner(ability: ESValue, callback: InteropAbilityLifecycleCallback): void { + callback.onAbilityDestroy(ability.unwrap()); + }; + + function onWindowStageCreateInner(ability: ESValue, windowStage: ESValue, + callback: InteropAbilityLifecycleCallback): void { + callback.onWindowStageCreate(ability.unwrap(), + transfer.transferStatic(windowStage.unwrap(), 'window.WindowStage') as window.WindowStage); + }; + + function onWindowStageDestroyInner(ability: ESValue, windowStage: ESValue, + callback: InteropAbilityLifecycleCallback): void { + callback.onWindowStageDestroy(ability.unwrap(), + transfer.transferStatic(windowStage.unwrap(), 'window.WindowStage') as window.WindowStage); + }; +} + +export default InteropAbilityLifecycleCallback; \ No newline at end of file diff --git a/frameworks/ets/ets/BUILD.gn b/frameworks/ets/ets/BUILD.gn index 07d5b3ee261..4e5b0b7ad70 100644 --- a/frameworks/ets/ets/BUILD.gn +++ b/frameworks/ets/ets/BUILD.gn @@ -905,6 +905,23 @@ ohos_prebuilt_etc("ability_runtime_ability_lifecycle_callback_abc_etc") { deps = [ ":ability_runtime_ability_lifecycle_callback_abc" ] } +generate_static_abc("ability_runtime_interop_ability_lifecycle_callback_abc") { + base_url = "./" + files = [ "./@ohos.app.ability.InteropAbilityLifecycleCallback.ets" ] + + is_boot_abc = "True" + device_dst_file = + "/system/framework/ability_runtime_interop_ability_lifecycle_callback_abc.abc" +} + +ohos_prebuilt_etc("ability_runtime_interop_ability_lifecycle_callback_abc_etc") { + source = "$target_out_dir/ability_runtime_interop_ability_lifecycle_callback_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ability_runtime_interop_ability_lifecycle_callback_abc" ] +} + generate_static_abc("ability_runtime_connect_options_abc") { base_url = "./" files = [ "./ability/connectOptions.ets" ] @@ -1275,6 +1292,7 @@ group("ets_packages") { ":ability_runtime_insight_intent_context_abc_etc", ":ability_runtime_insight_intent_driver_abc_etc", ":ability_runtime_insight_intent_executor_abc_etc", + ":ability_runtime_interop_ability_lifecycle_callback_abc_etc", ":ability_runtime_mission_info_abc_etc", ":ability_runtime_openLink_options_abc_etc", ":ability_runtime_share_extension_ability_abc_etc", diff --git a/frameworks/ets/ets/application/ApplicationContext.ets b/frameworks/ets/ets/application/ApplicationContext.ets index 5565205a386..87ad03d6aed 100644 --- a/frameworks/ets/ets/application/ApplicationContext.ets +++ b/frameworks/ets/ets/application/ApplicationContext.ets @@ -14,6 +14,7 @@ */ import AbilityLifecycleCallback from '../@ohos.app.ability.AbilityLifecycleCallback'; +import InteropAbilityLifecycleCallback from '../@ohos.app.ability.InteropAbilityLifecycleCallback'; import Context from 'application.Context' import { BusinessError, AsyncCallback } from '@ohos.base' import AsyncCallbackWrapper from '../utils/AbilityUtils'; @@ -307,4 +308,12 @@ export default class ApplicationContext extends Context { return this.nativeOnLifecycleCallbackSync(type, callback); } + public native nativeOnInteropLifecycleCallbackSync(type: string, callback: Object): void; + public native nativeOffInteropLifecycleCallbackSync(type: string, callback?: Object): void; + on(type: 'interopAbilityLifecycle', callback: InteropAbilityLifecycleCallback): void { + this.nativeOnInteropLifecycleCallbackSync(type, callback); + } + off(type: 'interopAbilityLifecycle', callback?: InteropAbilityLifecycleCallback): void { + this.nativeOffInteropLifecycleCallbackSync(type, callback); + } } diff --git a/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp b/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp index 51313253d1d..ee9659d65e2 100644 --- a/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp +++ b/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp @@ -461,6 +461,9 @@ void JsUIAbility::OnStart(const Want &want, sptr sessionInfo applicationContext = AbilityRuntime::Context::GetApplicationContext(); if (applicationContext != nullptr) { applicationContext->DispatchOnAbilityCreate(jsAbilityObj_); + std::shared_ptr interopObject = std::make_shared(env, + jsAbilityObj_->GetNapiValue()); + applicationContext->DispatchOnAbilityCreate(interopObject); } TAG_LOGD(AAFwkTag::UIABILITY, "end"); } @@ -598,6 +601,9 @@ void JsUIAbility::OnStopCallback() auto applicationContext = AbilityRuntime::Context::GetApplicationContext(); if (applicationContext != nullptr) { applicationContext->DispatchOnAbilityDestroy(jsAbilityObj_); + std::shared_ptr interopObject = std::make_shared( + jsRuntime_.GetNapiEnv(), jsAbilityObj_->GetNapiValue()); + applicationContext->DispatchOnAbilityDestroy(interopObject); } } @@ -640,6 +646,11 @@ void JsUIAbility::OnSceneCreated() applicationContext = AbilityRuntime::Context::GetApplicationContext(); if (applicationContext != nullptr) { applicationContext->DispatchOnWindowStageCreate(jsAbilityObj_, jsWindowStageObj_); + std::shared_ptr interopAbility = std::make_shared( + jsRuntime_.GetNapiEnv(), jsAbilityObj_->GetNapiValue()); + std::shared_ptr interopWindowStage = std::make_shared( + jsRuntime_.GetNapiEnv(), jsWindowStageObj_->GetNapiValue()); + applicationContext->DispatchOnWindowStageCreate(interopAbility, interopWindowStage); } TAG_LOGD(AAFwkTag::UIABILITY, "end"); @@ -724,7 +735,16 @@ void JsUIAbility::onSceneDestroyed() applicationContext = AbilityRuntime::Context::GetApplicationContext(); if (applicationContext != nullptr) { + if (jsAbilityObj_ == nullptr || jsWindowStageObj_ == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null ability or window stage"); + return; + } applicationContext->DispatchOnWindowStageDestroy(jsAbilityObj_, jsWindowStageObj_); + std::shared_ptr interopAbility = std::make_shared( + jsRuntime_.GetNapiEnv(), jsAbilityObj_->GetNapiValue()); + std::shared_ptr interopWindowStage = std::make_shared( + jsRuntime_.GetNapiEnv(), jsWindowStageObj_->GetNapiValue()); + applicationContext->DispatchOnWindowStageDestroy(interopAbility, interopWindowStage); } TAG_LOGD(AAFwkTag::UIABILITY, "end"); } @@ -789,6 +809,9 @@ void JsUIAbility::CallOnForegroundFunc(const Want &want) applicationContext = AbilityRuntime::Context::GetApplicationContext(); if (applicationContext != nullptr) { applicationContext->DispatchOnAbilityForeground(jsAbilityObj_); + std::shared_ptr interopObject = std::make_shared(env, + jsAbilityObj_->GetNapiValue()); + applicationContext->DispatchOnAbilityForeground(interopObject); } TAG_LOGD(AAFwkTag::UIABILITY, "end"); } @@ -818,8 +841,11 @@ void JsUIAbility::OnBackground() } applicationContext = AbilityRuntime::Context::GetApplicationContext(); - if (applicationContext != nullptr) { + if (applicationContext != nullptr && jsAbilityObj_ != nullptr) { applicationContext->DispatchOnAbilityBackground(jsAbilityObj_); + std::shared_ptr interopObject = std::make_shared( + jsRuntime_.GetNapiEnv(), jsAbilityObj_->GetNapiValue()); + applicationContext->DispatchOnAbilityBackground(interopObject); } auto want = GetWant(); if (want != nullptr) { diff --git a/frameworks/native/appkit/BUILD.gn b/frameworks/native/appkit/BUILD.gn index 314f54e138c..24198521423 100644 --- a/frameworks/native/appkit/BUILD.gn +++ b/frameworks/native/appkit/BUILD.gn @@ -433,6 +433,7 @@ ohos_shared_library("app_context") { branch_protector_ret = "pac_ret" include_dirs = [ + "${ability_runtime_path}/ets_environment/interfaces/inner_api", "${ability_runtime_path}/frameworks/ets/ani/ani_common/include", "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime/context", "${ability_runtime_path}/interfaces/kits/native/appkit/app", @@ -452,6 +453,9 @@ ohos_shared_library("app_context") { "${ability_runtime_native_path}/appkit/ability_runtime/context/context_impl.cpp", "${ability_runtime_native_path}/appkit/ability_runtime/context/environment_callback.cpp", "${ability_runtime_native_path}/appkit/ability_runtime/context/ets_ability_lifecycle_callback.cpp", + "${ability_runtime_native_path}/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.cpp", + "${ability_runtime_native_path}/appkit/ability_runtime/context/interop_object.cpp", + "${ability_runtime_native_path}/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.cpp", "${ability_runtime_native_path}/appkit/app/overlay_event_subscriber.cpp", "${ability_runtime_native_path}/appkit/app/sys_mgr_client.cpp", ] @@ -480,6 +484,7 @@ ohos_shared_library("app_context") { "napi:ace_napi", "resource_management:global_resmgr", "runtime_core:ani", + "runtime_core:ani_helpers", "samgr:samgr_proxy", ] public_external_deps = [ diff --git a/frameworks/native/appkit/ability_runtime/context/application_context.cpp b/frameworks/native/appkit/ability_runtime/context/application_context.cpp index d01e99c4713..6cf57e68463 100644 --- a/frameworks/native/appkit/ability_runtime/context/application_context.cpp +++ b/frameworks/native/appkit/ability_runtime/context/application_context.cpp @@ -29,6 +29,7 @@ namespace OHOS { namespace AbilityRuntime { const size_t ApplicationContext::CONTEXT_TYPE_ID(std::hash {} ("ApplicationContext")); std::vector> ApplicationContext::callbacks_; +std::vector> ApplicationContext::interopCallbacks_; std::vector> ApplicationContext::envCallbacks_; std::vector> ApplicationContext::applicationStateCallback_; @@ -70,6 +71,28 @@ void ApplicationContext::UnregisterAbilityLifecycleCallback( } } +void ApplicationContext::RegisterInteropAbilityLifecycleCallback( + std::shared_ptr callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "RegisterInteropAbilityLifecycleCallback called"); + if (callback == nullptr) { + return; + } + std::lock_guard lock(interopCallbackLock_); + interopCallbacks_.push_back(callback); +} + +void ApplicationContext::UnregisterInteropAbilityLifecycleCallback( + std::shared_ptr callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "called UnregisterInteropAbilityLifecycleCallback"); + std::lock_guard lock(interopCallbackLock_); + auto it = std::find(interopCallbacks_.begin(), interopCallbacks_.end(), callback); + if (it != interopCallbacks_.end()) { + interopCallbacks_.erase(it); + } +} + bool ApplicationContext::IsAbilityLifecycleCallbackEmpty() { std::lock_guard lock(callbackLock_); @@ -133,6 +156,22 @@ void ApplicationContext::DispatchOnAbilityCreate(std::shared_ptr ability) +{ + TAG_LOGD(AAFwkTag::APPKIT, "interop DispatchOnAbilityCreate"); + if (!ability) { + TAG_LOGE(AAFwkTag::APPKIT, "null ability"); + return; + } + std::lock_guard lock(interopCallbackLock_); + for (auto callback : interopCallbacks_) { + if (callback != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "call interop onAbilityCreate"); + callback->OnAbilityCreate(ability); + } + } +} + void ApplicationContext::DispatchOnWindowStageCreate(std::shared_ptr ability, std::shared_ptr windowStage) { @@ -163,6 +202,23 @@ void ApplicationContext::DispatchOnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage) +{ + TAG_LOGD(AAFwkTag::APPKIT, "interop DispatchOnWindowStageCreate"); + if (!ability || !windowStage) { + TAG_LOGE(AAFwkTag::APPKIT, "null ability or windowStage"); + return; + } + std::lock_guard lock(interopCallbackLock_); + for (auto callback : interopCallbacks_) { + if (callback != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "call interop OnWindowStageCreate"); + callback->OnWindowStageCreate(ability, windowStage); + } + } +} + void ApplicationContext::DispatchOnWindowStageDestroy(std::shared_ptr ability, std::shared_ptr windowStage) { @@ -193,6 +249,23 @@ void ApplicationContext::DispatchOnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage) +{ + TAG_LOGD(AAFwkTag::APPKIT, "interop DispatchOnWindowStageDestroy"); + if (!ability || !windowStage) { + TAG_LOGE(AAFwkTag::APPKIT, "null ability or windowStage"); + return; + } + std::lock_guard lock(interopCallbackLock_); + for (auto callback : interopCallbacks_) { + if (callback != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "call interop OnWindowStageDestroy"); + callback->OnWindowStageDestroy(ability, windowStage); + } + } +} + void ApplicationContext::DispatchWindowStageFocus(const std::shared_ptr &ability, const std::shared_ptr &windowStage) { @@ -253,6 +326,22 @@ void ApplicationContext::DispatchOnAbilityDestroy(std::shared_ptr ability) +{ + TAG_LOGD(AAFwkTag::APPKIT, "interop DispatchOnAbilityDestroy"); + if (!ability) { + TAG_LOGE(AAFwkTag::APPKIT, "null ability"); + return; + } + std::lock_guard lock(interopCallbackLock_); + for (auto callback : interopCallbacks_) { + if (callback != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "call interop OnAbilityDestroy"); + callback->OnAbilityDestroy(ability); + } + } +} + void ApplicationContext::DispatchOnAbilityForeground(std::shared_ptr ability) { if (!ability) { @@ -281,6 +370,22 @@ void ApplicationContext::DispatchOnAbilityForeground(std::shared_ptr ability) +{ + TAG_LOGD(AAFwkTag::APPKIT, "interop DispatchOnAbilityForeground"); + if (!ability) { + TAG_LOGE(AAFwkTag::APPKIT, "null ability"); + return; + } + std::lock_guard lock(interopCallbackLock_); + for (auto callback : interopCallbacks_) { + if (callback != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "call interop OnAbilityForeground"); + callback->OnAbilityForeground(ability); + } + } +} + void ApplicationContext::DispatchOnAbilityBackground(std::shared_ptr ability) { if (!ability) { @@ -309,6 +414,22 @@ void ApplicationContext::DispatchOnAbilityBackground(std::shared_ptr ability) +{ + TAG_LOGD(AAFwkTag::APPKIT, "interop DispatchOnAbilityBackground"); + if (!ability) { + TAG_LOGE(AAFwkTag::APPKIT, "null ability"); + return; + } + std::lock_guard lock(interopCallbackLock_); + for (auto callback : interopCallbacks_) { + if (callback != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "call interop OnAbilityBackground"); + callback->OnAbilityBackground(ability); + } + } +} + void ApplicationContext::DispatchOnAbilityContinue(const std::shared_ptr &ability) { if (!ability) { diff --git a/frameworks/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.cpp b/frameworks/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.cpp new file mode 100644 index 00000000000..f458802af2b --- /dev/null +++ b/frameworks/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.cpp @@ -0,0 +1,357 @@ +/* + * 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_interop_ability_lifecycle_callback.h" + +#include + +#include "ani.h" +#include "ets_exception_callback.h" +#include "ets_runtime.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char *SIGNATURE_NAMESPACE_INTEROP_ABILITY_LIFECYCLE = + "L@ohos/app/ability/InteropAbilityLifecycleCallback/InteropAbilityLifecycle;"; +constexpr const char *SIGNATURE_UIABILITY = + "Lstd/interop/ESValue;L@ohos/app/ability/InteropAbilityLifecycleCallback/InteropAbilityLifecycleCallback;:V"; +constexpr const char *SIGNATURE_UIABILITY_WINDOW_STAGE = + "Lstd/interop/ESValue;Lstd/interop/ESValue;" + "L@ohos/app/ability/InteropAbilityLifecycleCallback/InteropAbilityLifecycleCallback;:V"; +constexpr const int32_t ERROR_CODE_NULL_ENV = -1; +constexpr const int32_t ERROR_CODE_NULL_CALLBACK = -2; +constexpr const int32_t ERROR_CODE_NULL_REF = -3; +} +EtsInteropAbilityLifecycleCallback::EtsInteropAbilityLifecycleCallback(ani_env *env) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return; + } + ani_vm *aniVM = nullptr; + if (env->GetVM(&aniVM) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GetVM failed"); + return; + } + vm_ = aniVM; +} + +ani_env *EtsInteropAbilityLifecycleCallback::GetAniEnv() +{ + if (vm_ == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null vm_"); + return nullptr; + } + ani_env* env = nullptr; + if (vm_->GetEnv(ANI_VERSION_1, &env) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GetEnv failed"); + return nullptr; + } + return env; +} + +bool EtsInteropAbilityLifecycleCallback::Empty() +{ + std::lock_guard lock(callbacksLock_); + return callbacks_.empty(); +} + +void EtsInteropAbilityLifecycleCallback::CallObjectMethod(const char *methodName, + const char *signature, std::shared_ptr ability) +{ + ani_env *aniEnv = GetAniEnv(); + if (aniEnv == nullptr || ability == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null aniEnv or ability"); + return; + } + if (!ability->IsFromNapi()) { + TAG_LOGI(AAFwkTag::APPKIT, "not from js"); + return; + } + ani_ref abilityEsValue = ability->GetAniValue(aniEnv); + if (abilityEsValue == nullptr) { + TAG_LOGI(AAFwkTag::APPKIT, "null esvalue"); + return; + } + + ani_status status = ANI_ERROR; + ani_namespace ns; + if ((status = aniEnv->FindNamespace(SIGNATURE_NAMESPACE_INTEROP_ABILITY_LIFECYCLE, &ns)) != ANI_OK || + ns == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to find namespace, status=%{public}d", status); + return; + } + + ani_function callbackInnerFn = nullptr; + if ((status = aniEnv->Namespace_FindFunction(ns, methodName, signature, &callbackInnerFn)) != ANI_OK || + callbackInnerFn == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to find function %{public}s, status=%{public}d", methodName, status); + return; + } + + ani_value aniAbility {}; + aniAbility.r = reinterpret_cast(abilityEsValue); + std::lock_guard lock(callbacksLock_); + for (const auto &callback : callbacks_) { + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null callback"); + return; + } + if ((status = aniEnv->Function_Call_Void(callbackInnerFn, + aniAbility, reinterpret_cast(callback))) != ANI_OK) { + const EtsEnv::ETSErrorObject errorObj = GetETSErrorObject(); + TAG_LOGE(AAFwkTag::APPKIT, "failed to call function %{public}s,status=%{public}d\nname=%{public}s\n" + "message=%{public}s\nstack=%{public}s", methodName, status, errorObj.name.c_str(), + errorObj.message.c_str(), errorObj.stack.c_str()); + return; + } + } +} + +void EtsInteropAbilityLifecycleCallback::CallObjectMethod(const char *methodName, const char *signature, + std::shared_ptr ability, std::shared_ptr windowStage) +{ + ani_env *aniEnv = GetAniEnv(); + if (aniEnv == nullptr || ability == nullptr || windowStage == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null aniEnv or ability or windowStage"); + return; + } + if (!ability->IsFromNapi()) { + TAG_LOGI(AAFwkTag::APPKIT, "not from js"); + return; + } + ani_ref abilityEsValue = ability->GetAniValue(aniEnv); + ani_ref windowStageEsValue = windowStage->GetAniValue(aniEnv); + if (abilityEsValue == nullptr || windowStageEsValue == nullptr) { + TAG_LOGI(AAFwkTag::APPKIT, "null esvalue"); + return; + } + + ani_status status = ANI_ERROR; + ani_namespace ns = nullptr; + if ((status = aniEnv->FindNamespace(SIGNATURE_NAMESPACE_INTEROP_ABILITY_LIFECYCLE, &ns)) != ANI_OK || + ns == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to find namespace, status=%{public}d", status); + return; + } + + ani_function callbackInnerFn = nullptr; + if ((status = aniEnv->Namespace_FindFunction(ns, methodName, signature, &callbackInnerFn)) != ANI_OK || + callbackInnerFn == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to find function %{public}s, status=%{public}d", methodName, status); + return; + } + + ani_value aniAbility {}; + aniAbility.r = reinterpret_cast(abilityEsValue); + ani_value aniWindowStage {}; + aniWindowStage.r = reinterpret_cast(windowStageEsValue); + std::lock_guard lock(callbacksLock_); + for (const auto &callback : callbacks_) { + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null callback"); + return; + } + if ((status = aniEnv->Function_Call_Void(callbackInnerFn, + aniAbility, aniWindowStage, reinterpret_cast(callback))) != ANI_OK) { + const EtsEnv::ETSErrorObject errorObj = GetETSErrorObject(); + TAG_LOGE(AAFwkTag::APPKIT, "failed to call function %{public}s,status=%{public}d\nname=%{public}s\n" + "message=%{public}s\nstack=%{public}s", methodName, status, errorObj.name.c_str(), + errorObj.message.c_str(), errorObj.stack.c_str()); + return; + } + } +} + +int32_t EtsInteropAbilityLifecycleCallback::Register(ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "enter EtsInteropAbilityLifecycleCallback::Register"); + ani_env *aniEnv = GetAniEnv(); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null aniEnv"); + return ERROR_CODE_NULL_ENV; + } + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null aniEnv"); + return ERROR_CODE_NULL_CALLBACK; + } + ani_ref ref = nullptr; + ani_status status = ANI_ERROR; + if ((status = aniEnv->GlobalReference_Create(callback, &ref)) != ANI_OK || ref == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to create reference, status=%{public}d", status); + return ERROR_CODE_NULL_REF; + } + std::lock_guard lock(callbacksLock_); + callbacks_.push_back(ref); + return 0; +} + +EtsEnv::ETSErrorObject EtsInteropAbilityLifecycleCallback::GetETSErrorObject() +{ + TAG_LOGD(AAFwkTag::APPKIT, "GetETSErrorObject called"); + ani_boolean errorExists = ANI_FALSE; + ani_status status = ANI_ERROR; + auto aniEnv = GetAniEnv(); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return EtsEnv::ETSErrorObject(); + } + if ((status = aniEnv->ExistUnhandledError(&errorExists)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "ExistUnhandledError failed, status : %{public}d", status); + return EtsEnv::ETSErrorObject(); + } + if (errorExists == ANI_FALSE) { + TAG_LOGE(AAFwkTag::APPKIT, "not exist error"); + return EtsEnv::ETSErrorObject(); + } + ani_error aniError = nullptr; + if ((status = aniEnv->GetUnhandledError(&aniError)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GetUnhandledError failed, status : %{public}d", status); + return EtsEnv::ETSErrorObject(); + } + if ((status = aniEnv->ResetError()) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "ResetError failed, status : %{public}d", status); + return EtsEnv::ETSErrorObject(); + } + std::string errorMsg = GetErrorProperty(aniError, "message"); + std::string errorName = GetErrorProperty(aniError, "name"); + std::string errorStack = GetErrorProperty(aniError, "stack"); + const EtsEnv::ETSErrorObject errorObj = { + .name = errorName, + .message = errorMsg, + .stack = errorStack + }; + return errorObj; +} + +std::string EtsInteropAbilityLifecycleCallback::GetErrorProperty(ani_error aniError, const char *property) +{ + TAG_LOGD(AAFwkTag::APPKIT, "GetErrorProperty called"); + auto aniEnv = GetAniEnv(); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return ""; + } + std::string propertyValue; + ani_status status = ANI_ERROR; + ani_type errorType = nullptr; + if ((status = aniEnv->Object_GetType(aniError, &errorType)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "Object_GetType failed, status : %{public}d", status); + return propertyValue; + } + ani_method getterMethod = nullptr; + if ((status = aniEnv->Class_FindGetter(static_cast(errorType), property, &getterMethod)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "Class_FindGetter failed, status : %{public}d", status); + return propertyValue; + } + ani_ref aniRef = nullptr; + if ((status = aniEnv->Object_CallMethod_Ref(aniError, getterMethod, &aniRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "Object_CallMethod_Ref failed, status : %{public}d", status); + return propertyValue; + } + ani_string aniString = reinterpret_cast(aniRef); + ani_size sz {}; + if ((status = aniEnv->String_GetUTF8Size(aniString, &sz)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "String_GetUTF8Size failed, status : %{public}d", status); + return propertyValue; + } + propertyValue.resize(sz + 1); + if ((status = aniEnv->String_GetUTF8SubString( + aniString, 0, sz, propertyValue.data(), propertyValue.size(), &sz))!= ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "String_GetUTF8SubString failed, status : %{public}d", status); + return propertyValue; + } + propertyValue.resize(sz); + return propertyValue; +} + +bool EtsInteropAbilityLifecycleCallback::Unregister(ani_object aniCallback) +{ + ani_status status = ANI_ERROR; + ani_env *env = GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "GetEnv failed"); + return false; + } + if (aniCallback == nullptr) { + TAG_LOGI(AAFwkTag::APPKIT, "null aniCallback"); + std::lock_guard lock(callbacksLock_); + for (auto &callback : callbacks_) { + if (!callback) { + TAG_LOGE(AAFwkTag::APPKIT, "Invalid aniCallback"); + continue; + } + if ((status = env->GlobalReference_Delete(callback)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GlobalReference_Delete status: %{public}d", status); + } + } + callbacks_.clear(); + return true; + } + std::lock_guard lock(callbacksLock_); + for (auto iter = callbacks_.begin(); iter != callbacks_.end(); ++iter) { + if (*iter == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "Invalid callback"); + continue; + } + ani_boolean isEqual = false; + env->Reference_StrictEquals(aniCallback, *iter, &isEqual); + if (isEqual) { + if ((status = env->GlobalReference_Delete(*iter)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GlobalReference_Delete status: %{public}d", status); + return false; + } + callbacks_.erase(iter); + return true; + } + } + return false; +} + +void EtsInteropAbilityLifecycleCallback::OnAbilityCreate(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityCreateInner", SIGNATURE_UIABILITY, ability); +} + +void EtsInteropAbilityLifecycleCallback::OnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage) +{ + CallObjectMethod("onWindowStageCreateInner", SIGNATURE_UIABILITY_WINDOW_STAGE, ability, windowStage); +} + +void EtsInteropAbilityLifecycleCallback::OnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage) +{ + CallObjectMethod("onWindowStageDestroyInner", SIGNATURE_UIABILITY_WINDOW_STAGE, ability, windowStage); +} + +void EtsInteropAbilityLifecycleCallback::OnAbilityDestroy(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityDestroyInner", SIGNATURE_UIABILITY, ability); +} + +void EtsInteropAbilityLifecycleCallback::OnAbilityForeground(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityForegroundInner", SIGNATURE_UIABILITY, ability); +} + +void EtsInteropAbilityLifecycleCallback::OnAbilityBackground(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityBackgroundInner", SIGNATURE_UIABILITY, ability); +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/appkit/ability_runtime/context/interop_object.cpp b/frameworks/native/appkit/ability_runtime/context/interop_object.cpp new file mode 100644 index 00000000000..94bd69c80ef --- /dev/null +++ b/frameworks/native/appkit/ability_runtime/context/interop_object.cpp @@ -0,0 +1,120 @@ +/* + * 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 "interop_object.h" + +#include + +#include "ani.h" +#include "hilog_tag_wrapper.h" +#include "interop_js/hybridgref_ani.h" +#include "interop_js/hybridgref_napi.h" + +namespace OHOS { +namespace AbilityRuntime { +InteropObject::InteropObject(ani_env *env, ani_ref ref) +{ + if (env == nullptr || ref == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env or ref"); + return; + } + ani_vm *aniVM = nullptr; + if (env->GetVM(&aniVM) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GetVM failed"); + return; + } + vm_ = aniVM; + hybridgref_create_from_ani(env, ref, &ref_); +} + +InteropObject::InteropObject(napi_env env, napi_value value) +{ + if (env == nullptr || value == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env or value"); + return; + } + env_ = env; + hybridgref_create_from_napi(env, value, &ref_); +} + +InteropObject::~InteropObject() +{ + if (ref_ == nullptr) { + return; + } + if (vm_ != nullptr) { + ani_env *env = GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return; + } + hybridgref_delete_from_ani(env, ref_); + } + if (env_ != nullptr) { + hybridgref_delete_from_napi(env_, ref_); + } +} + +ani_env *InteropObject::GetAniEnv() +{ + if (vm_ == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null vm_"); + return nullptr; + } + ani_env* env = nullptr; + if (vm_->GetEnv(ANI_VERSION_1, &env) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "GetEnv failed"); + return nullptr; + } + return env; +} + +ani_object InteropObject::GetAniValue(ani_env *env) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return nullptr; + } + ani_object result = nullptr; + hybridgref_get_esvalue(env, ref_, &result); + return result; +} + + +napi_value InteropObject::GetNapiValue(napi_env env) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return nullptr; + } + napi_value result = nullptr; + if (!hybridgref_get_napi_value(env, ref_, &result)) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to get napi value"); + napi_get_undefined(env, &result); + } + return result; +} + +bool InteropObject::IsFromAni() +{ + return vm_ != nullptr; +} + +bool InteropObject::IsFromNapi() +{ + return env_ != nullptr; +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp b/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp index ef38cf80781..4e59458ca27 100644 --- a/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp +++ b/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp @@ -1371,6 +1371,9 @@ napi_value JsApplicationContextUtils::OnOn(napi_env env, NapiCallbackInfo& info) if (type == "applicationStateChange") { return OnOnApplicationStateChange(env, info); } + if (type == "interopAbilityLifecycle") { + return OnOnInteropAbilityLifecycle(env, info); + } TAG_LOGE(AAFwkTag::APPKIT, "on function type not match"); ThrowInvalidParamError(env, "Parse param callback failed, callback must be function."); return CreateJsUndefined(env); @@ -1378,7 +1381,6 @@ napi_value JsApplicationContextUtils::OnOn(napi_env env, NapiCallbackInfo& info) napi_value JsApplicationContextUtils::OnOff(napi_env env, NapiCallbackInfo& info) { - TAG_LOGD(AAFwkTag::APPKIT, "called"); if (info.argc < ARGC_ONE) { TAG_LOGE(AAFwkTag::APPKIT, "Not enough params"); ThrowInvalidParamError(env, "Not enough params."); @@ -1426,6 +1428,9 @@ napi_value JsApplicationContextUtils::OnOff(napi_env env, NapiCallbackInfo& info if (type == "environmentEvent") { return OnOffEnvironmentEventSync(env, info, callbackId); } + if (type == "interopAbilityLifecycle") { + return OnOffInteropAbilityLifecycle(env, info); + } TAG_LOGE(AAFwkTag::APPKIT, "off function type not match"); ThrowInvalidParamError(env, "Parse param callback failed, callback must be function."); return CreateJsUndefined(env); @@ -1488,6 +1493,66 @@ napi_value JsApplicationContextUtils::OnOffAbilityLifecycle( return result; } +napi_value JsApplicationContextUtils::OnOnInteropAbilityLifecycle( + napi_env env, NapiCallbackInfo& info) +{ + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null applicationContext"); + AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INVALID_PARAMETER); + return CreateJsUndefined(env); + } + + if (interopCallback_ != nullptr) { + TAG_LOGD(AAFwkTag::APPKIT, "interopCallback_ is not nullptr"); + auto result = interopCallback_->Register(info.argv[INDEX_ONE]); + if (result != ERR_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to register:%{public}d", result); + AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INVALID_PARAMETER); + } + return CreateJsUndefined(env); + } + interopCallback_ = std::make_shared(env); + auto result = interopCallback_->Register(info.argv[INDEX_ONE]); + if (result != ERR_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to register:%{public}d", result); + AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INVALID_PARAMETER); + return CreateJsUndefined(env); + } + applicationContext->RegisterInteropAbilityLifecycleCallback(interopCallback_); + return CreateJsUndefined(env); +} + +napi_value JsApplicationContextUtils::OnOffInteropAbilityLifecycle( + napi_env env, NapiCallbackInfo& info) +{ + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null applicationContext"); + AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INVALID_PARAMETER); + return CreateJsUndefined(env); + } + + if (interopCallback_ == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null interop callback"); + AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INVALID_PARAMETER); + return CreateJsUndefined(env); + } + + if (info.argc == ARGC_ONE || !CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { + interopCallback_->Unregister(); + } else if (!interopCallback_->Unregister(info.argv[INDEX_ONE])) { + TAG_LOGE(AAFwkTag::APPKIT, "call Unregister failed"); + AbilityRuntimeErrorUtil::Throw(env, ERR_ABILITY_RUNTIME_EXTERNAL_INVALID_PARAMETER); + return CreateJsUndefined(env); + } + + if (interopCallback_->Empty()) { + interopCallback_.reset(); + } + return CreateJsUndefined(env); +} + napi_value JsApplicationContextUtils::OnOffAbilityLifecycleEventSync( napi_env env, NapiCallbackInfo& info, int32_t callbackId) { diff --git a/frameworks/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.cpp b/frameworks/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.cpp new file mode 100644 index 00000000000..39286e8bf8e --- /dev/null +++ b/frameworks/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.cpp @@ -0,0 +1,196 @@ +/* + * 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 "js_interop_ability_lifecycle_callback.h" + +#include "hilog_tag_wrapper.h" +#include "js_runtime_utils.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr size_t ARGC_ONE = 1; +constexpr size_t ARGC_TWO = 2; +constexpr const int32_t ERROR_CODE_NULL_ENV = -1; +constexpr const int32_t ERROR_CODE_NULL_CALLBACK = -2; +} +JsInteropAbilityLifecycleCallback::JsInteropAbilityLifecycleCallback(napi_env env) : env_(env) +{} + +bool JsInteropAbilityLifecycleCallback::Empty() +{ + return callbacks_.empty(); +} + +void JsInteropAbilityLifecycleCallback::CallObjectMethod(const char *methodName, + std::shared_ptr ability) +{ + if (env_ == nullptr || ability == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env_ or ability"); + return; + } + if (!ability->IsFromAni()) { + TAG_LOGI(AAFwkTag::APPKIT, "not from ets"); + return; + } + napi_value jsAbility = ability->GetNapiValue(env_); + if (jsAbility == nullptr) { + TAG_LOGI(AAFwkTag::APPKIT, "null napi ability"); + return; + } + + napi_value argv[] = { jsAbility }; + for (const auto &callback : callbacks_) { + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null callback"); + return; + } + auto obj = callback->GetNapiValue(); + if (!CheckTypeForNapiValue(env_, obj, napi_object)) { + TAG_LOGE(AAFwkTag::APPKIT, "get object failed"); + return; + } + + napi_value method = nullptr; + napi_get_named_property(env_, obj, methodName, &method); + if (method == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null method %{public}s", methodName); + return; + } + napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr); + } +} + +void JsInteropAbilityLifecycleCallback::CallObjectMethod(const char *methodName, + std::shared_ptr ability, std::shared_ptr windowStage) +{ + if (env_ == nullptr || ability == nullptr || windowStage == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env_ or ability or windowStage"); + return; + } + if (!ability->IsFromAni()) { + TAG_LOGI(AAFwkTag::APPKIT, "not from ets"); + return; + } + napi_value jsAbility = ability->GetNapiValue(env_); + napi_value jsWindowStage = windowStage->GetNapiValue(env_); + if (jsAbility == nullptr || jsWindowStage == nullptr) { + TAG_LOGI(AAFwkTag::APPKIT, "null napi ability or windowStage"); + return; + } + + napi_value argv[] = { jsAbility, jsWindowStage }; + for (const auto &callback : callbacks_) { + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null callback"); + return; + } + auto obj = callback->GetNapiValue(); + if (!CheckTypeForNapiValue(env_, obj, napi_object)) { + TAG_LOGE(AAFwkTag::APPKIT, "get object failed"); + return; + } + + napi_value method = nullptr; + napi_get_named_property(env_, obj, methodName, &method); + if (method == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null method %{public}s", methodName); + return; + } + napi_call_function(env_, obj, method, ARGC_TWO, argv, nullptr); + } +} + +int32_t JsInteropAbilityLifecycleCallback::Register(napi_value callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "enter JsInteropAbilityLifecycleCallback::Register"); + if (env_ == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env_"); + return ERROR_CODE_NULL_ENV; + } + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env_"); + return ERROR_CODE_NULL_CALLBACK; + } + napi_ref ref = nullptr; + napi_create_reference(env_, callback, 1, &ref); + callbacks_.push_back(std::shared_ptr(reinterpret_cast(ref))); + return 0; +} + +bool JsInteropAbilityLifecycleCallback::Unregister(napi_value jsCallback) +{ + TAG_LOGI(AAFwkTag::APPKIT, "Unregister"); + if (jsCallback == nullptr) { + TAG_LOGI(AAFwkTag::APPKIT, "null jsCallback"); + callbacks_.clear(); + return true; + } + + for (auto iter = callbacks_.begin(); iter != callbacks_.end(); ++iter) { + if (*iter == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "Invalid callback"); + continue; + } + + napi_value value = (*iter)->GetNapiValue(); + if (value == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null value"); + continue; + } + + bool isEqual = false; + napi_strict_equals(env_, value, jsCallback, &isEqual); + if (isEqual) { + callbacks_.erase(iter); + return true; + } + } + return false; +} + +void JsInteropAbilityLifecycleCallback::OnAbilityCreate(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityCreate", ability); +} + +void JsInteropAbilityLifecycleCallback::OnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage) +{ + CallObjectMethod("onWindowStageCreate", ability, windowStage); +} + +void JsInteropAbilityLifecycleCallback::OnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage) +{ + CallObjectMethod("onWindowStageDestroy", ability, windowStage); +} + +void JsInteropAbilityLifecycleCallback::OnAbilityDestroy(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityDestroy", ability); +} + +void JsInteropAbilityLifecycleCallback::OnAbilityForeground(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityForeground", ability); +} + +void JsInteropAbilityLifecycleCallback::OnAbilityBackground(std::shared_ptr ability) +{ + CallObjectMethod("onAbilityBackground", ability); +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/native/appkit/ability_runtime/context/application_context.h b/interfaces/kits/native/appkit/ability_runtime/context/application_context.h index 84d70c7f420..cbf6c445f5a 100644 --- a/interfaces/kits/native/appkit/ability_runtime/context/application_context.h +++ b/interfaces/kits/native/appkit/ability_runtime/context/application_context.h @@ -24,6 +24,8 @@ #include "context.h" #include "context_impl.h" #include "environment_callback.h" +#include "interop_ability_lifecycle_callback.h" + namespace OHOS { namespace AAFwk { class Want; @@ -38,6 +40,8 @@ public: ~ApplicationContext() = default; void RegisterAbilityLifecycleCallback(const std::shared_ptr &abilityLifecycleCallback); void UnregisterAbilityLifecycleCallback(const std::shared_ptr &abilityLifecycleCallback); + void RegisterInteropAbilityLifecycleCallback(std::shared_ptr callback); + void UnregisterInteropAbilityLifecycleCallback(std::shared_ptr callback); bool IsAbilityLifecycleCallbackEmpty(); void RegisterEnvironmentCallback(const std::shared_ptr &environmentCallback); void UnregisterEnvironmentCallback(const std::shared_ptr &environmentCallback); @@ -45,24 +49,32 @@ public: const std::weak_ptr &applicationStateChangeCallback); void DispatchOnAbilityCreate(std::shared_ptr ability); void DispatchOnAbilityCreate(std::shared_ptr ability); + void DispatchOnAbilityCreate(std::shared_ptr ability); void DispatchOnWindowStageCreate(std::shared_ptr ability, std::shared_ptr windowStage); void DispatchOnWindowStageCreate(std::shared_ptr ability, std::shared_ptr windowStage); + void DispatchOnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage); void DispatchOnWindowStageDestroy(std::shared_ptr ability, std::shared_ptr windowStage); void DispatchOnWindowStageDestroy(std::shared_ptr ability, std::shared_ptr windowStage); + void DispatchOnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage); void DispatchWindowStageFocus(const std::shared_ptr &ability, const std::shared_ptr &windowStage); void DispatchWindowStageUnfocus(const std::shared_ptr &ability, const std::shared_ptr &windowStage); void DispatchOnAbilityDestroy(std::shared_ptr ability); void DispatchOnAbilityDestroy(std::shared_ptr ability); + void DispatchOnAbilityDestroy(std::shared_ptr ability); void DispatchOnAbilityForeground(std::shared_ptr ability); void DispatchOnAbilityForeground(std::shared_ptr ability); + void DispatchOnAbilityForeground(std::shared_ptr ability); void DispatchOnAbilityBackground(std::shared_ptr ability); void DispatchOnAbilityBackground(std::shared_ptr ability); + void DispatchOnAbilityBackground(std::shared_ptr ability); void DispatchOnAbilityContinue(const std::shared_ptr &ability); void DispatchOnAbilityWillContinue(const std::shared_ptr &ability); void DispatchOnWindowStageWillRestore(const std::shared_ptr &ability, @@ -180,9 +192,11 @@ protected: private: std::shared_ptr contextImpl_; static std::vector> callbacks_; + static std::vector> interopCallbacks_; static std::vector> envCallbacks_; static std::vector> applicationStateCallback_; std::recursive_mutex callbackLock_; + std::mutex interopCallbackLock_; std::recursive_mutex envCallbacksLock_; std::recursive_mutex applicationStateCallbackLock_; bool applicationInfoUpdateFlag_ = false; diff --git a/interfaces/kits/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.h b/interfaces/kits/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.h new file mode 100644 index 00000000000..83c131afae8 --- /dev/null +++ b/interfaces/kits/native/appkit/ability_runtime/context/ets_interop_ability_lifecycle_callback.h @@ -0,0 +1,66 @@ +/* + * 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_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_ETS_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H + +#include +#include + +#include "interop_ability_lifecycle_callback.h" + +typedef class __ani_error *ani_error; + +namespace OHOS { +namespace EtsEnv { +struct ETSErrorObject; +} + +namespace AbilityRuntime { +class EtsInteropAbilityLifecycleCallback : public InteropAbilityLifecycleCallback, + public std::enable_shared_from_this { +public: + EtsInteropAbilityLifecycleCallback(ani_env *env); + + void OnAbilityCreate(std::shared_ptr ability) override; + void OnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage) override; + void OnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage) override; + void OnAbilityDestroy(std::shared_ptr ability) override; + void OnAbilityForeground(std::shared_ptr ability) override; + void OnAbilityBackground(std::shared_ptr ability) override; + + int32_t Register(ani_object callback); + bool Unregister(ani_object aniCallback = nullptr); + bool Empty(); + +private: + ani_env *GetAniEnv(); + void CallObjectMethod(const char *methodName, const char *signature, + std::shared_ptr ability); + void CallObjectMethod(const char *methodName, const char *signature, + std::shared_ptr ability, std::shared_ptr windowStage); + EtsEnv::ETSErrorObject GetETSErrorObject(); + std::string GetErrorProperty(ani_error aniError, const char *propertyName); + +private: + ani_vm *vm_ = nullptr; + std::mutex callbacksLock_; + std::vector callbacks_; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H \ No newline at end of file diff --git a/interfaces/kits/native/appkit/ability_runtime/context/interop_ability_lifecycle_callback.h b/interfaces/kits/native/appkit/ability_runtime/context/interop_ability_lifecycle_callback.h new file mode 100644 index 00000000000..f70c304c53c --- /dev/null +++ b/interfaces/kits/native/appkit/ability_runtime/context/interop_ability_lifecycle_callback.h @@ -0,0 +1,39 @@ +/* + * 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_CONTEXT_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_CONTEXT_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H + +#include + +#include "interop_object.h" + +namespace OHOS { +namespace AbilityRuntime { +class InteropAbilityLifecycleCallback { +public: + virtual ~InteropAbilityLifecycleCallback() {} + virtual void OnAbilityCreate(std::shared_ptr ability) {} + virtual void OnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage) {} + virtual void OnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage) {} + virtual void OnAbilityDestroy(std::shared_ptr ability) {} + virtual void OnAbilityForeground(std::shared_ptr ability) {} + virtual void OnAbilityBackground(std::shared_ptr ability) {} +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_CONTEXT_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H \ No newline at end of file diff --git a/interfaces/kits/native/appkit/ability_runtime/context/interop_object.h b/interfaces/kits/native/appkit/ability_runtime/context/interop_object.h new file mode 100644 index 00000000000..e558ebaef29 --- /dev/null +++ b/interfaces/kits/native/appkit/ability_runtime/context/interop_object.h @@ -0,0 +1,50 @@ +/* + * 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_INTEROP_OBJECT_H +#define OHOS_ABILITY_RUNTIME_INTEROP_OBJECT_H + +typedef struct __hybridgref *hybridgref; +typedef struct __ani_env ani_env; +typedef struct __ani_vm ani_vm; +typedef class __ani_object *ani_object; +typedef class __ani_ref *ani_ref; + +typedef struct napi_env__ *napi_env; +typedef struct napi_value__ *napi_value; + +namespace OHOS { +namespace AbilityRuntime { +class InteropObject { +public: + InteropObject(ani_env *env, ani_ref ref); + InteropObject(napi_env env, napi_value value); + virtual ~InteropObject(); + + ani_object GetAniValue(ani_env *env); + napi_value GetNapiValue(napi_env env); + bool IsFromAni(); + bool IsFromNapi(); + +private: + ani_env *GetAniEnv(); + + hybridgref ref_ = nullptr; + ani_vm *vm_ = nullptr; + napi_env env_ = nullptr; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_INTEROP_OBJECT_H diff --git a/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h b/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h index 25e79d04b18..51bb24310b9 100644 --- a/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h +++ b/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h @@ -23,6 +23,7 @@ #include "application_context.h" #include "application_state_change_callback.h" #include "js_ability_auto_startup_callback.h" +#include "js_interop_ability_lifecycle_callback.h" #include "native_engine/native_engine.h" #include "running_process_info.h" @@ -63,6 +64,8 @@ public: napi_value OnOff(napi_env env, NapiCallbackInfo& info); napi_value OnOnAbilityLifecycle(napi_env env, NapiCallbackInfo& info, bool isSync); napi_value OnOffAbilityLifecycle(napi_env env, NapiCallbackInfo& info, int32_t callbackId); + napi_value OnOnInteropAbilityLifecycle(napi_env env, NapiCallbackInfo& info); + napi_value OnOffInteropAbilityLifecycle(napi_env env, NapiCallbackInfo& info); napi_value OnOffAbilityLifecycleEventSync(napi_env env, NapiCallbackInfo& info, int32_t callbackId); napi_value OnOnEnvironment(napi_env env, NapiCallbackInfo& info, bool isSync); napi_value OnOffEnvironment(napi_env env, NapiCallbackInfo& info, int32_t callbackId); @@ -144,6 +147,7 @@ private: static void BindNativeApplicationContextOne(napi_env env, napi_value object); static void BindNativeApplicationContextTwo(napi_env env, napi_value object); std::shared_ptr callback_; + std::shared_ptr interopCallback_; std::shared_ptr envCallback_; std::shared_ptr applicationStateCallback_; std::mutex applicationStateCallbackLock_; diff --git a/interfaces/kits/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.h b/interfaces/kits/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.h new file mode 100644 index 00000000000..74ee4a8873e --- /dev/null +++ b/interfaces/kits/native/appkit/ability_runtime/context/js_interop_ability_lifecycle_callback.h @@ -0,0 +1,56 @@ +/* + * 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_JS_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_JS_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H + +#include + +#include "interop_ability_lifecycle_callback.h" + +class NativeReference; + +namespace OHOS { +namespace AbilityRuntime { +class JsInteropAbilityLifecycleCallback : public InteropAbilityLifecycleCallback, + public std::enable_shared_from_this { +public: + JsInteropAbilityLifecycleCallback(napi_env env); + + void OnAbilityCreate(std::shared_ptr ability) override; + void OnWindowStageCreate(std::shared_ptr ability, + std::shared_ptr windowStage) override; + void OnWindowStageDestroy(std::shared_ptr ability, + std::shared_ptr windowStage) override; + void OnAbilityDestroy(std::shared_ptr ability) override; + void OnAbilityForeground(std::shared_ptr ability) override; + void OnAbilityBackground(std::shared_ptr ability) override; + + int32_t Register(napi_value callback); + bool Unregister(napi_value jsVallback = nullptr); + bool Empty(); + +private: + void CallObjectMethod(const char *methodName, std::shared_ptr ability); + void CallObjectMethod(const char *methodName, std::shared_ptr ability, + std::shared_ptr windowStage); + +private: + napi_env env_ = nullptr; + std::vector> callbacks_; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_JS_INTEROP_ABILITY_LIFECYCLE_CALLBACK_H \ No newline at end of file -- Gitee