From aa5f2497615ff1021c915a055996464e5bebb94f Mon Sep 17 00:00:00 2001 From: zhangzezhong Date: Mon, 4 Aug 2025 23:20:16 -0400 Subject: [PATCH] add ui_servcie_extension Signed-off-by: zhangzezhong --- bundle.json | 1 + frameworks/ets/ani/ani_common/BUILD.gn | 1 + .../include/ets_extension_common.h | 0 .../src/ets_extension_common.cpp | 0 .../include/ets_service_extension_context.h | 3 +- .../src/ets_service_extension_context.cpp | 47 ++ .../ui_ability/include/ets_ability_context.h | 15 +- .../ets_ui_ability_servicehost_stub_impl.h | 37 + .../ets_uiservice_ability_connection.h | 62 ++ .../ui_ability/src/ets_ability_context.cpp | 247 +++++- .../ets_ui_ability_servicehost_stub_impl.cpp | 40 + .../src/ets_uiservice_ability_connection.cpp | 351 ++++++++ .../include/ets_ui_extension_context.h | 23 +- .../ets_ui_extension_servicehost_stub_impl.h | 38 + .../include/ets_uiservice_uiext_connection.h | 62 ++ .../src/ets_ui_extension.cpp | 3 +- .../src/ets_ui_extension_context.cpp | 254 +++++- ...ets_ui_extension_servicehost_stub_impl.cpp | 39 + .../src/ets_uiservice_uiext_connection.cpp | 336 ++++++++ .../connection/ets_ui_service_host_proxy.cpp | 174 ++++ .../connection/ets_ui_service_host_proxy.h | 45 ++ .../connection/ets_ui_service_proxy.cpp | 181 +++++ .../connection/ets_ui_service_proxy.h | 51 ++ .../include/ets_ui_service_extension.h | 245 ++++++ .../ets_ui_service_extension_context.h | 90 +++ .../ets_ui_service_extension_instance.h | 29 + .../src/ets_ui_service_extension.cpp | 710 +++++++++++++++++ .../src/ets_ui_service_extension_context.cpp | 751 ++++++++++++++++++ .../src/ets_ui_service_extension_instance.cpp | 54 ++ ....app.ability.UIServiceExtensionAbility.ets | 57 ++ frameworks/ets/ets/BUILD.gn | 85 ++ .../application/ServiceExtensionContext.ets | 17 + .../ets/ets/application/UIAbilityContext.ets | 56 +- .../ets/application/UIExtensionContext.ets | 51 ++ .../UIServiceExtensionConnectCallback.ets | 24 + .../application/UIServiceExtensionContext.ets | 133 ++++ .../ets/application/UIServiceHostProxy.ets | 25 + .../ets/ets/application/UIServiceProxy.ets | 25 + frameworks/native/ability/native/BUILD.gn | 174 +++- .../ui_service_extension.cpp | 3 + 40 files changed, 4494 insertions(+), 45 deletions(-) rename frameworks/ets/ani/{ui_extension_ability => ani_common}/include/ets_extension_common.h (100%) rename frameworks/ets/ani/{ui_extension_ability => ani_common}/src/ets_extension_common.cpp (100%) create mode 100644 frameworks/ets/ani/ui_ability/include/ets_ui_ability_servicehost_stub_impl.h create mode 100644 frameworks/ets/ani/ui_ability/include/ets_uiservice_ability_connection.h create mode 100644 frameworks/ets/ani/ui_ability/src/ets_ui_ability_servicehost_stub_impl.cpp create mode 100644 frameworks/ets/ani/ui_ability/src/ets_uiservice_ability_connection.cpp create mode 100644 frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_servicehost_stub_impl.h create mode 100644 frameworks/ets/ani/ui_extension_ability/include/ets_uiservice_uiext_connection.h create mode 100644 frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_servicehost_stub_impl.cpp create mode 100644 frameworks/ets/ani/ui_extension_ability/src/ets_uiservice_uiext_connection.cpp create mode 100644 frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.cpp create mode 100644 frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.h create mode 100644 frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.cpp create mode 100644 frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.h create mode 100644 frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension.h create mode 100644 frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_context.h create mode 100644 frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_instance.h create mode 100644 frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension.cpp create mode 100644 frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_context.cpp create mode 100644 frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_instance.cpp create mode 100644 frameworks/ets/ets/@ohos.app.ability.UIServiceExtensionAbility.ets create mode 100644 frameworks/ets/ets/application/UIServiceExtensionConnectCallback.ets create mode 100644 frameworks/ets/ets/application/UIServiceExtensionContext.ets create mode 100644 frameworks/ets/ets/application/UIServiceHostProxy.ets create mode 100644 frameworks/ets/ets/application/UIServiceProxy.ets diff --git a/bundle.json b/bundle.json index 1f9d759a7c8..7392e614597 100644 --- a/bundle.json +++ b/bundle.json @@ -131,6 +131,7 @@ "//foundation/ability/ability_runtime/frameworks/native/ability/native:service_extension_ani", "//foundation/ability/ability_runtime/frameworks/native/ability/native:ui_ability_ani", "//foundation/ability/ability_runtime/frameworks/native/ability/native:ui_extension_ani", + "//foundation/ability/ability_runtime/frameworks/native/ability/native:ui_service_extension_ani", "//foundation/ability/ability_runtime/frameworks/native/appkit:ability_stage_ani", "//foundation/ability/ability_runtime/frameworks/native/appkit:test_runner_ani", "//foundation/ability/ability_runtime/frameworks/native/child_process:child_process", diff --git a/frameworks/ets/ani/ani_common/BUILD.gn b/frameworks/ets/ani/ani_common/BUILD.gn index aa4d97e986e..25fa7b39bc2 100644 --- a/frameworks/ets/ani/ani_common/BUILD.gn +++ b/frameworks/ets/ani/ani_common/BUILD.gn @@ -61,6 +61,7 @@ ohos_shared_library("ani_common") { "src/ets_extension_context.cpp", "src/ets_free_install_observer.cpp", "src/ani_common_remote.cpp", + "src/ets_extension_common.cpp", ] cflags = [] diff --git a/frameworks/ets/ani/ui_extension_ability/include/ets_extension_common.h b/frameworks/ets/ani/ani_common/include/ets_extension_common.h similarity index 100% rename from frameworks/ets/ani/ui_extension_ability/include/ets_extension_common.h rename to frameworks/ets/ani/ani_common/include/ets_extension_common.h diff --git a/frameworks/ets/ani/ui_extension_ability/src/ets_extension_common.cpp b/frameworks/ets/ani/ani_common/src/ets_extension_common.cpp similarity index 100% rename from frameworks/ets/ani/ui_extension_ability/src/ets_extension_common.cpp rename to frameworks/ets/ani/ani_common/src/ets_extension_common.cpp diff --git a/frameworks/ets/ani/service_extension_ability/include/ets_service_extension_context.h b/frameworks/ets/ani/service_extension_ability/include/ets_service_extension_context.h index 33034dbb0b8..e59d4e2bb01 100644 --- a/frameworks/ets/ani/service_extension_ability/include/ets_service_extension_context.h +++ b/frameworks/ets/ani/service_extension_ability/include/ets_service_extension_context.h @@ -69,6 +69,7 @@ public: static void StartServiceExtensionAbility(ani_env *env, ani_object obj, ani_object wantObj, ani_object callbackobj); static void StopServiceExtensionAbility( ani_env *env, ani_object aniObj, ani_object wantObj, ani_object callbackobj); + static void StartUIServiceExtension(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object callback); std::weak_ptr GetAbilityContext() { @@ -85,7 +86,7 @@ private: ani_object callback); void AddFreeInstallObserver(ani_env *env, const AAFwk::Want &want, ani_object callbackObj, std::shared_ptr context); - + void OnStartUIServiceExtension(ani_env *env, ani_object wantObj, ani_object callback); std::weak_ptr context_; sptr freeInstallObserver_ = nullptr; }; diff --git a/frameworks/ets/ani/service_extension_ability/src/ets_service_extension_context.cpp b/frameworks/ets/ani/service_extension_ability/src/ets_service_extension_context.cpp index 965c6196e96..bc2f2ac230a 100644 --- a/frameworks/ets/ani/service_extension_ability/src/ets_service_extension_context.cpp +++ b/frameworks/ets/ani/service_extension_ability/src/ets_service_extension_context.cpp @@ -65,6 +65,9 @@ bool BindNativeMethods(ani_env *env, ani_class &cls) reinterpret_cast(EtsServiceExtensionContext::ConnectServiceExtensionAbility) }, ani_native_function { "nativeDisconnectServiceExtensionAbility", SIGNATURE_DISCONNECT_SERVICE_EXTENSION, reinterpret_cast(EtsServiceExtensionContext::DisconnectServiceExtensionAbility) }, + ani_native_function{"nativeStartUIServiceExtensionAbility", + "L@ohos/app/ability/Want/Want;Lutils/AbilityUtils/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsServiceExtensionContext::StartUIServiceExtension)}, }; if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK && status != ANI_ALREADY_BINDED) { @@ -255,6 +258,50 @@ void EtsServiceExtensionContext::DisconnectServiceExtensionAbility(ani_env *env, etsServiceExtensionContext->OnDisconnectServiceExtensionAbility(env, aniObj, connectId, callback); } +void EtsServiceExtensionContext::StartUIServiceExtension(ani_env *env, ani_object aniObj, + ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "StartUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + auto etsServiceExtensionContext = EtsServiceExtensionContext::GetEtsAbilityContext(env, aniObj); + if (etsServiceExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null etsServiceExtensionContext"); + return; + } + etsServiceExtensionContext->OnStartUIServiceExtension(env, wantObj, callback); +} + +void EtsServiceExtensionContext::OnStartUIServiceExtension(ani_env *env, ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, " OnStartUIServiceExtensioncalled"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::UI_EXT, "UnwrapWant failed"); + ani_object aniObject = EtsErrorUtil::CreateInvalidParamError(env, "Parse param want failed, want must be Want"); + AppExecFwk::AsyncCallback(env, callback, aniObject, nullptr); + return; + } + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT), nullptr); + return; + } + int32_t innerErrCode = static_cast(ERR_OK); + innerErrCode = context->StartUIServiceExtensionAbility(want); + TAG_LOGD(AAFwkTag::CONTEXT, "StartUIServiceExtensionAbility code:%{public}d", innerErrCode); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateErrorByNativeErr(env, innerErrCode), nullptr); +} + EtsServiceExtensionContext *EtsServiceExtensionContext::GetEtsAbilityContext( ani_env *env, ani_object aniObj) { diff --git a/frameworks/ets/ani/ui_ability/include/ets_ability_context.h b/frameworks/ets/ani/ui_ability/include/ets_ability_context.h index 4f8fe0a235a..5b88ae988b0 100644 --- a/frameworks/ets/ani/ui_ability/include/ets_ability_context.h +++ b/frameworks/ets/ani/ui_ability/include/ets_ability_context.h @@ -44,10 +44,12 @@ public: void RemoveConnectionObject(); ani_env *AttachCurrentThread(); void DetachCurrentThread(); + ani_ref GetEtsConnectionObject() { return etsConnectionRef_; } protected: + void ReleaseObjectReference(ani_ref etsObjRef); ani_vm *etsVm_ = nullptr; int32_t connectionId_ = -1; - ani_ref stsConnectionRef_ = nullptr; + ani_ref etsConnectionRef_ = nullptr; bool isAttachThread_ = false; }; @@ -126,6 +128,12 @@ public: static void NativeOnSetRestoreEnabled(ani_env *env, ani_object aniObj, ani_boolean aniEnabled); static void NativeChangeAbilityVisibility(ani_env *env, ani_object aniObj, ani_boolean isShow, ani_object callbackObj); + static void ConnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object wantObj, + ani_object uiServiceExtConCallbackObj, ani_object callback); + static void StartUIServiceExtension(ani_env *env, ani_object aniObj, + ani_object wantObj, ani_object callback); + static void DisconnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object proxyObj, + ani_object callback); #ifdef SUPPORT_GRAPHICS public: @@ -191,6 +199,11 @@ private: AAFwk::StartOptions &options, std::string appId, ani_object callbackObj); void OnStartAbilityWithAccount( ani_env *env, ani_object aniObj, ani_object aniWant, ani_int aniAccountId, ani_object aniOpt, ani_object call); + void OnConnectUIServiceExtension(ani_env *env, ani_object wantObj, ani_object uiServiceExtConCallbackObj, + ani_object callback); + void OnStartUIServiceExtension(ani_env *env, ani_object wantObj, ani_object callback); + void OnDisconnectUIServiceExtension(ani_env *env, ani_object proxyObj, ani_object callback); + bool CheckConnectAlreadyExist(ani_env *env, const AAFwk::Want& want, ani_object callback, ani_object myCallback); ani_env *env_ = nullptr; std::weak_ptr context_; diff --git a/frameworks/ets/ani/ui_ability/include/ets_ui_ability_servicehost_stub_impl.h b/frameworks/ets/ani/ui_ability/include/ets_ui_ability_servicehost_stub_impl.h new file mode 100644 index 00000000000..3521faf416b --- /dev/null +++ b/frameworks/ets/ani/ui_ability/include/ets_ui_ability_servicehost_stub_impl.h @@ -0,0 +1,37 @@ +/* + * 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_ABILITY_SERVICEHOST_STUB_IMPL_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_ABILITY_SERVICEHOST_STUB_IMPL_H + +#include "ets_ability_context.h" +#include "ets_uiservice_ability_connection.h" +#include "ui_service_host_stub.h" + +namespace OHOS { +namespace AbilityRuntime { +class EtsUIAbilityServiceHostStubImpl : public AAFwk::UIServiceHostStub { +public: + EtsUIAbilityServiceHostStubImpl(wptr conn); + ~EtsUIAbilityServiceHostStubImpl() = default; + virtual int32_t SendData(AAFwk::WantParams &data) override; + +protected: + wptr conn_; +}; + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_ABILITY_SERVICEHOST_STUB_IMPL_H diff --git a/frameworks/ets/ani/ui_ability/include/ets_uiservice_ability_connection.h b/frameworks/ets/ani/ui_ability/include/ets_uiservice_ability_connection.h new file mode 100644 index 00000000000..c0bf5ffd4a3 --- /dev/null +++ b/frameworks/ets/ani/ui_ability/include/ets_uiservice_ability_connection.h @@ -0,0 +1,62 @@ +/* + * 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_UISERVICE_ABILITY_CONNECTION_H +#define OHOS_ABILITY_RUNTIME_ETS_UISERVICE_ABILITY_CONNECTION_H + +#include "ets_ability_context.h" + +namespace OHOS { +namespace AbilityRuntime { +class EtsUIServiceExtAbilityConnection; +namespace EtsUIServiceConnection { +void RemoveUIServiceAbilityConnection(int64_t connectId); +int64_t InsertUIServiceAbilityConnection(sptr connection, const AAFwk::Want &want); +void FindUIServiceAbilityConnection(const int64_t& connectId, AAFwk::Want& want, + sptr& connection); +void FindUIServiceAbilityConnection(ani_env *env, const AAFwk::Want &want, ani_object callback, + sptr &connection); +} // namespace EtsUIServiceConnection + +class EtsUIAbilityServiceHostStubImpl; +class EtsUIServiceExtAbilityConnection : public ETSAbilityConnection { +public: + EtsUIServiceExtAbilityConnection(ani_vm *etsVm); + ~EtsUIServiceExtAbilityConnection(); + virtual void HandleOnAbilityConnectDone( + const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode); + virtual void HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode); + sptr GetServiceHostStub() { return serviceHostStub_; } + void SetProxyObject(ani_object proxy); + ani_ref GetProxyObject(); + void SetAniAsyncCallback_(ani_object myCallback); + void ResolveDuplicatedPendingCallbacks(ani_env *env, ani_object proxyObj); + void AddDuplicatedPendingCallback(ani_object myCallback); + void RejectDuplicatedPendingCallbacks(ani_env *env, int32_t error); + int32_t OnSendData(AAFwk::WantParams &data); + void HandleOnSendData(const AAFwk::WantParams &data); + static bool IsEtsCallbackObjectEquals(ani_env *env, ani_ref callback, ani_object value); +private: + void ReleaseReference(ani_env *env, ani_ref etsObjRef); + void CallObjectMethod(ani_env *env, const char *methodName, const char *signature, ...); + sptr serviceHostStub_; + ani_ref serviceProxyObject_; + ani_ref aniAsyncCallback_; + std::vector duplicatedPendingCallbacks_; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UISERVICE_ABILITY_CONNECTION_H + diff --git a/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp b/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp index 19aaa94e99f..773bd05d761 100644 --- a/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp +++ b/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp @@ -19,24 +19,27 @@ #include "ani_common_ability_result.h" #include "ani_common_configuration.h" -#include "ani_common_start_options.h" #include "ani_common_remote.h" +#include "ani_common_start_options.h" #include "ani_common_want.h" #include "ani_enum_convert.h" #include "ani_remote_object.h" #include "app_utils.h" #include "common_fun_ani.h" -#include "interop_js/arkts_esvalue.h" -#include "interop_js/hybridgref_ani.h" -#include "interop_js/hybridgref_napi.h" #include "ets_caller_complex.h" #include "ets_context_utils.h" #include "ets_error_utils.h" +#include "ets_ui_ability_servicehost_stub_impl.h" #include "ets_ui_extension_callback.h" +#include "ets_ui_service_proxy.h" +#include "ets_uiservice_ability_connection.h" #include "hilog_tag_wrapper.h" #include "hitrace_meter.h" #include "ipc_skeleton.h" #include "tokenid_kit.h" +#include "interop_js/arkts_esvalue.h" +#include "interop_js/hybridgref_ani.h" +#include "interop_js/hybridgref_napi.h" #include "want.h" #ifdef SUPPORT_GRAPHICS #include "pixel_map_taihe_ani.h" @@ -77,6 +80,7 @@ constexpr const char *SIGNATURE_START_ABILITY_WITH_ACCOUNT_OPTIONS = constexpr int32_t ARGC_ONE = 1; constexpr int32_t ARGC_TWO = 2; constexpr const char* SIGNATURE_RESTORE_WINDOW_STAGE = "Larkui/stateManagement/storage/localStorage/LocalStorage;:V"; +constexpr const char* UI_SERVICE_HOSTPROXY_KEY = "ohos.ability.params.UIServiceHostProxy"; int64_t RequestCodeFromStringToInt64(const std::string &requestCode) { @@ -1303,7 +1307,7 @@ ani_object EtsAbilityContext::OnStartAbilityByType( callback->SetEtsCallbackObject(startCallback); auto context = context_.lock(); if (context == nullptr) { - TAG_LOGE(AAFwkTag::UI_EXT, "null context"); + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); return EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); } #ifdef SUPPORT_SCREEN @@ -1456,7 +1460,6 @@ void EtsAbilityContext::OnRevokeDelegator(ani_env *env, ani_object aniObj, ani_o AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, innerErrCode), nullptr); } - void EtsAbilityContext::OnStartAbilityForResultWithAccount(ani_env *env, ani_object aniObj, ani_object wantObj, ani_int etsAccountId, ani_object startOptionsObj, ani_object callback) { @@ -1949,6 +1952,195 @@ void EtsAbilityContext::OnStartAbilityWithAccount( AppExecFwk::AsyncCallback(env, call, aniObject, nullptr); } +void EtsAbilityContext::ConnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object wantObj, + ani_object uiServiceExtConCallbackObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "ConnectUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + auto etsContext = GetEtsAbilityContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null etsContext"); + return; + } + etsContext->OnConnectUIServiceExtension(env, wantObj, uiServiceExtConCallbackObj, callback); +} + +void EtsAbilityContext::OnConnectUIServiceExtension(ani_env *env, ani_object wantObj, + ani_object uiServiceExtConCallbackObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "OnConnectUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + ani_vm *aniVM = nullptr; + if (env->GetVM(&aniVM) != ANI_OK) { + TAG_LOGE(AAFwkTag::DELEGATOR, "GetVM failed"); + return; + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::CONTEXT, "input param want failed"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateInvalidParamError(env, "Parse param want failed, want must be Want."), + AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + return; + } + if (CheckConnectAlreadyExist(env, want, uiServiceExtConCallbackObj, callback)) { + TAG_LOGE(AAFwkTag::CONTEXT, "duplicated"); + return; + } + sptr connection = sptr::MakeSptr(aniVM); + sptr stub = connection->GetServiceHostStub(); + want.SetParam(UI_SERVICE_HOSTPROXY_KEY, stub->AsObject()); + connection->SetConnectionRef(uiServiceExtConCallbackObj); + connection->SetAniAsyncCallback_(callback); + EtsUIServiceConnection::InsertUIServiceAbilityConnection(connection, want); + int64_t connectId = connection->GetConnectionId(); + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr( + env, static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)), + AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + EtsUIServiceConnection::RemoveUIServiceAbilityConnection(connectId); + return; + } + int32_t innerErrorCode = context->ConnectUIServiceExtensionAbility(want, connection); + TAG_LOGD(AAFwkTag::CONTEXT, "ConnectUIServiceExtensionAbility errcode: %{public}d.", innerErrorCode); + if (innerErrorCode != static_cast(AbilityErrorCode::ERROR_OK)) { + TAG_LOGE(AAFwkTag::CONTEXT, "errcode: %{public}d.", innerErrorCode); + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr( + env, static_cast(innerErrorCode)), AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + EtsUIServiceConnection::RemoveUIServiceAbilityConnection(connectId); + } +} + +void EtsAbilityContext::StartUIServiceExtension(ani_env *env, ani_object aniObj, + ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "StartUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + auto etsContext = GetEtsAbilityContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null etsContext"); + return; + } + etsContext->OnStartUIServiceExtension(env, wantObj, callback); +} + +void EtsAbilityContext::OnStartUIServiceExtension(ani_env *env, ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "OnStartUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::CONTEXT, "UnwrapWant failed"); + ani_object aniObject = EtsErrorUtil::CreateInvalidParamError(env, "Parse param want failed, want must be Want"); + AppExecFwk::AsyncCallback(env, callback, aniObject, nullptr); + return; + } + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT), nullptr); + return; + } + int32_t innerErrCode = static_cast(ERR_OK); + innerErrCode = context->StartUIServiceExtensionAbility(want); + TAG_LOGD(AAFwkTag::CONTEXT, "StartUIServiceExtensionAbility code:%{public}d", innerErrCode); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateErrorByNativeErr(env, innerErrCode), nullptr); +} + +void EtsAbilityContext::DisconnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object proxyObj, + ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "DisconnectUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + auto etsContext = GetEtsAbilityContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null etsContext"); + return; + } + etsContext->OnDisconnectUIServiceExtension(env, proxyObj, callback); +} + +void EtsAbilityContext::OnDisconnectUIServiceExtension(ani_env *env, ani_object proxyObj, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + AAFwk::EtsUIServiceProxy* proxy = AAFwk::EtsUIServiceProxy::GetEtsUIServiceProxy(env, proxyObj); + if (proxy == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null proxy"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateInvalidParamError(env, "Parameter verification failed"), nullptr); + return; + } + int64_t connectId = proxy->GetConnectionId(); + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT), nullptr); + return; + } + AAFwk::Want want; + sptr connection = nullptr; + EtsUIServiceConnection::FindUIServiceAbilityConnection(connectId, want, connection); + TAG_LOGD(AAFwkTag::CONTEXT, "connection:%{public}d.", static_cast(connectId)); + if (connection == nullptr) { + TAG_LOGW(AAFwkTag::CONTEXT, "null connection"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INNER), nullptr); + EtsUIServiceConnection::RemoveUIServiceAbilityConnection(connectId); + return; + } + context->DisconnectAbility(want, connection); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_OK), nullptr); +} + +bool EtsAbilityContext::CheckConnectAlreadyExist(ani_env *env, const AAFwk::Want& want, + ani_object callback, ani_object myCallback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "CheckConnectAlreadyExist called"); + sptr connection = nullptr; + EtsUIServiceConnection::FindUIServiceAbilityConnection(env, want, callback, connection); + if (connection == nullptr) { + TAG_LOGD(AAFwkTag::CONTEXT, "null connection"); + return false; + } + ani_ref proxy = connection->GetProxyObject(); + if (proxy == nullptr) { + TAG_LOGW(AAFwkTag::CONTEXT, "null proxy"); + connection->AddDuplicatedPendingCallback(myCallback); + } else { + TAG_LOGI(AAFwkTag::CONTEXT, "Resolve, got proxy object"); + AppExecFwk::AsyncCallback(env, reinterpret_cast(myCallback), + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr( + env, static_cast(AbilityErrorCode::ERROR_OK)), reinterpret_cast(proxy)); + } + return true; +} + namespace { bool BindNativeMethods(ani_env *env, ani_class &cls) { @@ -2065,6 +2257,9 @@ bool BindNativeMethods(ani_env *env, ani_class &cls) reinterpret_cast(EtsAbilityContext::StartAbilityForResultWithAccountResult) }, ani_native_function { "nativeRestoreWindowStage", SIGNATURE_RESTORE_WINDOW_STAGE, reinterpret_cast(EtsAbilityContext::RestoreWindowStage) }, + ani_native_function{"nativeStartUIServiceExtensionAbility", + "L@ohos/app/ability/Want/Want;Lutils/AbilityUtils/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::StartUIServiceExtension)}, }; if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK) { TAG_LOGE(AAFwkTag::CONTEXT, "Class_BindNativeMethods failed status: %{public}d", status); @@ -2148,13 +2343,30 @@ void ETSAbilityConnection::SetConnectionId(int32_t id) connectionId_ = id; } +void ETSAbilityConnection::ReleaseObjectReference(ani_ref etsObjRef) +{ + if (etsVm_ == nullptr || etsObjRef == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "etsVm_ or etsObjRef null"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetEnv status:%{public}d", status); + return; + } + if ((status = env->GlobalReference_Delete(etsObjRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "GlobalReference_Delete status: %{public}d", status); + } +} + void ETSAbilityConnection::RemoveConnectionObject() { - if (etsVm_ != nullptr && stsConnectionRef_ != nullptr) { + if (etsVm_ != nullptr && etsConnectionRef_ != nullptr) { ani_env *env = nullptr; if (etsVm_->GetEnv(ANI_VERSION_1, &env) == ANI_OK && env != nullptr) { - env->GlobalReference_Delete(stsConnectionRef_); - stsConnectionRef_ = nullptr; + env->GlobalReference_Delete(etsConnectionRef_); + etsConnectionRef_ = nullptr; } } } @@ -2166,8 +2378,8 @@ void ETSAbilityConnection::CallEtsFailed(int32_t errorCode) TAG_LOGE(AAFwkTag::CONTEXT, "null etsVm"); return; } - if (stsConnectionRef_ == nullptr) { - TAG_LOGE(AAFwkTag::CONTEXT, "null stsConnectionRef_"); + if (etsConnectionRef_ == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null etsConnectionRef_"); return; } ani_env *env = nullptr; @@ -2177,7 +2389,7 @@ void ETSAbilityConnection::CallEtsFailed(int32_t errorCode) return; } ani_ref funRef; - if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(stsConnectionRef_), + if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(etsConnectionRef_), "onFailed", &funRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::CONTEXT, "get onFailed failed status : %{public}d", status); return; @@ -2201,6 +2413,7 @@ void ETSAbilityConnection::CallEtsFailed(int32_t errorCode) void ETSAbilityConnection::SetConnectionRef(ani_object connectOptionsObj) { + TAG_LOGD(AAFwkTag::CONTEXT, "SetConnectionRef callled"); if (etsVm_ == nullptr) { TAG_LOGE(AAFwkTag::CONTEXT, "etsVm_ is nullptr"); return; @@ -2211,7 +2424,7 @@ void ETSAbilityConnection::SetConnectionRef(ani_object connectOptionsObj) TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); return; } - if ((status = env->GlobalReference_Create(connectOptionsObj, &stsConnectionRef_)) != ANI_OK) { + if ((status = env->GlobalReference_Create(connectOptionsObj, &etsConnectionRef_)) != ANI_OK) { TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); } } @@ -2220,7 +2433,7 @@ void ETSAbilityConnection::OnAbilityConnectDone( const AppExecFwk::ElementName &element, const sptr &remoteObject, int32_t resultCode) { TAG_LOGD(AAFwkTag::CONTEXT, "OnAbilityConnectDone"); - if (etsVm_ == nullptr || stsConnectionRef_ == nullptr) { + if (etsVm_ == nullptr || etsConnectionRef_ == nullptr) { TAG_LOGE(AAFwkTag::CONTEXT, "null stsConnectionRef or etsVm"); return; } @@ -2248,7 +2461,7 @@ void ETSAbilityConnection::OnAbilityConnectDone( } ani_status status = ANI_ERROR; ani_ref funRef; - if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(stsConnectionRef_), + if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(etsConnectionRef_), "onConnect", &funRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::CONTEXT, "get onConnect failed status : %{public}d", status); return; @@ -2268,7 +2481,7 @@ void ETSAbilityConnection::OnAbilityConnectDone( void ETSAbilityConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int32_t resultCode) { TAG_LOGD(AAFwkTag::CONTEXT, "OnAbilityDisconnectDone"); - if (etsVm_ == nullptr || stsConnectionRef_ == nullptr) { + if (etsVm_ == nullptr || etsConnectionRef_ == nullptr) { TAG_LOGE(AAFwkTag::CONTEXT, "null stsConnectionRef or etsVm"); return; } @@ -2285,7 +2498,7 @@ void ETSAbilityConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName } ani_status status = ANI_ERROR; ani_ref funRef; - if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(stsConnectionRef_), + if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(etsConnectionRef_), "onDisconnect", &funRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::CONTEXT, "get onDisconnect failed status : %{public}d", status); return; diff --git a/frameworks/ets/ani/ui_ability/src/ets_ui_ability_servicehost_stub_impl.cpp b/frameworks/ets/ani/ui_ability/src/ets_ui_ability_servicehost_stub_impl.cpp new file mode 100644 index 00000000000..980a9d03383 --- /dev/null +++ b/frameworks/ets/ani/ui_ability/src/ets_ui_ability_servicehost_stub_impl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 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_ability_servicehost_stub_impl.h" + +#include "ability_business_error.h" +#include "ets_uiservice_ability_connection.h" + +namespace OHOS { +namespace AbilityRuntime { + +EtsUIAbilityServiceHostStubImpl::EtsUIAbilityServiceHostStubImpl(wptr conn) + :conn_(conn) +{ + +} + +int32_t EtsUIAbilityServiceHostStubImpl::SendData(OHOS::AAFwk::WantParams &data) +{ + sptr conn = conn_.promote(); + if (conn != nullptr) { + return conn->OnSendData(data); + } + + return static_cast(AbilityErrorCode::ERROR_CODE_INNER); +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/ui_ability/src/ets_uiservice_ability_connection.cpp b/frameworks/ets/ani/ui_ability/src/ets_uiservice_ability_connection.cpp new file mode 100644 index 00000000000..849ba47af86 --- /dev/null +++ b/frameworks/ets/ani/ui_ability/src/ets_uiservice_ability_connection.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2024 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_uiservice_ability_connection.h" + +#include "ability_business_error.h" +#include "ani_common_want.h" +#include "ets_error_utils.h" +#include "ets_ui_ability_servicehost_stub_impl.h" +#include "ets_ui_service_proxy.h" +#include "hilog_tag_wrapper.h" +#include "ui_ability_servicehost_stub_impl.h" + +namespace OHOS { +namespace AbilityRuntime { + +namespace EtsUIServiceConnection { +static std::map, KeyCompare> g_uiServiceExtensionConnects; +static std::recursive_mutex g_uiServiceExtensionConnectsLock_; +static int64_t g_uiServiceExtensionSerialNumber = 0; + +// This function has to be called from engine thread +void RemoveUIServiceAbilityConnection(int64_t connectId) +{ + std::lock_guard lock(g_uiServiceExtensionConnectsLock_); + auto item = std::find_if(g_uiServiceExtensionConnects.begin(), g_uiServiceExtensionConnects.end(), + [&connectId](const auto &obj) { + return connectId == obj.first.id; + }); + if (item != g_uiServiceExtensionConnects.end()) { + TAG_LOGD(AAFwkTag::UI_EXT, "exist, remove"); + if (item->second) { + item->second->RemoveConnectionObject(); + item->second->SetProxyObject(nullptr); + } + g_uiServiceExtensionConnects.erase(item); + } else { + TAG_LOGD(AAFwkTag::UI_EXT, "not exist"); + } + TAG_LOGI(AAFwkTag::UI_EXT, "connects new size:%{public}zu", g_uiServiceExtensionConnects.size()); +} + +int64_t InsertUIServiceAbilityConnection(sptr connection, const AAFwk::Want &want) +{ + std::lock_guard lock(g_uiServiceExtensionConnectsLock_); + if (connection == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null connection"); + return -1; + } + int64_t connectId = g_uiServiceExtensionSerialNumber; + ConnectionKey key; + key.id = g_uiServiceExtensionSerialNumber; + key.want = want; + key.accountId = 0; + connection->SetConnectionId(key.id); + g_uiServiceExtensionConnects.emplace(key, connection); + if (g_uiServiceExtensionSerialNumber < INT32_MAX) { + g_uiServiceExtensionSerialNumber++; + } else { + g_uiServiceExtensionSerialNumber = 0; + } + return connectId; +} + +void FindUIServiceAbilityConnection(const int64_t& connectId, AAFwk::Want& want, + sptr& connection) +{ + std::lock_guard lock(g_uiServiceExtensionConnectsLock_); + TAG_LOGI(AAFwkTag::UI_EXT, "connection:%{public}d", static_cast(connectId)); + auto item = std::find_if(g_uiServiceExtensionConnects.begin(), g_uiServiceExtensionConnects.end(), + [&connectId](const auto &obj) { + return connectId == obj.first.id; + }); + if (item != g_uiServiceExtensionConnects.end()) { + want = item->first.want; + connection = item->second; + TAG_LOGI(AAFwkTag::UI_EXT, "found"); + } else { + TAG_LOGI(AAFwkTag::UI_EXT, "not found"); + } +} + +void FindUIServiceAbilityConnection(ani_env *env, const AAFwk::Want &want, ani_object callback, + sptr &connection) +{ + std::lock_guard lock(g_uiServiceExtensionConnectsLock_); + auto item = std::find_if(g_uiServiceExtensionConnects.begin(), g_uiServiceExtensionConnects.end(), + [&want, env, callback](const auto &obj) { + bool wantEquals = (obj.first.want.GetElement() == want.GetElement()); + ani_ref tmpCallbackRef = obj.second->GetEtsConnectionObject(); + bool callbackObjectEquals = + EtsUIServiceExtAbilityConnection::IsEtsCallbackObjectEquals(env, tmpCallbackRef, callback); + return wantEquals && callbackObjectEquals; + }); + if (item == g_uiServiceExtensionConnects.end()) { + return; + } + connection = item->second; +} +} + +EtsUIServiceExtAbilityConnection::EtsUIServiceExtAbilityConnection(ani_vm *etsVm) : ETSAbilityConnection(etsVm) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "EtsUIServiceExtAbilityConnection"); + wptr weakthis = this; + serviceHostStub_ = sptr::MakeSptr(weakthis); +} + +EtsUIServiceExtAbilityConnection::~EtsUIServiceExtAbilityConnection() +{ + TAG_LOGI(AAFwkTag::UISERVC_EXT, "~EtsUIServiceExtAbilityConnection"); + serviceHostStub_ = nullptr; + serviceHostStub_ = nullptr; + ReleaseObjectReference(serviceProxyObject_); + ReleaseObjectReference(aniAsyncCallback_); + for (auto& callback : duplicatedPendingCallbacks_) { + ReleaseObjectReference(callback); + } + duplicatedPendingCallbacks_.clear(); +} + +void EtsUIServiceExtAbilityConnection::HandleOnAbilityConnectDone( + const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "HandleOnAbilityConnectDone called"); + ani_env *env = AttachCurrentThread(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed"); + return; + } + if (aniAsyncCallback_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null napiAsyncTask_"); + DetachCurrentThread(); + return; + } + sptr hostStub = GetServiceHostStub(); + sptr hostProxy = nullptr; + if (hostStub != nullptr) { + hostProxy = hostStub->AsObject(); + } + ani_object proxyObj = AAFwk::EtsUIServiceProxy::CreateEtsUIServiceProxy(env, remoteObject, + connectionId_, hostProxy); + SetProxyObject(proxyObj); + AppExecFwk::AsyncCallback(env, reinterpret_cast(aniAsyncCallback_), + EtsErrorUtil::CreateError(env, static_cast(AbilityErrorCode::ERROR_OK)), proxyObj); + + ResolveDuplicatedPendingCallbacks(env, proxyObj); + ReleaseObjectReference(proxyObj); + DetachCurrentThread(); +} + +void EtsUIServiceExtAbilityConnection::ReleaseReference(ani_env *env, ani_ref etsObjRef) +{ + if (env == nullptr || etsObjRef == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "env or etsObjRef null"); + return; + } + ani_status status = ANI_ERROR; + if ((status = env->GlobalReference_Delete(etsObjRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "GlobalReference_Delete status: %{public}d", status); + } +} + +void EtsUIServiceExtAbilityConnection::CallObjectMethod(ani_env *env, const char *methodName, + const char *signature, ...) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "call method:%{public}s", methodName); + if (env == nullptr || etsConnectionRef_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ nullptr"); + return; + } + env->ResetError(); + va_list args; + va_start(args, signature); + ani_status status = ANI_ERROR; + if ((status = env->Object_CallMethodByName_Void(reinterpret_cast(etsConnectionRef_), + methodName, signature, args)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "Failed to CallObjectMethod , status: %{public}d", status); + } + va_end(args); +} + +void EtsUIServiceExtAbilityConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, + int resultCode) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "HandleOnAbilityDisconnectDone called"); + if (aniAsyncCallback_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null napiAsyncTask_"); + return; + } + ani_env *env = AttachCurrentThread(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed"); + return; + } + AppExecFwk::AsyncCallback(env, reinterpret_cast(aniAsyncCallback_), + EtsErrorUtil::CreateError(env, static_cast(AbilityErrorCode::ERROR_CODE_INNER)), + AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + + RejectDuplicatedPendingCallbacks(env, static_cast(AbilityErrorCode::ERROR_CODE_INNER)); + ReleaseReference(env, aniAsyncCallback_); + CallObjectMethod(env, "onDisconnect", nullptr); + EtsUIServiceConnection::RemoveUIServiceAbilityConnection(connectionId_); +} + +void EtsUIServiceExtAbilityConnection::SetAniAsyncCallback_(ani_object myCallback) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "SetAniAsyncCallback_ called"); + if (myCallback == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "myCallback is null"); + return; + } + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ is null"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed status: %{public}d", status); + return; + } + ani_ref global = nullptr; + if ((status = env->GlobalReference_Create(myCallback, &global)) != ANI_OK + || global == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GlobalReference_Create failed status: %{public}d", status); + return; + } + aniAsyncCallback_ = global; + TAG_LOGD(AAFwkTag::UI_EXT, "SetAniAsyncCallback_ success"); +} + +void EtsUIServiceExtAbilityConnection::AddDuplicatedPendingCallback(ani_object myCallback) +{ + duplicatedPendingCallbacks_.push_back(myCallback); +} + +void EtsUIServiceExtAbilityConnection::ResolveDuplicatedPendingCallbacks(ani_env *env, ani_object proxyObj) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "ResolveDuplicatedPendingCallbacks, size: %{public}zu", + duplicatedPendingCallbacks_.size()); + for (auto &callback : duplicatedPendingCallbacks_) { + if (callback == nullptr) { + continue; + } + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + EtsErrorUtil::CreateError(env, static_cast(AbilityErrorCode::ERROR_OK)), proxyObj); + ReleaseReference(env, callback); + } + duplicatedPendingCallbacks_.clear(); +} + +void EtsUIServiceExtAbilityConnection::RejectDuplicatedPendingCallbacks(ani_env *env, int32_t error) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "RejectDuplicatedPendingCallbacks, size: %{public}zu", + duplicatedPendingCallbacks_.size()); + for (auto &callback : duplicatedPendingCallbacks_) { + if (callback == nullptr) { + continue; + } + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr( + env, static_cast(error)), AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + ReleaseReference(env, callback); + } + duplicatedPendingCallbacks_.clear(); +} + +void EtsUIServiceExtAbilityConnection::SetProxyObject(ani_object proxy) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "SetProxyObject"); + if (proxy == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ nullptr"); + return; + } + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ nullptr"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed status: %{public}d", status); + return; + } + ani_ref global = nullptr; + if ((status = env->GlobalReference_Create(proxy, &global)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status : %{public}d", status); + return; + } + ReleaseObjectReference(serviceProxyObject_); + serviceProxyObject_ = global; +} + +ani_ref EtsUIServiceExtAbilityConnection::GetProxyObject() +{ + return serviceProxyObject_; +} + +int32_t EtsUIServiceExtAbilityConnection::OnSendData(OHOS::AAFwk::WantParams &data) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "OnSendData called"); + HandleOnSendData(data); + return static_cast(AbilityErrorCode::ERROR_OK); +} + +void EtsUIServiceExtAbilityConnection::HandleOnSendData(const OHOS::AAFwk::WantParams &data) +{ + ani_env *env = AttachCurrentThread(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null aniEnv"); + return; + } + ani_ref aniWantParams = AppExecFwk::WrapWantParams(env, data); + if (aniWantParams == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null aniWantParams"); + DetachCurrentThread(); + return; + } + CallObjectMethod(env, "onData", nullptr, aniWantParams); + DetachCurrentThread(); +} + +bool EtsUIServiceExtAbilityConnection::IsEtsCallbackObjectEquals(ani_env *env, ani_ref callback, ani_object value) +{ + if (env == nullptr || callback == nullptr || value == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "env or callback or value null"); + return false; + } + ani_boolean isEquals = ANI_FALSE; + ani_status status = ANI_ERROR; + if ((status = env->Reference_StrictEquals(callback, value, &isEquals)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "Reference_StrictEquals failed status: %{public}d", status); + return false; + } + return isEquals == ANI_TRUE; +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_context.h b/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_context.h index bea58021baa..ac9c36f36f7 100644 --- a/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_context.h +++ b/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_context.h @@ -40,16 +40,18 @@ public: const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode) override; void OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode) override; void CallEtsFailed(int32_t errorCode); - void SetConnectionId(int32_t id); - int32_t GetConnectionId() { return connectionId_; } + void SetConnectionId(int64_t id); + int64_t GetConnectionId() { return connectionId_; } void SetConnectionRef(ani_object connectOptionsObj); + void RemoveConnectionObject(); ani_env *AttachCurrentThread(); void DetachCurrentThread(); - + ani_ref GetEtsConnectionObject() { return etsConnectionRef_; } protected: + void ReleaseObjectReference(ani_ref etsObjRef); ani_vm *etsVm_ = nullptr; - int32_t connectionId_ = -1; - ani_ref stsConnectionRef_ = nullptr; + int64_t connectionId_ = -1; + ani_ref etsConnectionRef_ = nullptr; bool isAttachThread_ = false; }; @@ -85,6 +87,12 @@ public: ani_object optionsObj, ani_object callbackobj); static void OpenAtomicService(ani_env *env, ani_object aniObj, ani_string aniAppId, ani_object callbackobj, ani_object optionsObj); + static void ConnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object wantObj, + ani_object uiServiceExtConCallbackObj, ani_object callback); + static void StartUIServiceExtension(ani_env *env, ani_object aniObj, + ani_object wantObj, ani_object callback); + static void DisconnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object proxyObj, + ani_object callback); static bool BindNativePtrCleaner(ani_env *env); static void Clean(ani_env *env, ani_object object); @@ -130,6 +138,11 @@ private: void InitDisplayId(AAFwk::Want &want, AAFwk::StartOptions &startOptions, ani_env *env, ani_object optionsObj); #endif + void OnConnectUIServiceExtension(ani_env *env, ani_object wantObj, ani_object uiServiceExtConCallbackObj, + ani_object callback); + void OnStartUIServiceExtension(ani_env *env, ani_object wantObj, ani_object callback); + void OnDisconnectUIServiceExtension(ani_env *env, ani_object proxyObj, ani_object callback); + bool CheckConnectAlreadyExist(ani_env *env, const AAFwk::Want& want, ani_object callback, ani_object myCallback); protected: std::weak_ptr context_; sptr freeInstallObserver_ = nullptr; diff --git a/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_servicehost_stub_impl.h b/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_servicehost_stub_impl.h new file mode 100644 index 00000000000..3124c0110ea --- /dev/null +++ b/frameworks/ets/ani/ui_extension_ability/include/ets_ui_extension_servicehost_stub_impl.h @@ -0,0 +1,38 @@ +/* + * 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_SERVICEHOST_STUB_IMPL_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_EXTENSION_SERVICEHOST_STUB_IMPL_H + +#include "ets_ui_extension_context.h" +#include "ui_service_host_stub.h" + +namespace OHOS { +namespace AbilityRuntime { + +class EtsUIServiceUIExtConnection; +class EtsUIExtensionServiceHostStubImpl : public AAFwk::UIServiceHostStub { +public: + EtsUIExtensionServiceHostStubImpl(wptr conn); + ~EtsUIExtensionServiceHostStubImpl() = default; + virtual int32_t SendData(OHOS::AAFwk::WantParams &data) override; + +protected: + wptr conn_; +}; + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_EXTENSION_SERVICEHOST_STUB_IMPL_H + diff --git a/frameworks/ets/ani/ui_extension_ability/include/ets_uiservice_uiext_connection.h b/frameworks/ets/ani/ui_extension_ability/include/ets_uiservice_uiext_connection.h new file mode 100644 index 00000000000..81dd8f1e34a --- /dev/null +++ b/frameworks/ets/ani/ui_extension_ability/include/ets_uiservice_uiext_connection.h @@ -0,0 +1,62 @@ +/* + * 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_UISERVICE_UIEXT_CONNECTION_H +#define OHOS_ABILITY_RUNTIME_ETS_UISERVICE_UIEXT_CONNECTION_H + +#include "ets_ui_extension_context.h" +#include "ets_ui_extension_servicehost_stub_impl.h" +#include "ui_service_host_stub.h" + +namespace OHOS { +namespace AbilityRuntime { +class EtsUIServiceUIExtConnection; +namespace ETSUIServiceConnection { +void AddUIServiceExtensionConnection(AAFwk::Want &want, sptr &connection); +void RemoveUIServiceExtensionConnection(const int64_t &connectId); +void FindUIServiceExtensionConnection(const int64_t &connectId, AAFwk::Want& want, + sptr &connection); +void FindUIServiceExtensionConnection(ani_env *env, const AAFwk::Want &want, ani_object callback, + sptr &connection); +} + +class EtsUIServiceUIExtConnection : public EtsUIExtensionConnection { +public: + EtsUIServiceUIExtConnection(ani_vm *etsVm); + ~EtsUIServiceUIExtConnection(); + virtual void HandleOnAbilityConnectDone( + const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode); + virtual void HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode); + sptr GetServiceHostStub() { return serviceHostStub_; } + void SetProxyObject(ani_object proxy); + ani_ref GetProxyObject(); + void SetAniAsyncCallback_(ani_object myCallback); + void ResolveDuplicatedPendingCallbacks(ani_env *env, ani_object proxyObj); + void AddDuplicatedPendingCallback(ani_object myCallback); + void RejectDuplicatedPendingCallbacks(ani_env *env, int32_t error); + int32_t OnSendData(AAFwk::WantParams &data); + void HandleOnSendData(const AAFwk::WantParams &data); + static bool IsEtsCallbackObjectEquals(ani_env *env, ani_ref callback, ani_object value); +private: + void ReleaseReference(ani_env *env, ani_ref etsObjRef); + void CallObjectMethod(ani_env *env, const char *methodName, const char *signature, ...); + sptr serviceHostStub_; + ani_ref serviceProxyObject_; + ani_ref aniAsyncCallback_; + std::vector duplicatedPendingCallbacks_; +}; + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UISERVICE_UIEXT_CONNECTION_H diff --git a/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension.cpp b/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension.cpp index 58157cb3f78..83ac687ee33 100644 --- a/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension.cpp +++ b/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension.cpp @@ -159,7 +159,8 @@ bool EtsUIExtension::BindNativeMethods() TAG_LOGE(AAFwkTag::UI_EXT, "FindClass failed status: %{public}d", status); return false; } - if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK) { + if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK + && status != ANI_ALREADY_BINDED) { TAG_LOGE(AAFwkTag::UI_EXT, "Class_BindNativeMethods status: %{public}d", status); return false; } 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 8c5652f6988..d7e14b4f4f9 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 @@ -27,9 +27,10 @@ #include "ets_extension_context.h" #include "ipc_skeleton.h" #include "tokenid_kit.h" +#include "ets_uiservice_uiext_connection.h" +#include "ets_ui_service_proxy.h" #include "ui_extension_context.h" - namespace OHOS { namespace AbilityRuntime { static std::mutex g_connectsMutex; @@ -37,6 +38,7 @@ int32_t g_serialNumber = 0; static std::map, Etskey_compare> g_connects; const char *UI_EXTENSION_CONTEXT_CLASS_NAME = "C{application.UIExtensionContext.UIExtensionContext}"; const char *UI_EXTENSION_CONTEXT_CLEANER_CLASS_NAME = "C{application.UIExtensionContext.Cleaner}"; +constexpr const char* UISERVICEHOSTPROXY_KEY = "ohos.ability.params.UIServiceHostProxy"; constexpr const int FAILED_CODE = -1; constexpr const char *SIGNATURE_CONNECT_SERVICE_EXTENSION = "C{@ohos.app.ability.Want.Want}C{ability.connectOptions.ConnectOptions}:l"; @@ -1011,6 +1013,196 @@ void EtsUIExtensionContext::OnReportDrawnCompleted(ani_env* env, ani_object aniC TAG_LOGD(AAFwkTag::UI_EXT, "NativeReportDrawnCompleted end"); } +void EtsUIExtensionContext::ConnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object wantObj, + ani_object uiServiceExtConCallbackObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "ConnectUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIExtensionContext(env, aniObj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnConnectUIServiceExtension(env, wantObj, uiServiceExtConCallbackObj, callback); +} + +bool EtsUIExtensionContext::CheckConnectAlreadyExist(ani_env *env, const AAFwk::Want& want, + ani_object callback, ani_object myCallback) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "CheckConnectAlreadyExist called"); + sptr connection = nullptr; + ETSUIServiceConnection::FindUIServiceExtensionConnection(env, want, callback, connection); + if (connection == nullptr) { + TAG_LOGD(AAFwkTag::UI_EXT, "null connection"); + return false; + } + ani_ref proxy = connection->GetProxyObject(); + if (proxy == nullptr) { + TAG_LOGW(AAFwkTag::UI_EXT, "null proxy"); + connection->AddDuplicatedPendingCallback(myCallback); + } else { + TAG_LOGI(AAFwkTag::UI_EXT, "Resolve, got proxy object"); + AppExecFwk::AsyncCallback(env, reinterpret_cast(myCallback), + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr( + env, static_cast(AbilityErrorCode::ERROR_OK)), reinterpret_cast(proxy)); + } + return true; +} + +void EtsUIExtensionContext::OnConnectUIServiceExtension(ani_env *env, ani_object wantObj, + ani_object uiServiceExtConCallbackObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "OnConnectUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + ani_vm *aniVM = nullptr; + if (env->GetVM(&aniVM) != ANI_OK) { + TAG_LOGE(AAFwkTag::DELEGATOR, "GetVM failed"); + return; + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::UI_EXT, "input param want failed"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateInvalidParamError(env, "Parse param want failed, want must be Want."), + AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + return; + } + if (CheckConnectAlreadyExist(env, want, uiServiceExtConCallbackObj, callback)) { + TAG_LOGE(AAFwkTag::UI_EXT, "duplicated"); + return; + } + sptr connection = sptr::MakeSptr(aniVM); + sptr stub = connection->GetServiceHostStub(); + want.SetParam(UISERVICEHOSTPROXY_KEY, stub->AsObject()); + connection->SetConnectionRef(uiServiceExtConCallbackObj); + connection->SetAniAsyncCallback_(callback); + ETSUIServiceConnection::AddUIServiceExtensionConnection(want, connection); + int64_t connectId = connection->GetConnectionId(); + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + EtsErrorUtil::CreateErrorByNativeErr(env, + static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)), + AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + ETSUIServiceConnection::RemoveUIServiceExtensionConnection(connectId); + return; + } + int32_t innerErrorCode = context->ConnectUIServiceExtensionAbility(want, connection); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "errcode: %{public}d.", innerErrorCode); + if (innerErrorCode != static_cast(AbilityErrorCode::ERROR_OK)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "errcode: %{public}d.", innerErrorCode); + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + EtsErrorUtil::CreateErrorByNativeErr(env, + static_cast(innerErrorCode)), AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + ETSUIServiceConnection::RemoveUIServiceExtensionConnection(connectId); + } +} + +void EtsUIExtensionContext::StartUIServiceExtension(ani_env *env, ani_object aniObj, + ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "StartUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIExtensionContext(env, aniObj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnStartUIServiceExtension(env, wantObj, callback); +} + +void EtsUIExtensionContext::OnStartUIServiceExtension(ani_env *env, ani_object wantObj, ani_object callback) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::UI_EXT, "OnStartUIServiceExtension is called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT), nullptr); + return; + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::UI_EXT, "UnwrapWant failed"); + ani_object aniObject = EtsErrorUtil::CreateInvalidParamError(env, "Parse param want failed, want must be Want"); + AppExecFwk::AsyncCallback(env, callback, aniObject, nullptr); + return; + } + int32_t innerErrCode = static_cast(ERR_OK); + innerErrCode = context->StartUIServiceExtension(want); + TAG_LOGD(AAFwkTag::UI_EXT, "StartUIServiceExtension code:%{public}d", innerErrCode); + AppExecFwk::AsyncCallback(env, callback, + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr(env, static_cast(innerErrCode)), nullptr); +} +void EtsUIExtensionContext::DisconnectUIServiceExtension(ani_env *env, ani_object aniObj, + ani_object proxyObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "StartUIServiceExtension called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIExtensionContext(env, aniObj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnDisconnectUIServiceExtension(env, proxyObj, callback); +} + +void EtsUIExtensionContext::OnDisconnectUIServiceExtension(ani_env *env, ani_object proxyObj, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return; + } + AAFwk::EtsUIServiceProxy* proxy = AAFwk::EtsUIServiceProxy::GetEtsUIServiceProxy(env, proxyObj); + if (proxy == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null proxy"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateInvalidParamError(env, "Parameter verification failed"), nullptr); + return; + } + int64_t connectId = proxy->GetConnectionId(); + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT), nullptr); + return; + } + AAFwk::Want want; + sptr connection = nullptr; + ETSUIServiceConnection::FindUIServiceExtensionConnection(connectId, want, connection); + TAG_LOGD(AAFwkTag::UI_EXT, "connection:%{public}d.", static_cast(connectId)); + + if (connection == nullptr) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "null connection"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_CODE_INNER), nullptr); + ETSUIServiceConnection::RemoveUIServiceExtensionConnection(connectId); + return; + } + context->DisconnectAbility(want, connection); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_OK), nullptr); +} + bool EtsUIExtensionContext::BindNativePtrCleaner(ani_env *env) { if (env == nullptr) { @@ -1026,10 +1218,11 @@ bool EtsUIExtensionContext::BindNativePtrCleaner(ani_env *env) std::array methods = { ani_native_function { "clean", nullptr, reinterpret_cast(EtsUIExtensionContext::Clean) }, }; - if (ANI_OK != env->Class_BindNativeMethods(cleanerCls, methods.data(), methods.size())) { + if ((status = env->Class_BindNativeMethods(cleanerCls, methods.data(), methods.size())) != ANI_OK + && status != ANI_ALREADY_BINDED) { TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); return false; - }; + } return true; } @@ -1143,9 +1336,13 @@ ani_object CreateEtsUIExtensionContext(ani_env *env, std::shared_ptr(EtsUIExtensionContext::OpenAtomicService) }, ani_native_function { "nativeOpenLinkSync", SIGNATURE_OPEN_LINK, reinterpret_cast(EtsUIExtensionContext::OpenLink) }, + ani_native_function{"nativeStartUIServiceExtensionAbility", + "L@ohos/app/ability/Want/Want;Lutils/AbilityUtils/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsUIExtensionContext::StartUIServiceExtension)}, }; - if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK) { - TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK + && status != ANI_ALREADY_BINDED) { + TAG_LOGE(AAFwkTag::UI_EXT, "BindNativeMethods status: %{public}d", status); return nullptr; } auto workContext = new (std::nothrow) @@ -1194,16 +1391,16 @@ EtsUIExtensionConnection::EtsUIExtensionConnection(ani_vm *etsVm) : etsVm_(etsVm EtsUIExtensionConnection::~EtsUIExtensionConnection() { - if (etsVm_ != nullptr && stsConnectionRef_ != nullptr) { + if (etsVm_ != nullptr && etsConnectionRef_ != nullptr) { ani_env* env = nullptr; if (etsVm_->GetEnv(ANI_VERSION_1, &env) == ANI_OK) { - env->GlobalReference_Delete(stsConnectionRef_); - stsConnectionRef_ = nullptr; + env->GlobalReference_Delete(etsConnectionRef_); + etsConnectionRef_ = nullptr; } } } -void EtsUIExtensionConnection::SetConnectionId(int32_t id) +void EtsUIExtensionConnection::SetConnectionId(int64_t id) { connectionId_ = id; } @@ -1225,12 +1422,12 @@ void EtsUIExtensionConnection::CallEtsFailed(int32_t errorCode) TAG_LOGE(AAFwkTag::UI_EXT, "null env"); return; } - if (stsConnectionRef_ == nullptr) { - TAG_LOGE(AAFwkTag::UI_EXT, "null stsConnectionRef_"); + if (etsConnectionRef_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null etsConnectionRef_"); return; } ani_ref funRef; - if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(stsConnectionRef_), + if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(etsConnectionRef_), "onFailed", &funRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::UI_EXT, "get onFailed failed status : %{public}d", status); return; @@ -1266,7 +1463,7 @@ void EtsUIExtensionConnection::SetConnectionRef(ani_object connectOptionsObj) TAG_LOGE(AAFwkTag::UI_EXT, "Faied to getEnv, status: %{public}d", status); return; } - if ((status = env->GlobalReference_Create(connectOptionsObj, &stsConnectionRef_)) != ANI_OK) { + if ((status = env->GlobalReference_Create(connectOptionsObj, &etsConnectionRef_)) != ANI_OK) { TAG_LOGE(AAFwkTag::UI_EXT, "Faied to createReference, status: %{public}d", status); } } @@ -1275,7 +1472,7 @@ void EtsUIExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementNam const sptr &remoteObject, int resultCode) { TAG_LOGD(AAFwkTag::UI_EXT, "OnAbilityConnectDone"); - if (etsVm_ == nullptr || stsConnectionRef_ == nullptr) { + if (etsVm_ == nullptr || etsConnectionRef_ == nullptr) { TAG_LOGE(AAFwkTag::UI_EXT, "null stsConnectionRef or etsVm"); return; } @@ -1303,7 +1500,7 @@ void EtsUIExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementNam } ani_status status = ANI_ERROR; ani_ref funRef; - if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(stsConnectionRef_), + if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(etsConnectionRef_), "onConnect", &funRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::UI_EXT, "get onConnect failed status : %{public}d", status); return; @@ -1324,7 +1521,7 @@ void EtsUIExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementNam void EtsUIExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode) { TAG_LOGD(AAFwkTag::UI_EXT, "OnAbilityDisconnectDone"); - if (etsVm_ == nullptr || stsConnectionRef_ == nullptr) { + if (etsVm_ == nullptr || etsConnectionRef_ == nullptr) { TAG_LOGE(AAFwkTag::UI_EXT, "null stsConnectionRef or etsVm"); return; } @@ -1341,7 +1538,7 @@ void EtsUIExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::Element } ani_status status = ANI_ERROR; ani_ref funRef; - if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(stsConnectionRef_), + if ((status = env->Object_GetPropertyByName_Ref(reinterpret_cast(etsConnectionRef_), "onDisconnect", &funRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::UI_EXT, "get onDisconnect failed status : %{public}d", status); return; @@ -1359,6 +1556,29 @@ void EtsUIExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::Element DetachCurrentThread(); } +void EtsUIExtensionConnection::ReleaseObjectReference(ani_ref etsObjRef) +{ + if (etsVm_ == nullptr || etsObjRef == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ or etsObjRef null"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv status:%{public}d", status); + return; + } + if ((status = env->GlobalReference_Delete(etsObjRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "GlobalReference_Delete status: %{public}d", status); + } +} + +void EtsUIExtensionConnection::RemoveConnectionObject() +{ + ReleaseObjectReference(etsConnectionRef_); + etsConnectionRef_ = nullptr; +} + ani_env *EtsUIExtensionConnection::AttachCurrentThread() { ani_env *env = nullptr; diff --git a/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_servicehost_stub_impl.cpp b/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_servicehost_stub_impl.cpp new file mode 100644 index 00000000000..01a2084c429 --- /dev/null +++ b/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_servicehost_stub_impl.cpp @@ -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. + */ + +#include "ets_ui_extension_servicehost_stub_impl.h" + +#include "ability_business_error.h" +#include "ets_uiservice_uiext_connection.h" + +namespace OHOS { +namespace AbilityRuntime { + +EtsUIExtensionServiceHostStubImpl::EtsUIExtensionServiceHostStubImpl(wptr conn) + :conn_(conn) +{ +} + +int32_t EtsUIExtensionServiceHostStubImpl::SendData(OHOS::AAFwk::WantParams &data) +{ + sptr conn = conn_.promote(); + if (conn != nullptr) { + return conn->OnSendData(data); + } + return static_cast(AbilityErrorCode::ERROR_CODE_INNER); +} + +} // namespace AbilityRuntime +} //namespace OHOS diff --git a/frameworks/ets/ani/ui_extension_ability/src/ets_uiservice_uiext_connection.cpp b/frameworks/ets/ani/ui_extension_ability/src/ets_uiservice_uiext_connection.cpp new file mode 100644 index 00000000000..666281b935a --- /dev/null +++ b/frameworks/ets/ani/ui_extension_ability/src/ets_uiservice_uiext_connection.cpp @@ -0,0 +1,336 @@ +/* + * 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_uiservice_uiext_connection.h" + +#include "ability_business_error.h" +#include "ani_common_want.h" +#include "ets_error_utils.h" +#include "ets_ui_service_proxy.h" +#include "hilog_tag_wrapper.h" +#include "ui_extension_servicehost_stub_impl.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace ETSUIServiceConnection { +static std::map, key_compare> gUiServiceExtConnects; +static std::recursive_mutex gUiServiceExtConnectsLock; +static int64_t gUiServiceExtConnectSn = 0; + +void AddUIServiceExtensionConnection(AAFwk::Want &want, sptr &connection) +{ + std::lock_guard lock(gUiServiceExtConnectsLock); + UIExtensionConnectionKey key; + key.id = gUiServiceExtConnectSn; + key.want = want; + connection->SetConnectionId(key.id); + gUiServiceExtConnects.emplace(key, connection); + if (gUiServiceExtConnectSn < INT32_MAX) { + gUiServiceExtConnectSn++; + } else { + gUiServiceExtConnectSn = 0; + } +} + +void RemoveUIServiceExtensionConnection(const int64_t &connectId) +{ + std::lock_guard lock(gUiServiceExtConnectsLock); + auto item = std::find_if(gUiServiceExtConnects.begin(), gUiServiceExtConnects.end(), + [&connectId](const auto &obj) { + return connectId == obj.first.id; + }); + if (item != gUiServiceExtConnects.end()) { + TAG_LOGD(AAFwkTag::UI_EXT, "found, erase"); + gUiServiceExtConnects.erase(item); + } else { + TAG_LOGD(AAFwkTag::UI_EXT, "not found"); + } + TAG_LOGD(AAFwkTag::UI_EXT, "gUiServiceExtConnects new size:%{public}zu", gUiServiceExtConnects.size()); +} + +void FindUIServiceExtensionConnection(const int64_t& connectId, AAFwk::Want& want, + sptr &connection) +{ + std::lock_guard lock(gUiServiceExtConnectsLock); + TAG_LOGD(AAFwkTag::UI_EXT, "connection:%{public}d", static_cast(connectId)); + auto item = std::find_if(gUiServiceExtConnects.begin(), gUiServiceExtConnects.end(), + [&connectId](const auto &obj) { + return connectId == obj.first.id; + }); + if (item != gUiServiceExtConnects.end()) { + want = item->first.want; + connection = item->second; + TAG_LOGD(AAFwkTag::UI_EXT, "found ui service ext connection"); + } else { + TAG_LOGD(AAFwkTag::UI_EXT, "not found ui service ext connection"); + } +} + +void FindUIServiceExtensionConnection(ani_env *env, const AAFwk::Want& want, ani_object callback, + sptr &connection) +{ + std::lock_guard lock(gUiServiceExtConnectsLock); + auto item = std::find_if(gUiServiceExtConnects.begin(), gUiServiceExtConnects.end(), + [&want, env, callback](const auto &obj) { + bool wantEquals = (obj.first.want.GetElement() == want.GetElement()); + ani_ref tmpCallbackRef = obj.second->GetEtsConnectionObject(); + bool callbackObjectEquals = + AbilityRuntime::EtsUIServiceUIExtConnection::IsEtsCallbackObjectEquals(env, tmpCallbackRef, callback); + return wantEquals && callbackObjectEquals; + }); + if (item == gUiServiceExtConnects.end()) { + return; + } + connection = item->second; +} +} // namespace UIServiceConnection + +EtsUIServiceUIExtConnection::EtsUIServiceUIExtConnection(ani_vm *etsVm) : EtsUIExtensionConnection(etsVm) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "EtsUIServiceUIExtConnection"); + wptr weakthis = this; + serviceHostStub_ = sptr::MakeSptr(weakthis); +} + +void EtsUIServiceUIExtConnection::ReleaseReference(ani_env *env, ani_ref etsObjRef) +{ + if (env == nullptr || etsObjRef == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "env or etsObjRef null"); + return; + } + ani_status status = ANI_ERROR; + if ((status = env->GlobalReference_Delete(etsObjRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "GlobalReference_Delete status: %{public}d", status); + } +} + +EtsUIServiceUIExtConnection::~EtsUIServiceUIExtConnection() +{ + TAG_LOGD(AAFwkTag::UI_EXT, "~EtsUIServiceUIExtConnection"); + serviceHostStub_ = nullptr; + ReleaseObjectReference(serviceProxyObject_); + ReleaseObjectReference(aniAsyncCallback_); + for (auto& callback : duplicatedPendingCallbacks_) { + ReleaseObjectReference(callback); + } + duplicatedPendingCallbacks_.clear(); +} + +void EtsUIServiceUIExtConnection::HandleOnAbilityConnectDone( + const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "HandleOnAbilityConnectDone called"); + ani_env *env = AttachCurrentThread(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed"); + return; + } + if (aniAsyncCallback_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null aniAsyncCallback_"); + DetachCurrentThread(); + return; + } + sptr hostStub = GetServiceHostStub(); + sptr hostProxy = nullptr; + if (hostStub != nullptr) { + hostProxy = hostStub->AsObject(); + } + ani_object proxyObj = AAFwk::EtsUIServiceProxy::CreateEtsUIServiceProxy(env, remoteObject, + connectionId_, hostProxy); + SetProxyObject(proxyObj); + AppExecFwk::AsyncCallback(env, reinterpret_cast(aniAsyncCallback_), + EtsErrorUtil::CreateError(env, static_cast(AbilityErrorCode::ERROR_OK)), proxyObj); + + ResolveDuplicatedPendingCallbacks(env, proxyObj); + ReleaseObjectReference(proxyObj); + DetachCurrentThread(); +} + +void EtsUIServiceUIExtConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, + int resultCode) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "HandleOnAbilityDisconnectDone called"); + if (aniAsyncCallback_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null napiAsyncTask_"); + return; + } + ani_env *env = AttachCurrentThread(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed"); + return; + } + AppExecFwk::AsyncCallback(env, reinterpret_cast(aniAsyncCallback_), + EtsErrorUtil::CreateError(env, static_cast(AbilityErrorCode::ERROR_CODE_INNER)), + AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + RejectDuplicatedPendingCallbacks(env, static_cast(AbilityErrorCode::ERROR_CODE_INNER)); + ReleaseReference(env, aniAsyncCallback_); + CallObjectMethod(env, "onDisconnect", nullptr); + SetProxyObject(nullptr); + RemoveConnectionObject(); + duplicatedPendingCallbacks_.clear(); + ETSUIServiceConnection::RemoveUIServiceExtensionConnection(connectionId_); +} + +void EtsUIServiceUIExtConnection::SetAniAsyncCallback_(ani_object myCallback) +{ + if (myCallback == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "myCallback is null"); + return; + } + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ is null"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed status: %{public}d", status); + return; + } + ani_ref global = nullptr; + if ((status = env->GlobalReference_Create(myCallback, &global)) != ANI_OK + || global == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GlobalReference_Create failed status: %{public}d", status); + return; + } + aniAsyncCallback_ = global; + TAG_LOGD(AAFwkTag::UI_EXT, "SetAniAsyncCallback_ success"); +} + +void EtsUIServiceUIExtConnection::AddDuplicatedPendingCallback(ani_object myCallback) +{ + duplicatedPendingCallbacks_.push_back(myCallback); +} + +void EtsUIServiceUIExtConnection::ResolveDuplicatedPendingCallbacks(ani_env *env, ani_object proxyObj) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "ResolveDuplicatedPendingCallbacks, size: %{public}zu", + duplicatedPendingCallbacks_.size()); + for (auto &callback : duplicatedPendingCallbacks_) { + if (callback == nullptr) { + continue; + } + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + EtsErrorUtil::CreateError(env, static_cast(AbilityErrorCode::ERROR_OK)), proxyObj); + ReleaseReference(env, callback); + } + duplicatedPendingCallbacks_.clear(); +} + +void EtsUIServiceUIExtConnection::RejectDuplicatedPendingCallbacks(ani_env *env, int32_t error) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "RejectDuplicatedPendingCallbacks, size: %{public}zu", + duplicatedPendingCallbacks_.size()); + for (auto &callback : duplicatedPendingCallbacks_) { + if (callback == nullptr) { + continue; + } + AppExecFwk::AsyncCallback(env, reinterpret_cast(callback), + AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr( + env, static_cast(error)), AAFwk::EtsUIServiceProxy::CreateEmptyProxyObject(env)); + ReleaseReference(env, callback); + } + duplicatedPendingCallbacks_.clear(); +} + +void EtsUIServiceUIExtConnection::SetProxyObject(ani_object proxy) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "SetProxyObject"); + if (proxy == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ nullptr"); + return; + } + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ nullptr"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "GetEnv failed status: %{public}d", status); + return; + } + ani_ref global = nullptr; + if ((status = env->GlobalReference_Create(proxy, &global)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status : %{public}d", status); + return; + } + ReleaseObjectReference(serviceProxyObject_); + serviceProxyObject_ = global; +} + +ani_ref EtsUIServiceUIExtConnection::GetProxyObject() +{ + return serviceProxyObject_; +} + +int32_t EtsUIServiceUIExtConnection::OnSendData(AAFwk::WantParams &data) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "OnSendData called"); + HandleOnSendData(data); + return static_cast(AbilityErrorCode::ERROR_OK); +} + +void EtsUIServiceUIExtConnection::HandleOnSendData(const OHOS::AAFwk::WantParams &data) +{ + ani_env *env = AttachCurrentThread(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null aniEnv"); + return; + } + ani_ref aniWantParams = AppExecFwk::WrapWantParams(env, data); + if (aniWantParams == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null aniWantParams"); + DetachCurrentThread(); + return; + } + CallObjectMethod(env, "onData", nullptr, aniWantParams); + DetachCurrentThread(); +} + +void EtsUIServiceUIExtConnection::CallObjectMethod(ani_env *env, const char *methodName, const char *signature, ...) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "call method:%{public}s", methodName); + if (env == nullptr || etsConnectionRef_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsVm_ nullptr"); + return; + } + env->ResetError(); + va_list args; + va_start(args, signature); + ani_status status = ANI_ERROR; + if ((status = env->Object_CallMethodByName_Void(reinterpret_cast(etsConnectionRef_), + methodName, signature, args)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "Failed to CallObjectMethod , status: %{public}d", status); + } + va_end(args); +} + +bool EtsUIServiceUIExtConnection::IsEtsCallbackObjectEquals(ani_env *env, ani_ref callback, ani_object value) +{ + if (env == nullptr || callback == nullptr || value == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "env or callback or value null"); + return false; + } + ani_boolean isEquals = ANI_FALSE; + ani_status status = ANI_ERROR; + if ((status = env->Reference_StrictEquals(callback, value, &isEquals)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "Reference_StrictEquals failed status: %{public}d", status); + return false; + } + return isEquals == ANI_TRUE; +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.cpp b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.cpp new file mode 100644 index 00000000000..75c4456625b --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.cpp @@ -0,0 +1,174 @@ +/* + * 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_service_host_proxy.h" + +#include "ability_business_error.h" +#include "ani_common_want.h" +#include "ets_error_utils.h" +#include "hilog_tag_wrapper.h" +#include "ipc_skeleton.h" +#include "ipc_types.h" +#include "permission_constants.h" +#include "tokenid_kit.h" +#include "ui_service_host_proxy.h" + +namespace OHOS { +namespace AAFwk { +using namespace AbilityRuntime; + +constexpr const char *UI_SERVICE_HOST_PROXY_CLASS_NAME = "Lapplication/UIServiceHostProxy/UIServiceHostProxyImpl;"; + +ani_object EtsUIServiceHostProxy::CreateEtsUIServiceHostProxy(ani_env *env, const sptr &impl) +{ + TAG_LOGI(AAFwkTag::UISERVC_EXT, "CreateEtsUIServiceHostProxy called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return nullptr; + } + ani_object object = nullptr; + ani_class cls {}; + ani_status status = env->FindClass(UI_SERVICE_HOST_PROXY_CLASS_NAME, &cls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "FindClass failed status: %{public}d", status); + return nullptr; + } + ani_method method = nullptr; + status = env->Class_FindMethod(cls, "", ":V", &method); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ABILITY, "call Class_FindMethod ctor failed"); + return nullptr; + } + status = env->Object_New(cls, method, &object); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ABILITY, "call Object_New abilityStageCtxCls failed"); + return nullptr; + } + ani_field field = nullptr; + if ((status = env->Class_FindField(cls, "nativeServiceHostProxy", &field)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return nullptr; + } + std::unique_ptr proxy = std::make_unique(impl); + ani_long nativeServiceHostProxyLong = reinterpret_cast(proxy.release()); + if ((status = env->Object_SetField_Long(object, field, nativeServiceHostProxyLong)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return nullptr; + } + std::array functions = { + ani_native_function {"nativeSendData", nullptr, + reinterpret_cast(EtsUIServiceHostProxy::SendData)}}; + if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK + && status != ANI_ALREADY_BINDED) { + TAG_LOGE(AAFwkTag::APPKIT, "Class_BindNativeMethods status: %{public}d", status); + return nullptr; + } + return object; +} + +EtsUIServiceHostProxy::EtsUIServiceHostProxy(const sptr &impl) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "EtsUIServiceHostProxy called"); + if (impl != nullptr) { + proxy_ = iface_cast(impl); + } + if (proxy_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null proxy"); + } +} + +EtsUIServiceHostProxy::~EtsUIServiceHostProxy() +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "EtsUIServiceHostProxy destroyed"); + proxy_ = nullptr; +} + +bool EtsUIServiceHostProxy::CheckCallerIsSystemApp() +{ + auto selfToken = IPCSkeleton::GetSelfTokenID(); + if (!Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(selfToken)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "not allow"); + return false; + } + return true; +} + +EtsUIServiceHostProxy* EtsUIServiceHostProxy::GetEtsUIServiceHostProxy(ani_env *env, ani_object obj) +{ + if (env == nullptr || obj == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env or obj"); + return nullptr; + } + EtsUIServiceHostProxy *serviceHostProxy = nullptr; + ani_status status = ANI_ERROR; + ani_long nativeServiceHostProxyLong = 0; + if ((status = env->Object_GetFieldByName_Long(obj, "nativeServiceHostProxy", + &nativeServiceHostProxyLong)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return nullptr; + } + serviceHostProxy = reinterpret_cast(nativeServiceHostProxyLong); + if (serviceHostProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "serviceHostProxy null"); + return nullptr; + } + return serviceHostProxy; +} + +void EtsUIServiceHostProxy::SendData(ani_env *env, ani_object obj, ani_object data) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "SendData called"); + EtsUIServiceHostProxy* etsProxy = GetEtsUIServiceHostProxy(env, obj); + if (etsProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetEtsUIServiceHostProxy failed"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return; + } + etsProxy->OnSendData(env, data); +} + +void EtsUIServiceHostProxy::OnSendData(ani_env *env, ani_object data) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return; + } + if (!CheckCallerIsSystemApp()) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "CheckCallerIsSystemApp failed"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_NOT_SYSTEM_APP); + return; + } + if (proxy_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null proxy_"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return; + } + AAFwk::WantParams params; + bool result = AppExecFwk::UnwrapWantParams(env, data, params); + if (!result) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "UnwrapWantParams failed"); + AbilityRuntime::EtsErrorUtil::ThrowError( + env, static_cast(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), "Data verification failed"); + return; + } + + int32_t ret = proxy_->SendData(params); + if (ret != static_cast(AbilityErrorCode::ERROR_OK)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "SendData failed"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + } +} +} +} diff --git a/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.h b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.h new file mode 100644 index 00000000000..74a248fd448 --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.h @@ -0,0 +1,45 @@ +/* + * 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_SERVICE_HOST_PROXY_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_HOST_PROXY_H + +#include + +#include "ani.h" +#include "ets_runtime.h" +#include "ui_service_host_proxy.h" + +namespace OHOS { +namespace AAFwk { +using namespace AbilityRuntime; + +class EtsUIServiceHostProxy { +public: + static ani_object CreateEtsUIServiceHostProxy(ani_env *env, const sptr &impl); + explicit EtsUIServiceHostProxy(const sptr &impl); + virtual ~EtsUIServiceHostProxy(); + static EtsUIServiceHostProxy *GetEtsUIServiceHostProxy(ani_env *env, ani_object obj); + static void SendData(ani_env *env, ani_object obj, ani_object data); +private: + bool CheckCallerIsSystemApp(); + void OnSendData(ani_env *env, ani_object data); +protected: + sptr proxy_; +}; + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_HOST_PROXY_H \ No newline at end of file diff --git a/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.cpp b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.cpp new file mode 100644 index 00000000000..f778415312e --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.cpp @@ -0,0 +1,181 @@ +/* + * 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_service_proxy.h" + +#include "ability_business_error.h" +#include "ani_common_want.h" +#include "ets_error_utils.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AAFwk { +using namespace AbilityRuntime; + +constexpr const char *UI_SERVICE_PROXY_CLASS_NAME = "Lapplication/UIServiceProxy/UIServiceProxyImpl;"; + +ani_object EtsUIServiceProxy::CreateEtsUIServiceProxy( + ani_env *env, const sptr &impl, int64_t connectionId, const sptr &hostProxy) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "CreateEtsUIServiceProxy called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return nullptr; + } + ani_object object = nullptr; + ani_class cls {}; + ani_status status = env->FindClass(UI_SERVICE_PROXY_CLASS_NAME, &cls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "FindClass failed status: %{public}d", status); + return nullptr; + } + ani_method method = nullptr; + status = env->Class_FindMethod(cls, "", ":V", &method); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ABILITY, "call Class_FindMethod ctor failed"); + return nullptr; + } + status = env->Object_New(cls, method, &object); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ABILITY, "call Object_New failed"); + return nullptr; + } + ani_field field = nullptr; + if ((status = env->Class_FindField(cls, "nativeServiceProxy", &field)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return nullptr; + } + std::unique_ptr proxy = std::make_unique(impl, hostProxy); + proxy->SetConnectionId(connectionId); + + ani_long nativeServiceProxyLong = reinterpret_cast(proxy.release()); + if ((status = env->Object_SetField_Long(object, field, nativeServiceProxyLong)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return nullptr; + } + + std::array functions = { + ani_native_function {"nativeSendData", nullptr, + reinterpret_cast(EtsUIServiceProxy::SendData)}}; + + if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK + && status != ANI_ALREADY_BINDED) { + TAG_LOGE(AAFwkTag::APPKIT, "Class_BindNativeMethods status: %{public}d", status); + return nullptr; + } + return object; +} + +ani_object EtsUIServiceProxy::CreateEmptyProxyObject(ani_env *env) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "CreateEmptyProxyObject called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return nullptr; + } + ani_object object = nullptr; + ani_class cls {}; + ani_status status = env->FindClass(UI_SERVICE_PROXY_CLASS_NAME, &cls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "FindClass failed status: %{public}d", status); + return nullptr; + } + ani_method method = nullptr; + status = env->Class_FindMethod(cls, "", ":V", &method); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ABILITY, "call Class_FindMethod ctor failed"); + return nullptr; + } + status = env->Object_New(cls, method, &object); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ABILITY, "call Object_New abilityStageCtxCls failed"); + return nullptr; + } + return object; +} + +EtsUIServiceProxy::EtsUIServiceProxy(const sptr &impl, const sptr &hostProxy) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "EtsUIServiceProxy called"); + proxy_ = iface_cast(impl); + hostProxy_ = hostProxy; + if (proxy_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null proxy"); + } +} + +EtsUIServiceProxy::~EtsUIServiceProxy() +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "EtsUIServiceProxy destroyed"); + proxy_ = nullptr; + hostProxy_ = nullptr; +} + +EtsUIServiceProxy* EtsUIServiceProxy::GetEtsUIServiceProxy(ani_env *env, ani_object obj) +{ + if (env == nullptr || obj == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env or obj"); + return nullptr; + } + EtsUIServiceProxy *serviceProxy = nullptr; + ani_status status = ANI_ERROR; + ani_long nativeServiceProxyLong = 0; + if ((status = env->Object_GetFieldByName_Long(obj, "nativeServiceProxy", &nativeServiceProxyLong)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return nullptr; + } + serviceProxy = reinterpret_cast(nativeServiceProxyLong); + if (serviceProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "serviceProxy null"); + return nullptr; + } + return serviceProxy; +} + +void EtsUIServiceProxy::SendData(ani_env *env, ani_object obj, ani_object data) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "SendData called"); + EtsUIServiceProxy* etsUIServiceProxy = GetEtsUIServiceProxy(env, obj); + if (etsUIServiceProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetEtsUIServiceProxy failed"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return; + } + etsUIServiceProxy->OnSendData(env, data); +} + +void EtsUIServiceProxy::OnSendData(ani_env *env, ani_object data) +{ + if (proxy_ == nullptr || hostProxy_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null proxy_ or hostProxy_"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return; + } + AAFwk::WantParams params; + bool result = AppExecFwk::UnwrapWantParams(env, data, params); + if (!result) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "UnwrapWantParams failed"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM); + return; + } + + int32_t ret = proxy_->SendData(hostProxy_, params); + if (ret != static_cast(AbilityErrorCode::ERROR_OK)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "proxy_->SendData failed"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + } +} +} // namespace AAFwk +} // namespace OHOS diff --git a/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.h b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.h new file mode 100644 index 00000000000..eb1182108b2 --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.h @@ -0,0 +1,51 @@ +/* + * 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_SERVICE_PROXY_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_PROXY_H + +#include + +#include "ani.h" +#include "ets_runtime.h" +#include "ui_service_proxy.h" + +namespace OHOS { +namespace AAFwk { +using namespace AbilityRuntime; + +class EtsUIServiceProxy { +public: + static ani_object CreateEtsUIServiceProxy(ani_env *env, const sptr &impl, + int64_t connectionId, const sptr &hostProxy); + static ani_object CreateEmptyProxyObject(ani_env *env); + EtsUIServiceProxy(const sptr &impl, const sptr &hostProxy); + virtual ~EtsUIServiceProxy(); + static EtsUIServiceProxy* GetEtsUIServiceProxy(ani_env *env, ani_object obj); + static void SendData(ani_env *env, ani_object obj, ani_object data); + void SetConnectionId(int64_t id) { connectionId_ = id; } + int64_t GetConnectionId() { return connectionId_; } +private: + void OnSendData(ani_env *env, ani_object data); + +protected: + sptr proxy_ = nullptr; + int64_t connectionId_ = 0; + sptr hostProxy_ = nullptr; +}; + +} // namespace AAFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_PROXY_H \ No newline at end of file diff --git a/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension.h b/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension.h new file mode 100644 index 00000000000..7fd3a2006c0 --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension.h @@ -0,0 +1,245 @@ +/* + * 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_SERVICE_EXTENSION_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_EXTENSION_H + +#include "ability_info.h" +#include "ani.h" +#include "configuration.h" +#include "ets_native_reference.h" +#include "ets_runtime.h" +#include "ui_service_extension.h" +#include "ui_service_extension_context.h" +#include "ui_service_stub.h" +#ifdef SUPPORT_GRAPHICS +#include "display_manager.h" +#include "js_window_stage.h" +#include "system_ability_status_change_stub.h" +#include "window_option.h" +#endif + +namespace OHOS { +namespace AbilityRuntime { +class UIServiceExtension; +class Runtime; +class UIServiceExtensionContext; +class EtsUIServiceExtension; + +class UIEtsServiceStubImpl : public AAFwk::UIServiceStub { +public: + explicit UIEtsServiceStubImpl(std::weak_ptr &ext); + virtual ~UIEtsServiceStubImpl(); + virtual int32_t SendData(sptr hostProxy, AAFwk::WantParams &data) override; + +protected: + std::weak_ptr extension_; +}; + +/** + * @brief Basic service components. + */ +class EtsUIServiceExtension : public UIServiceExtension { +public: + explicit EtsUIServiceExtension(ETSRuntime &etsRuntime); + virtual ~EtsUIServiceExtension() override; + + /** + * @brief Create EtsServiceExtension. + * + * @param runtime The runtime. + * @return The EtsServiceExtension instance. + */ + static EtsUIServiceExtension* Create(const std::unique_ptr &runtime); + + /** + * @brief Init the extension. + * + * @param record the extension record. + * @param application the application info. + * @param handler the extension handler. + * @param token the remote token. + */ + virtual void Init(const std::shared_ptr &record, + const std::shared_ptr &application, + std::shared_ptr &handler, + const sptr &token) override; + + /** + * @brief Called when this extension is started. You must override this function if you want to perform some + * initialization operations during extension startup. + * + * This function can be called only once in the entire lifecycle of an extension. + * @param Want Indicates the {@link Want} structure containing startup information about the extension. + */ + void OnStart(const AAFwk::Want &want) override; + + /** + * @brief Called when this extension enters the STATE_STOP state. + * + * The extension in the STATE_STOP is being destroyed. + * You can override this function to implement your own processing logic. + */ + void OnStop() override; + + /** + * @brief Called when this Service extension is connected for the first time. + * + * You can override this function to implement your own processing logic. + * + * @param want Indicates the {@link Want} structure containing connection information about the Service extension. + * @param callbackInfo Indicates the lifecycle transaction callback information + * @param isAsyncCallback Indicates whether it is an asynchronous lifecycle callback + * @return Returns a pointer to the sid of the connected Service extension. + */ + sptr OnConnect(const AAFwk::Want &want, + AppExecFwk::AbilityTransactionCallbackInfo> *callbackInfo, bool &isAsyncCallback) override; + + /** + * @brief Called when all abilities connected to this Service extension are disconnected. + * + * You can override this function to implement your own processing logic. + * @param callbackInfo Indicates the lifecycle transaction callback information + * @param isAsyncCallback Indicates whether it is an asynchronous lifecycle callback + */ + void OnDisconnect(const AAFwk::Want &want, AppExecFwk::AbilityTransactionCallbackInfo<> *callbackInfo, + bool &isAsyncCallback) override; + + /** + * @brief Called back when Service is started. + * This method can be called only by Service. You can use the StartAbility(ohos.aafwk.content.Want) method to start + * Service. Then the system calls back the current method to use the transferred want parameter to execute its own + * logic. + * + * @param want Indicates the want of Service to start. + * @param restart Indicates the startup mode. The value true indicates that Service is restarted after being + * destroyed, and the value false indicates a normal startup. + * @param startId Indicates the number of times the Service extension has been started. The startId is incremented + * by 1 every time the extension is started. For example, if the extension has been started for six times, the + * value of startId is 6. + */ + void OnCommand(const AAFwk::Want &want, bool restart, int startId) override; + + /** + * @brief Called when the system configuration is updated. + * + * @param configuration Indicates the updated configuration information. + */ + void OnConfigurationUpdated(const AppExecFwk::Configuration& configuration) override; + + /** + * @brief Called when configuration changed, including system configuration and window configuration. + * + */ + void ConfigurationUpdated(); + + /** + * @brief Called when client send data to extension. + * + * @param hostProxy the proxy used to send data back to client + * @param data The data to send. + */ + int32_t OnSendData(sptr hostProxy, OHOS::AAFwk::WantParams &data); + +protected: + bool showOnLockScreen_ = false; + +private: + bool CreateWindowIfNeeded(); + void AbilityWindowConfigTransition(sptr& option, uint32_t windowId); + bool CallObjectMethod(bool withResult, const char* name, const char* signature, ...); + void HandleSendData(sptr hostProxy, const OHOS::AAFwk::WantParams &data); + sptr GetHostProxyFromWant(const AAFwk::Want &want); + void BindContext(ani_env *env); + void GetSrcPath(std::string& srcPath); + void ListenWMS(); + ani_env *AttachCurrentThread(); + void DetachCurrentThread(); + + ETSRuntime& etsRuntime_; + std::unique_ptr etsObj_ = nullptr; + std::shared_ptr aContext_ = nullptr; + std::shared_ptr shellContextRef_ = nullptr; + std::shared_ptr handler_ = nullptr; + int32_t hostWindowIdInStart_ = 0; + sptr extensionStub_; + std::map, ani_ref> hostProxyMap_; + bool firstRequest_ = true; + ani_vm *etsVm_ = nullptr; + bool isAttachThread_ = true; + +#ifdef SUPPORT_GRAPHICS + void InitDisplayId(AAFwk::Want &want, AAFwk::StartOptions &startOptions, napi_env &env, NapiCallbackInfo& info); + void OnSceneWillCreated(std::shared_ptr extensionWindowConfig); + void OnSceneDidCreated(sptr& window); +protected: + class EtsUIServiceExtensionDisplayListener : public Rosen::DisplayManager::IDisplayListener { + public: + explicit EtsUIServiceExtensionDisplayListener(const std::weak_ptr& etsUIServiceExtension) + { + etsUIServiceExtension_ = etsUIServiceExtension; + } + + void OnCreate(Rosen::DisplayId displayId) override + { + auto ptrEtsUIServiceExtension = etsUIServiceExtension_.lock(); + if (ptrEtsUIServiceExtension != nullptr) { + ptrEtsUIServiceExtension->OnCreate(displayId); + } + } + + void OnDestroy(Rosen::DisplayId displayId) override + { + auto ptrEtsUIServiceExtension = etsUIServiceExtension_.lock(); + if (ptrEtsUIServiceExtension != nullptr) { + ptrEtsUIServiceExtension->OnDestroy(displayId); + } + } + + void OnChange(Rosen::DisplayId displayId) override + { + auto ptrEtsUIServiceExtension = etsUIServiceExtension_.lock(); + if (ptrEtsUIServiceExtension != nullptr) { + ptrEtsUIServiceExtension->OnChange(displayId); + } + } + + private: + std::weak_ptr etsUIServiceExtension_; + }; + + void OnCreate(Rosen::DisplayId displayId); + void OnDestroy(Rosen::DisplayId displayId); + void OnChange(Rosen::DisplayId displayId); + +private: + class SystemAbilityStatusChangeListener : public OHOS::SystemAbilityStatusChangeStub { + public: + SystemAbilityStatusChangeListener(sptr displayListener) + : tmpDisplayListener_(displayListener) {}; + virtual void OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId) override; + virtual void OnRemoveSystemAbility(int32_t systemAbilityId, const std::string& deviceId) override {} + + private: + sptr tmpDisplayListener_ = nullptr; + }; + + sptr displayListener_ = nullptr; + sptr saStatusChangeListener_ = nullptr; +#endif +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_EXTENSION_H diff --git a/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_context.h b/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_context.h new file mode 100644 index 00000000000..3d6faf4d70f --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_context.h @@ -0,0 +1,90 @@ +/* + * 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_SERVICE_EXTENSION_CONTEXT_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_EXTENSION_CONTEXT_H + +#include + +#include "ability_connect_callback.h" +#include "event_handler.h" +#include "ets_free_install_observer.h" +#include "ets_runtime.h" +#include "ui_service_extension_context.h" + +namespace OHOS { +namespace AbilityRuntime { +ani_object CreateEtsUIServiceExtensionContext(ani_env *env, std::shared_ptr context); + +class EtsUIServiceExtensionConnection : public AbilityConnectCallback { +public: + explicit EtsUIServiceExtensionConnection(ani_vm *etsVm); + virtual ~EtsUIServiceExtensionConnection(); + void OnAbilityConnectDone( + const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode) override; + void OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode) override; + void HandleOnAbilityConnectDone( + const AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode); + void HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode); + void SetEtsConnectionObject(ani_object connObject); + void RemoveConnectionObject(); + void CallEtsFailed(int32_t errorCode); + void SetConnectionId(int64_t id); + int64_t GetConnectionId(); +private: + void ReleaseObjectReference(ani_ref etsObjRef); + ani_vm *etsVm_ = nullptr; + ani_ref etsConnectionObject_ = nullptr; + int64_t connectionId_ = -1; +}; + +class EtsUIServiceExtensionContext final { +public: + explicit EtsUIServiceExtensionContext( + const std::shared_ptr &context) : context_(context) {} + ~EtsUIServiceExtensionContext() = default; + static EtsUIServiceExtensionContext* GetEtsUIServiceExtensionContext(ani_env *env, ani_object obj); + static void StartAbility(ani_env *env, ani_object obj, + ani_object callback, ani_object aniWant, ani_object aniStartOption); + static void TerminateSelf(ani_env *env, ani_object obj, ani_object callback); + static void StartAbilityByType(ani_env *env, ani_object obj, ani_string aniType, ani_object aniWantParam, + ani_object abilityStartCallback, ani_object callback); + static ani_long ConnectServiceExtensionAbility(ani_env *env, ani_object obj, + ani_object aniWant, ani_object aniOptions); + static void DisConnectServiceExtensionAbility(ani_env *env, ani_object obj, + ani_long aniConnectionId, ani_object callback); + static bool BindNativeMethods(ani_env *env); + static bool BindNativePtrCleaner(ani_env *env); + static void Clean(ani_env *env, ani_object object); +private: + void OnStartAbility(ani_env *env, ani_object callback, ani_object aniWant, ani_object aniStartOption); + void OnTerminateSelf(ani_env *env, ani_object callback); + void OnStartAbilityByType(ani_env *env, ani_string aniType, ani_object aniWantParam, + ani_object abilityStartCallback, ani_object callback); + ani_long OnConnectServiceExtensionAbility(ani_env *env, ani_object aniWant, ani_object aniOptions); + void OnDisConnectServiceExtensionAbility(ani_env *env, ani_long aniConnectionId, ani_object callback); + std::shared_ptr GetContext(); + void InitDisplayId(AAFwk::Want &want, AAFwk::StartOptions &startOptions); + bool CheckConnectionParam(ani_env *env, ani_object object, + sptr& connection, AAFwk::Want& want, int32_t accountId = -1); + void RemoveConnection(int64_t connectId); + void FindConnection(AAFwk::Want &want, sptr &connection, int64_t &connectId, + int32_t &accountId); + std::weak_ptr context_; +}; // EtsUIServiceExtensionContext + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_EXTENSION_CONTEXT_H \ No newline at end of file diff --git a/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_instance.h b/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_instance.h new file mode 100644 index 00000000000..bd803a7d307 --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/include/ets_ui_service_extension_instance.h @@ -0,0 +1,29 @@ +/* + * 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_SERVICE_EXTENSION_INSTANCE_H +#define OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_EXTENSION_INSTANCE_H + +#include "ui_service_extension.h" + +namespace OHOS { +namespace AbilityRuntime { +class UIServiceExtension; +class Runtime; + +UIServiceExtension *CreateETSUIServiceExtension(const std::unique_ptr &runtime); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_UI_SERVICE_EXTENSION_INSTANCE_H \ No newline at end of file diff --git a/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension.cpp b/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension.cpp new file mode 100644 index 00000000000..28c73026f82 --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension.cpp @@ -0,0 +1,710 @@ +/* + * 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_service_extension.h" + +#include + +#include "ability.h" +#include "ability_business_error.h" +#include "ability_context.h" +#include "ability_handler.h" +#include "ability_info.h" +#include "ability_manager_client.h" +#include "ani_common_configuration.h" +#include "ani_common_want.h" +#include "ani_window.h" +#include "configuration_utils.h" +#include "display_util.h" +#include "ets_extension_common.h" +#include "ets_extension_context.h" +#include "ets_ui_service_extension_context.h" +#include "ets_ui_service_host_proxy.h" +#include "ets_ui_service_proxy.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "scene_board_judgement.h" +#include "session_info.h" +#include "ui_service_extension_connection_constants.h" +#include "window.h" +#include "window_scene.h" +#include "wm_common.h" + +#ifdef SUPPORT_GRAPHICS +#include "iservice_registry.h" +#include "system_ability_definition.h" +#endif + +#ifdef WINDOWS_PLATFORM +#define ETS_EXPORT __declspec(dllexport) +#else +#define ETS_EXPORT __attribute__((visibility("default"))) +#endif + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char* WANT_PARAMS_HOST_WINDOW_ID_KEY = "ohos.extra.param.key.hostwindowid"; +} + +using namespace OHOS::AppExecFwk; +UIEtsServiceStubImpl::UIEtsServiceStubImpl(std::weak_ptr& ext) + :extension_(ext) +{ +} + +UIEtsServiceStubImpl::~UIEtsServiceStubImpl() +{ +} + +int32_t UIEtsServiceStubImpl::SendData(sptr hostProxy, AAFwk::WantParams &data) +{ + auto sptr = extension_.lock(); + if (sptr) { + return sptr->OnSendData(hostProxy, data); + } + return static_cast(AbilityErrorCode::ERROR_CODE_INNER); +} + +EtsUIServiceExtension* EtsUIServiceExtension::Create(const std::unique_ptr& runtime) +{ + if (runtime == nullptr) { + return nullptr; + } + return new (std::nothrow) EtsUIServiceExtension(static_cast(*runtime)); +} + +EtsUIServiceExtension::EtsUIServiceExtension(AbilityRuntime::ETSRuntime& etsRuntime) : etsRuntime_(etsRuntime) {} + +EtsUIServiceExtension::~EtsUIServiceExtension() +{ + auto context = GetContext(); + if (context) { + context->Unbind(); + } +} + +void EtsUIServiceExtension::BindContext(ani_env *env) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "BindContext call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env null"); + return; + } + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + return; + } + ani_object contextObj = CreateEtsUIServiceExtensionContext(env, context); + if (contextObj == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null shellContextRef_"); + return; + } + ani_field contextField; + auto status = env->Class_FindField(etsObj_->aniCls, "context", &contextField); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return; + } + ani_ref contextRef = nullptr; + if ((status = env->GlobalReference_Create(contextObj, &contextRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return; + } + + if ((status = env->Object_SetField_Ref(etsObj_->aniObj, contextField, contextRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + } + + shellContextRef_ = std::make_shared(); + shellContextRef_->aniObj = contextObj; + shellContextRef_->aniRef = contextRef; + + TAG_LOGD(AAFwkTag::UISERVC_EXT, "end."); +} + +void EtsUIServiceExtension::Init(const std::shared_ptr &record, + const std::shared_ptr &application, std::shared_ptr &handler, + const sptr &token) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Init"); + UIServiceExtension::Init(record, application, handler, token); + + std::string srcPath = ""; + GetSrcPath(srcPath); + if (srcPath.empty()) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "srcPath empty"); + return; + } + if (abilityInfo_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null abilityInfo_"); + return; + } + std::string moduleName(abilityInfo_->moduleName); + moduleName.append("::").append(abilityInfo_->name); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "UIServiceExtension::Init entryPath:%{public}s", abilityInfo_->srcEntrance.c_str()); + etsObj_ = etsRuntime_.LoadModule( + moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == AppExecFwk::CompileMode::ES_MODULE, + false, abilityInfo_->srcEntrance); + if (etsObj_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsObj_"); + return; + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + BindContext(env); + SetExtensionCommon(EtsExtensionCommon::Create( + etsRuntime_, static_cast(*etsObj_), shellContextRef_)); + + ListenWMS(); +} + +void EtsUIServiceExtension::ListenWMS() +{ +#ifdef SUPPORT_GRAPHICS + TAG_LOGD(AAFwkTag::UISERVC_EXT, "ListenWMS clled"); + auto abilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (abilityManager == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null SaMgr"); + return; + } + + auto etsUIServiceExtension = std::static_pointer_cast(shared_from_this()); + displayListener_ = sptr::MakeSptr(etsUIServiceExtension); + if (displayListener_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null displayListener"); + return; + } + + saStatusChangeListener_ = sptr::MakeSptr(displayListener_); + if (saStatusChangeListener_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null saStatusChangeListener"); + return; + } + + auto ret = abilityManager->SubscribeSystemAbility(WINDOW_MANAGER_SERVICE_ID, saStatusChangeListener_); + if (ret != 0) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "ret = %{public}d", ret); + } +#endif +} + +void EtsUIServiceExtension::SystemAbilityStatusChangeListener::OnAddSystemAbility(int32_t systemAbilityId, + const std::string& deviceId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "systemAbilityId: %{public}d add", systemAbilityId); + if (systemAbilityId == WINDOW_MANAGER_SERVICE_ID) { + Rosen::DisplayManager::GetInstance().RegisterDisplayListener(tmpDisplayListener_); + } +} + +bool EtsUIServiceExtension::CallObjectMethod(bool withResult, const char *name, const char *signature, ...) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, std::string("CallObjectMethod:") + name); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "CallObjectMethod name:%{public}s", name); + if (etsObj_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "etsObj_ nullptr"); + return false; + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null env"); + return false; + } + ani_status status = ANI_OK; + ani_method method = nullptr; + if ((status = env->Class_FindMethod(etsObj_->aniCls, name, signature, &method)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return false; + } + env->ResetError(); + if (withResult) { + ani_boolean res = 0; + va_list args; + va_start(args, signature); + if ((status = env->Object_CallMethod_Boolean(etsObj_->aniObj, method, &res, args)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + etsRuntime_.HandleUncaughtError(); + } + va_end(args); + return res; + } + va_list args; + va_start(args, signature); + if ((status = env->Object_CallMethod_Void_V(etsObj_->aniObj, method, args)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + etsRuntime_.HandleUncaughtError(); + return false; + } + va_end(args); + return false; +} + +void EtsUIServiceExtension::OnStart(const AAFwk::Want &want) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnStart called"); + Extension::OnStart(want); + auto context = GetContext(); + if (context != nullptr) { + int32_t displayId = AAFwk::DisplayUtil::GetDefaultDisplayId(); + displayId = want.GetIntParam(Want::PARAM_RESV_DISPLAY_ID, displayId); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "displayId %{public}d", displayId); + auto configUtils = std::make_shared(); + configUtils->InitDisplayConfig(displayId, context->GetConfiguration(), context->GetResourceManager()); + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + if (context != nullptr) { + EtsExtensionContext::ConfigurationUpdated(env, shellContextRef_, context->GetConfiguration()); + } + if (want.HasParameter(WANT_PARAMS_HOST_WINDOW_ID_KEY)) { + hostWindowIdInStart_ = want.GetIntParam(WANT_PARAMS_HOST_WINDOW_ID_KEY, 0); + } + ani_object aniWant = AppExecFwk::WrapWant(env, want); + if (aniWant == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null aniWant"); + return; + } + CallObjectMethod(false, "onCreate", "L@ohos/app/ability/Want/Want;:V", aniWant); +} + +void EtsUIServiceExtension::OnStop() +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnStop called"); + Extension::OnStop(); + auto context = GetContext(); + if (context != nullptr) { + sptr win = context->GetWindow(); + if (win != nullptr) { + TAG_LOGI(AAFwkTag::UISERVC_EXT, "Destroy Window"); + win->Destroy(); + context->SetWindow(nullptr); + } + } + CallObjectMethod(false, "onDestroy", nullptr); + bool ret = ConnectionManager::GetInstance().DisconnectCaller(GetContext()->GetToken()); + if (ret) { + ConnectionManager::GetInstance().ReportConnectionLeakEvent(getpid(), gettid()); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "The service extension connection is not disconnected."); + } +#ifdef SUPPORT_GRAPHICS + Rosen::DisplayManager::GetInstance().UnregisterDisplayListener(displayListener_); + if (saStatusChangeListener_) { + auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (saMgr) { + saMgr->UnSubscribeSystemAbility(WINDOW_MANAGER_SERVICE_ID, saStatusChangeListener_); + } else { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "OnStop SaMgr null"); + } + } +#endif + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnStop end"); +} + +sptr EtsUIServiceExtension::OnConnect(const AAFwk::Want &want, + AppExecFwk::AbilityTransactionCallbackInfo> *callbackInfo, bool &isAsyncCallback) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGI(AAFwkTag::UISERVC_EXT, "OnConnect call"); + Extension::OnConnect(want); + sptr hostProxy = GetHostProxyFromWant(want); + if (hostProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null hostProxy"); + return nullptr; + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return nullptr; + } + ani_object aniWant = AppExecFwk::WrapWant(env, want); + if (aniWant == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null aniWant"); + return nullptr; + } + if (extensionStub_ == nullptr) { + std::weak_ptr weakThis = + std::static_pointer_cast(shared_from_this()); + extensionStub_ = sptr::MakeSptr(weakThis); + } + sptr stubObject = nullptr; + if (extensionStub_ != nullptr) { + stubObject = extensionStub_->AsObject(); + } + if (hostProxyMap_.find(hostProxy) != hostProxyMap_.end()) { + TAG_LOGI(AAFwkTag::UISERVC_EXT, "hostProxy exist"); + return stubObject; + } + ani_object hostProxyObj = AAFwk::EtsUIServiceHostProxy::CreateEtsUIServiceHostProxy(env, hostProxy); + if (hostProxyObj == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null hostProxyObj"); + return nullptr; + } + CallObjectMethod(false, "onConnect", + "L@ohos/app/ability/Want/Want;Lapplication/UIServiceHostProxy/UIServiceHostProxy;:V", + aniWant, hostProxyObj); + + ani_ref hostProxyRef = nullptr; + ani_status status = ANI_OK; + if ((status = env->GlobalReference_Create(hostProxyObj, &hostProxyRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return nullptr; + } + bool ret = CreateWindowIfNeeded(); + if (!ret) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "create window failed"); + if ((status = env->GlobalReference_Delete(hostProxyRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + } + return nullptr; + } + hostProxyMap_[hostProxy] = hostProxyRef; + return stubObject; +} + +void EtsUIServiceExtension::OnDisconnect(const AAFwk::Want &want, + AppExecFwk::AbilityTransactionCallbackInfo<> *callbackInfo, bool &isAsyncCallback) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGI(AAFwkTag::UISERVC_EXT, "OnDisconnect call"); + Extension::OnDisconnect(want); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + sptr hostProxy = GetHostProxyFromWant(want); + if (hostProxy == nullptr) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "null hostProxy"); + return; + } + ani_object aniWant = AppExecFwk::WrapWant(env, want); + if (aniWant == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null aniWant"); + return; + } + ani_ref etsHostProxy = nullptr; + auto iter = hostProxyMap_.find(hostProxy); + if (iter != hostProxyMap_.end()) { + if (iter->second != nullptr) { + etsHostProxy = iter->second; + } + } + if (etsHostProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsHostProxy"); + return; + } + CallObjectMethod(false, "onDisconnect", nullptr, aniWant, etsHostProxy); + hostProxyMap_.erase(iter); +} + +void EtsUIServiceExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnCommand call"); + Extension::OnCommand(want, restart, startId); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "restart=%{public}s,startId=%{public}d.", restart ? "true" : "false", startId); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + // wrap want + ani_object aniWant = AppExecFwk::WrapWant(env, want); + if (aniWant == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null aniWant"); + return; + } + // wrap startId + ani_double aniStartId = static_cast(startId); + CallObjectMethod(false, "onRequest", "L@ohos/app/ability/Want/Want;D:V", aniWant, aniStartId); + CreateWindowIfNeeded(); +} + +bool EtsUIServiceExtension::CreateWindowIfNeeded() +{ +#ifdef SUPPORT_GRAPHICS + if (firstRequest_ == false) { + return true; + } + firstRequest_ = false; + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + return false; + } + TAG_LOGI(AAFwkTag::UISERVC_EXT, "create window hostWindowId %{public}d", hostWindowIdInStart_); + auto extensionWindowConfig = std::make_shared(); + OnSceneWillCreated(extensionWindowConfig); + auto option = GetWindowOption(extensionWindowConfig, hostWindowIdInStart_); + sptr extensionWindow = nullptr; + if (option != nullptr) { + HITRACE_METER_NAME(HITRACE_TAG_APP, "Rosen::Window::Create"); + extensionWindow = Rosen::Window::Create(extensionWindowConfig->windowName, option, context); + } + if (extensionWindow == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null extensionWindow"); + context->TerminateSelf(); + return false; + } + OnSceneDidCreated(extensionWindow); + context->SetWindow(extensionWindow); + AbilityWindowConfigTransition(option, extensionWindow->GetWindowId()); +#endif + return true; +} + +void EtsUIServiceExtension::AbilityWindowConfigTransition(sptr& option, uint32_t windowId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "AbilityWindowConfigTransition call"); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + return; + } + auto token = context->GetToken(); + if (token == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null token"); + return; + } + AAFwk::WindowConfig windowConfig; + if (option != nullptr) { + windowConfig.windowType = static_cast(option->GetWindowType()); + } + windowConfig.windowId = windowId; + + AAFwk::AbilityManagerClient::GetInstance()->AbilityWindowConfigTransitionDone(token, windowConfig); +} + +int32_t EtsUIServiceExtension::OnSendData(sptr hostProxy, OHOS::AAFwk::WantParams &data) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnSendData call"); + HandleSendData(hostProxy, data); + return static_cast(AbilityErrorCode::ERROR_OK); +} + +void EtsUIServiceExtension::HandleSendData(sptr hostProxy, const OHOS::AAFwk::WantParams &data) +{ + if (hostProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null hostProxy"); + return; + } + ani_ref etsHostProxy = nullptr; + auto iter = hostProxyMap_.find(hostProxy); + if (iter != hostProxyMap_.end()) { + if (iter->second != nullptr) { + etsHostProxy = iter->second; + } + } + if (etsHostProxy == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsHostProxy"); + return; + } + + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + env = AttachCurrentThread(); + } + ani_ref wantObj = AppExecFwk::WrapWantParams(env, data); + CallObjectMethod(false, "onData", nullptr, etsHostProxy, wantObj); + DetachCurrentThread(); +} + +ani_env *EtsUIServiceExtension::AttachCurrentThread() +{ + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) == ANI_OK) { + return env; + } + ani_option interopEnabled { "--interop=disable", nullptr }; + ani_options aniArgs { 1, &interopEnabled }; + if ((status = etsVm_->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPMGR, "status: %{public}d", status); + return nullptr; + } + isAttachThread_ = true; + return env; +} + +void EtsUIServiceExtension::DetachCurrentThread() +{ + if (isAttachThread_) { + etsVm_->DetachCurrentThread(); + isAttachThread_ = false; + } +} + +sptr EtsUIServiceExtension::GetHostProxyFromWant(const AAFwk::Want &want) +{ + if (!want.HasParameter(UISERVICEHOSTPROXY_KEY)) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "Not found UISERVICEHOSTPROXY_KEY"); + return nullptr; + } + return want.GetRemoteObject(UISERVICEHOSTPROXY_KEY); +} + +void EtsUIServiceExtension::GetSrcPath(std::string &srcPath) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "GetSrcPath start."); + if (!Extension::abilityInfo_->srcEntrance.empty()) { + srcPath.append(Extension::abilityInfo_->moduleName + "/"); + srcPath.append(Extension::abilityInfo_->srcEntrance); + auto pos = srcPath.rfind("."); + if (pos != std::string::npos) { + srcPath.erase(pos); + srcPath.append(".abc"); + } + } +} + +void EtsUIServiceExtension::OnConfigurationUpdated(const AppExecFwk::Configuration& configuration) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnConfigurationUpdated called"); + UIServiceExtension::OnConfigurationUpdated(configuration); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "call"); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + return; + } + auto contextConfig = context->GetConfiguration(); + if (contextConfig != nullptr) { + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Config dump: %{public}s", contextConfig->GetName().c_str()); + std::vector changeKeyV; + contextConfig->CompareDifferent(changeKeyV, configuration); + if (!changeKeyV.empty()) { + contextConfig->Merge(changeKeyV, configuration); + } + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Config dump after merge: %{public}s", contextConfig->GetName().c_str()); + } + ConfigurationUpdated(); +} + +void EtsUIServiceExtension::ConfigurationUpdated() +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "ConfigurationUpdated called"); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + // Notify extension context + auto fullConfig = GetContext()->GetConfiguration(); + if (!fullConfig) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null fullConfig"); + return; + } + ani_object aniConfiguration = AppExecFwk::WrapConfiguration(env, *fullConfig); + if (aniConfiguration == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null aniConfiguration"); + return; + } + CallObjectMethod(false, "onConfigurationUpdate", + "L@ohos/app/ability/Configuration/Configuration;:V", aniConfiguration); + AbilityRuntime::EtsExtensionContext::ConfigurationUpdated(env, shellContextRef_, fullConfig); +} + +#ifdef SUPPORT_GRAPHICS +void EtsUIServiceExtension::OnCreate(Rosen::DisplayId displayId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "enter."); +} + +void EtsUIServiceExtension::OnDestroy(Rosen::DisplayId displayId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "exit."); +} + +void EtsUIServiceExtension::OnChange(Rosen::DisplayId displayId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "displayId: %{public}" PRIu64"", displayId); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + return; + } + auto contextConfig = context->GetConfiguration(); + if (contextConfig == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null configuration"); + return; + } + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Config dump: %{public}s", contextConfig->GetName().c_str()); + bool configChanged = false; + auto configUtils = std::make_shared(); + configUtils->UpdateDisplayConfig(displayId, contextConfig, context->GetResourceManager(), configChanged); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Config dump after update: %{public}s", contextConfig->GetName().c_str()); + + if (configChanged) { + auto etsUIServiceExtension = std::static_pointer_cast(shared_from_this()); + auto task = [etsUIServiceExtension]() { + if (etsUIServiceExtension) { + etsUIServiceExtension->ConfigurationUpdated(); + } + }; + if (handler_ != nullptr) { + handler_->PostTask(task, "EtsServiceExtension:OnChange"); + } + } + TAG_LOGD(AAFwkTag::UISERVC_EXT, "finished."); +} + +void EtsUIServiceExtension::OnSceneWillCreated(std::shared_ptr extensionWindowConfig) +{ + TAG_LOGI(AAFwkTag::UISERVC_EXT, "OnSceneWillCreated call"); + if (extensionWindowConfig == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null extensionWindowConfig"); + return; + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + TAG_LOGI(AAFwkTag::UISERVC_EXT, "end OnSceneWillCreated"); +} + +void EtsUIServiceExtension::OnSceneDidCreated(sptr& window) +{ + TAG_LOGI(AAFwkTag::UISERVC_EXT, "OnSceneDidCreated call"); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return; + } + ani_ref aniWindow = Rosen::CreateAniWindowObject(env, window); + if (aniWindow == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null aniWindow"); + return; + } + CallObjectMethod(false, "onWindowDidCreate", nullptr, aniWindow); + TAG_LOGI(AAFwkTag::UISERVC_EXT, "end OnSceneDidCreated"); +} +#endif +} // AbilityRuntime +} // OHOS + +ETS_EXPORT extern "C" OHOS::AbilityRuntime::UIServiceExtension *OHOS_ETS_UI_SERVICE_Extension_Create( + const std::unique_ptr &runtime) +{ + return OHOS::AbilityRuntime::EtsUIServiceExtension::Create(runtime); +} \ No newline at end of file diff --git a/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_context.cpp b/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_context.cpp new file mode 100644 index 00000000000..65b4bc93c8b --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_context.cpp @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2024 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_service_extension_context.h" + +#include +#include + +#include "ability_manager_client.h" +#include "ability_runtime/js_caller_complex.h" +#include "ani_common_start_options.h" +#include "ani_common_remote.h" +#include "ani_common_util.h" +#include "ani_common_want.h" +#include "ani_remote_object.h" +#include "ets_context_utils.h" +#include "ets_data_struct_converter.h" +#include "ets_error_utils.h" +#include "ets_extension_context.h" +#include "ets_free_install_observer.h" +#include "ets_runtime.h" +#include "ets_ui_extension_callback.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "start_options.h" +#include "ui_service_extension.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const int FAILED_CODE = -1; +constexpr int32_t ERROR_CODE_ONE = 1; +constexpr int32_t ERROR_CODE_TWO = 2; +constexpr int32_t INVALID_PARAM = static_cast(AbilityErrorCode::ERROR_CODE_INVALID_PARAM); +constexpr const char *UI_SERVICE_CONTEXT_CLASS_NAME = + "Lapplication/UIServiceExtensionContext/UIServiceExtensionContext;"; +const char *UI_SERVICE_EXTENSION_CONTEXT_CLEANER_CLASS_NAME = "Lapplication/UIServiceExtensionContext/Cleaner;"; +constexpr const char *SIGNATURE_START_ABILITY = "Lutils/AbilityUtils/AsyncCallbackWrapper;" + "L@ohos/app/ability/Want/Want;L@ohos/app/ability/StartOptions/StartOptions;:V"; +constexpr const char *SIGNATURE_TERMINATE_SELF = "Lutils/AbilityUtils/AsyncCallbackWrapper;:V"; +constexpr const char *SIGNATURE_START_ABILITY_BY_TYPE = "Lstd/core/String;Lescompat/Record;" + "Lapplication/AbilityStartCallback/AbilityStartCallback;Lutils/AbilityUtils/AsyncCallbackWrapper;:V"; +constexpr const char *SIGNATURE_ONCONNECT = "LbundleManager/ElementName/ElementName;L@ohos/rpc/rpc/IRemoteObject;:V"; +constexpr const char *SIGNATURE_ONDISCONNECT = "LbundleManager/ElementName/ElementName;:V"; +constexpr const char *SIGNATURE_CONNECT_SERVICE_EXTENSION = + "L@ohos/app/ability/Want/Want;Lability/connectOptions/ConnectOptions;:J"; +constexpr const char *SIGNATURE_DISCONNECT_SERVICE_EXTENSION = "JLutils/AbilityUtils/AsyncCallbackWrapper;:V"; + +struct ConnectionKey { + AAFwk::Want want; + int64_t id; + int32_t accountId; +}; +struct key_compare { + bool operator()(const ConnectionKey &key1, const ConnectionKey &key2) const + { + if (key1.id < key2.id) { + return true; + } + return false; + } +}; +static std::mutex g_connectsMutex; +static std::map, key_compare> g_connects; +static int64_t g_serialNumber = 0; +} // namespace + +EtsUIServiceExtensionContext* EtsUIServiceExtensionContext::GetEtsUIServiceExtensionContext(ani_env *env, + ani_object obj) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return nullptr; + } + EtsUIServiceExtensionContext *etsContext = nullptr; + ani_status status = ANI_ERROR; + ani_long etsUIServiceContextLong = 0; + if ((status = env->Object_GetFieldByName_Long(obj, "nativeUiExtensionContext", + &etsUIServiceContextLong)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return nullptr; + } + etsContext = reinterpret_cast(etsUIServiceContextLong); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "etsContext null"); + return nullptr; + } + return etsContext; +} + +std::shared_ptr EtsUIServiceExtensionContext::GetContext() +{ + std::shared_ptr context = nullptr; + if (!context_.expired() && (context = context_.lock()) != nullptr) { + return context; + } + return nullptr; +} + +void EtsUIServiceExtensionContext::StartAbility( + ani_env *env, ani_object obj, ani_object callback, ani_object aniWant, ani_object aniStartOption) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "StartAbility"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIServiceExtensionContext(env, obj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnStartAbility(env, callback, aniWant, aniStartOption); +} + +void EtsUIServiceExtensionContext::OnStartAbility( + ani_env *env, ani_object callback, ani_object aniWant, ani_object aniStartOption) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnStartAbility Called"); + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, aniWant, want)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "UnwrapWant failed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Parse param want failed, want must be Want."); + return; + } + ani_status status = ANI_OK; + ani_boolean isUndefined = false; + if ((status = env->Reference_IsUndefined(aniStartOption, &isUndefined)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "Failed to check undefined status : %{public}d", status); + return; + } + AAFwk::StartOptions startOptions; + if (!isUndefined && !AppExecFwk::UnwrapStartOptions(env, aniStartOption, startOptions)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "UnwrapStartOptions filed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Parse param startOptions failed."); + return; + } +#ifdef SUPPORT_SCREEN + InitDisplayId(want, startOptions); +#endif + int32_t innerErrCode = static_cast(ERR_OK); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, static_cast( + AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)), + nullptr); + return; + } + innerErrCode = static_cast(context->StartAbility(want, startOptions)); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "StartAbility innerErrCode: %{public}d", innerErrCode); + if (innerErrCode == static_cast(AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)) { + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, static_cast( + AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)), + nullptr); + } else { + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateErrorByNativeErr(env, static_cast(innerErrCode)), + nullptr); + } +} + +#ifdef SUPPORT_SCREEN +void EtsUIServiceExtensionContext::InitDisplayId(AAFwk::Want &want, AAFwk::StartOptions &startOptions) +{ + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + return; + } + auto window = context->GetWindow(); + if (window == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null window"); + return; + } + TAG_LOGI(AAFwkTag::UISERVC_EXT, "startOption displayId %{public}d", startOptions.GetDisplayID()); + TAG_LOGI(AAFwkTag::UISERVC_EXT, "window displayId %{public}" PRIu64, window->GetDisplayId()); + startOptions.SetDisplayID(window->GetDisplayId()); +} +#endif + +void EtsUIServiceExtensionContext::TerminateSelf(ani_env *env, ani_object obj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "TerminateSelf Called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIServiceExtensionContext(env, obj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnTerminateSelf(env, callback); +} + +void EtsUIServiceExtensionContext::OnTerminateSelf(ani_env *env, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnTerminateSelf Called"); + int32_t innerErrCode = static_cast(ERR_OK); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, ERROR_CODE_ONE, "Context is released"), + nullptr); + return; + } + innerErrCode= context->TerminateSelf(); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "TerminateSelf innerErrCode: %{public}d", innerErrCode); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateErrorByNativeErr(env, static_cast(innerErrCode)), + nullptr); +} + +void EtsUIServiceExtensionContext::StartAbilityByType(ani_env *env, ani_object obj, + ani_string aniType, ani_object aniWantParam, ani_object abilityStartCallback, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "StartAbilityByType Called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIServiceExtensionContext(env, obj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnStartAbilityByType(env, aniType, aniWantParam, abilityStartCallback, callback); +} + +void EtsUIServiceExtensionContext::OnStartAbilityByType(ani_env *env, ani_string aniType, + ani_object aniWantParam, ani_object abilityStartCallback, ani_object aniCallback) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnStartAbilityByType Called"); + std::string type; + if (!OHOS::AppExecFwk::GetStdString(env, aniType, type)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetStdString Failed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Incorrect parameter types, param type must be a string"); + return; + } + + AAFwk::WantParams wantParam; + if (!AppExecFwk::UnwrapWantParams(env, aniWantParam, wantParam)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "parse wantParam failed"); + EtsErrorUtil::ThrowError(env, INVALID_PARAM, "Parameter error. The type of \"WantParams\" must be array"); + return; + } + ani_vm *aniVM = nullptr; + if (env->GetVM(&aniVM) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "get aniVM failed"); + return; + } + std::shared_ptr callback = std::make_shared(aniVM); + callback->SetEtsCallbackObject(abilityStartCallback); + int32_t innerErrCode = static_cast(ERR_OK); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "null context"); + AppExecFwk::AsyncCallback(env, aniCallback, + EtsErrorUtil::CreateError(env, + static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)), + nullptr); + return; + } + innerErrCode = context->StartAbilityByType(type, wantParam, callback); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "StartAbilityByType innerErrCode: %{public}d", innerErrCode); + AppExecFwk::AsyncCallback(env, aniCallback, + EtsErrorUtil::CreateErrorByNativeErr(env, static_cast(innerErrCode)), + nullptr); +} + +ani_long EtsUIServiceExtensionContext::ConnectServiceExtensionAbility(ani_env *env, ani_object obj, + ani_object aniWant, ani_object aniOptions) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Connect ServiceExtensionAbility called."); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return FAILED_CODE; + } + auto etsUiExtensionContext = GetEtsUIServiceExtensionContext(env, obj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsUiExtensionContext"); + return FAILED_CODE; + } + return etsUiExtensionContext->OnConnectServiceExtensionAbility(env, aniWant, aniOptions); +} + +ani_long EtsUIServiceExtensionContext::OnConnectServiceExtensionAbility(ani_env *env, + ani_object aniWant, ani_object aniConnectObj) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnConnectServiceExtensionAbility called."); + // Unwrap want and connection + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, aniWant, want)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "UnwrapWant failed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Parse param want failed, must be a Want."); + return FAILED_CODE; + } + ani_vm *aniVM = nullptr; + if (env->GetVM(&aniVM) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "get aniVM failed"); + return FAILED_CODE; + } + sptr connection = new (std::nothrow)EtsUIServiceExtensionConnection(aniVM); + if (!CheckConnectionParam(env, aniConnectObj, connection, want)) { + EtsErrorUtil::ThrowInvalidParamError(env, "Parse param options failed, must be a ConnectOptions."); + return FAILED_CODE; + } + int64_t connectId = connection->GetConnectionId(); + int32_t innerErrorCode = static_cast(ERR_OK); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + EtsErrorUtil::ThrowError(env, static_cast( + AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)); + return FAILED_CODE; + } + innerErrorCode = context->ConnectServiceExtensionAbility(want, connection); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "ConnectServiceExtensionAbility innerErrorCode: %{public}d", innerErrorCode); + if (innerErrorCode != RESULT_OK) { + connection->CallEtsFailed(static_cast(GetJsErrorCodeByNativeError(innerErrorCode))); + RemoveConnection(connectId); + } + return connectId; +} + +bool EtsUIServiceExtensionContext::CheckConnectionParam(ani_env *env, ani_object connectObj, + sptr& connection, AAFwk::Want& want, int32_t accountId) +{ + if (connection == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null connection"); + return false; + } + connection->SetEtsConnectionObject(connectObj); + ConnectionKey key; + { + std::lock_guard guard(g_connectsMutex); + key.id = g_serialNumber; + key.want = want; + key.accountId = accountId; + connection->SetConnectionId(key.id); + g_connects.emplace(key, connection); + if (g_serialNumber < INT32_MAX) { + g_serialNumber++; + } else { + g_serialNumber = 0; + } + } + TAG_LOGD(AAFwkTag::UISERVC_EXT, "Unable to find connection, make new one"); + return true; +} + +void EtsUIServiceExtensionContext::RemoveConnection(int64_t connectId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "RemoveConnection called"); + std::lock_guard guard(g_connectsMutex); + auto item = std::find_if(g_connects.begin(), g_connects.end(), + [&connectId](const auto &obj) { + return connectId == obj.first.id; + }); + if (item != g_connects.end()) { + TAG_LOGD(AAFwkTag::UISERVC_EXT, "remove conn ability exist."); + if (item->second) { + item->second->RemoveConnectionObject(); + } + g_connects.erase(item); + } else { + TAG_LOGD(AAFwkTag::UISERVC_EXT, "remove conn ability not exist."); + } +} + +void EtsUIServiceExtensionContext::DisConnectServiceExtensionAbility(ani_env *env, ani_object obj, + ani_long aniConnectionId, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "DisConnect ServiceExtensionAbility start"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null env"); + return; + } + auto etsUiExtensionContext = GetEtsUIServiceExtensionContext(env, obj); + if (etsUiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsUiExtensionContext"); + return; + } + etsUiExtensionContext->OnDisConnectServiceExtensionAbility(env, aniConnectionId, callback); +} + +void EtsUIServiceExtensionContext::OnDisConnectServiceExtensionAbility(ani_env *env, + ani_long aniConnectionId, ani_object callback) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "DisConnectServiceExtensionAbility start"); + int64_t connectionId = static_cast(aniConnectionId); + AAFwk::Want want; + sptr connection = nullptr; + int32_t accountId = -1; + FindConnection(want, connection, connectionId, accountId); + // begin disconnect + int32_t innerErrCode = static_cast(ERR_OK); + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, ERROR_CODE_ONE, "Context is released"), nullptr); + return; + } + if (connection == nullptr) { + TAG_LOGW(AAFwkTag::UISERVC_EXT, "null connection"); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateError(env, ERROR_CODE_TWO, "not found connection"), nullptr); + return; + } + innerErrCode = context->DisConnectServiceExtensionAbility(want, connection, accountId); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "DisConnectServiceExtensionAbility innerErrorCode: %{public}d", innerErrCode); + AppExecFwk::AsyncCallback(env, callback, + EtsErrorUtil::CreateErrorByNativeErr(env, static_cast(innerErrCode)), nullptr); +} + +void EtsUIServiceExtensionContext::FindConnection( + AAFwk::Want &want, sptr &connection, int64_t &connectId, int32_t &accountId) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "connection:%{public}d", static_cast(connectId)); + std::lock_guard guard(g_connectsMutex); + auto item = std::find_if(g_connects.begin(), + g_connects.end(), + [&connectId](const auto &obj) { + return connectId == obj.first.id; + }); + if (item != g_connects.end()) { + // match id + want = item->first.want; + connection = item->second; + accountId = item->first.accountId; + TAG_LOGD(AAFwkTag::UISERVC_EXT, "find conn ability exist"); + } +} + +ani_object CreateEtsUIServiceExtensionContext(ani_env *env, std::shared_ptr context) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "CreateEtsUIServiceExtensionContext called"); + std::shared_ptr abilityInfo = nullptr; + if (context == nullptr || env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null context or env nullptr"); + return nullptr; + } + abilityInfo = context->GetAbilityInfo(); + ani_class cls = nullptr; + ani_status status = ANI_ERROR; + ani_method method = nullptr; + ani_object contextObj = nullptr; + if ((env->FindClass(UI_SERVICE_CONTEXT_CLASS_NAME, &cls)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return nullptr; + } + if ((status = env->Class_FindMethod(cls, "", "J:V", &method)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + return nullptr; + } + std::unique_ptr etsContext = std::make_unique(context); + if ((status = env->Object_New(cls, method, &contextObj, reinterpret_cast(etsContext.release()))) != + ANI_OK) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return nullptr; + } + EtsUIServiceExtensionContext::BindNativeMethods(env); + auto workContext = new (std::nothrow)std::weak_ptr(context); + if (workContext == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null workContext"); + return nullptr; + } + if (!ContextUtil::SetNativeContextLong(env, contextObj, (ani_long)workContext)) { + TAG_LOGE(AAFwkTag::UI_EXT, "SetNativeContextLong failed"); + delete workContext; + return nullptr; + } + if (!EtsUIServiceExtensionContext::BindNativePtrCleaner(env)) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status: %{public}d", status); + delete workContext; + return nullptr; + } + AbilityRuntime::ContextUtil::CreateEtsBaseContext(env, cls, contextObj, context); + AbilityRuntime::CreateEtsExtensionContext(env, cls, contextObj, context, context->GetAbilityInfo()); + return contextObj; +} + +bool EtsUIServiceExtensionContext::BindNativePtrCleaner(ani_env *env) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "nullptr env"); + return false; + } + ani_class cleanerCls; + ani_status status = env->FindClass(UI_SERVICE_EXTENSION_CONTEXT_CLEANER_CLASS_NAME, &cleanerCls); + if (ANI_OK != status) { + TAG_LOGE(AAFwkTag::UI_EXT, "Not found Cleaner. status:%{public}d.", status); + return false; + } + std::array methods = { + ani_native_function { "clean", nullptr, reinterpret_cast(EtsUIServiceExtensionContext::Clean) }, + }; + if ((status = env->Class_BindNativeMethods(cleanerCls, methods.data(), methods.size())) != ANI_OK + && status != ANI_ALREADY_BINDED) { + TAG_LOGE(AAFwkTag::UI_EXT, "status: %{public}d", status); + return false; + } + return true; +} + +void EtsUIServiceExtensionContext::Clean(ani_env *env, ani_object object) +{ + ani_long ptr = 0; + if (ANI_OK != env->Object_GetFieldByName_Long(object, "nativeServiceExtensionContext", &ptr)) { + return; + } + + if (ptr != 0) { + delete reinterpret_cast(ptr); + ptr = 0; + } +} + +bool EtsUIServiceExtensionContext::BindNativeMethods(ani_env *env) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "env nullptr"); + return false; + } + std::array functions = { + ani_native_function{"nativeStartAbility", SIGNATURE_START_ABILITY, + reinterpret_cast(EtsUIServiceExtensionContext::StartAbility)}, + ani_native_function{"nativeTerminateSelf", SIGNATURE_TERMINATE_SELF, + reinterpret_cast(EtsUIServiceExtensionContext::TerminateSelf)}, + ani_native_function{ + "nativeStartAbilityByType", SIGNATURE_START_ABILITY_BY_TYPE, + reinterpret_cast(EtsUIServiceExtensionContext::StartAbilityByType)}, + ani_native_function{"nativeConnectServiceExtensionAbility", SIGNATURE_CONNECT_SERVICE_EXTENSION, + reinterpret_cast(EtsUIServiceExtensionContext::ConnectServiceExtensionAbility)}, + ani_native_function{"nativeDisconnectServiceExtensionAbility", SIGNATURE_DISCONNECT_SERVICE_EXTENSION, + reinterpret_cast(EtsUIServiceExtensionContext::DisConnectServiceExtensionAbility)}, + }; + ani_class cls {}; + ani_status status = env->FindClass(UI_SERVICE_CONTEXT_CLASS_NAME, &cls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "FindClass failed status: %{public}d", status); + return false; + } + if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "Class_BindNativeMethods status: %{public}d", status); + return false; + } + return true; +} + +EtsUIServiceExtensionConnection::EtsUIServiceExtensionConnection(ani_vm *etsVm) : etsVm_(etsVm) {} + +EtsUIServiceExtensionConnection::~EtsUIServiceExtensionConnection() +{ + if (etsConnectionObject_ == nullptr) { + return; + } + ReleaseObjectReference(etsConnectionObject_); +} + +void EtsUIServiceExtensionConnection::SetConnectionId(int64_t id) +{ + connectionId_ = id; +} + +int64_t EtsUIServiceExtensionConnection::GetConnectionId() +{ + return connectionId_; +} + +void EtsUIServiceExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element, + const sptr &remoteObject, int resultCode) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnAbilityConnectDone, resultCode:%{public}d", resultCode); + HandleOnAbilityConnectDone(element, remoteObject, resultCode); +} + +void EtsUIServiceExtensionConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element, + const sptr &remoteObject, int resultCode) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "resultCode:%{public}d", resultCode); + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "etsVm nullptr"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetEnv status:%{public}d", status); + return; + } + ani_ref refElement = AppExecFwk::WrapElementName(env, element); + if (refElement == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null refElement"); + return; + } + if (remoteObject == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null remoteObject"); + return; + } + ani_object refRemoteObject = AniRemote::CreateAniRemoteObject(env, remoteObject); + if (refRemoteObject == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null refRemoteObject"); + return; + } + if ((status = env->Object_CallMethodByName_Void(reinterpret_cast(etsConnectionObject_), "onConnect", + SIGNATURE_ONCONNECT, refElement, refRemoteObject)) != ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "Failed to call onConnect, status: %{public}d", status); + } +} + +void EtsUIServiceExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone, resultCode:%{public}d", resultCode); + HandleOnAbilityDisconnectDone(element, resultCode); +} + +void EtsUIServiceExtensionConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, + int resultCode) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "HandleOnAbilityDisconnectDone, resultCode:%{public}d", resultCode); + if (etsVm_ == nullptr || etsConnectionObject_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsConnectionObject_ or etsVm"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetEnv status:%{public}d", status); + return; + } + ani_ref refElement = AppExecFwk::WrapElementName(env, element); + if (refElement == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null refElement"); + return; + } + { + std::lock_guard guard(g_connectsMutex); + TAG_LOGD(AAFwkTag::UISERVC_EXT, "OnAbilityDisconnectDone g_connects.size:%{public}zu", g_connects.size()); + std::string bundleName = element.GetBundleName(); + std::string abilityName = element.GetAbilityName(); + auto item = std::find_if(g_connects.begin(), g_connects.end(), + [bundleName, abilityName, connectionId = connectionId_]( + const auto &obj) { + return (bundleName == obj.first.want.GetBundle()) && + (abilityName == obj.first.want.GetElement().GetAbilityName()) && + connectionId == obj.first.id; + }); + if (item != g_connects.end()) { + g_connects.erase(item); + TAG_LOGD(AAFwkTag::UISERVC_EXT, + "OnAbilityDisconnectDone erase g_connects.size:%{public}zu", g_connects.size()); + } + } + if ((status = env->Object_CallMethodByName_Void(reinterpret_cast(etsConnectionObject_), "onDisconnect", + SIGNATURE_ONDISCONNECT, refElement)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "Failed to call onDisconnect, status: %{public}d", status); + } +} + +void EtsUIServiceExtensionConnection::SetEtsConnectionObject(ani_object connObject) +{ + if (connObject == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "etsConnectionObject null"); + ReleaseObjectReference(etsConnectionObject_); + etsConnectionObject_ = nullptr; + return; + } + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "etsVm_ null"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetEnv status:%{public}d", status); + return; + } + ani_ref global = nullptr; + if ((status = env->GlobalReference_Create(connObject, &global)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "status : %{public}d", status); + return; + } + etsConnectionObject_ = global; +} + +void EtsUIServiceExtensionConnection::ReleaseObjectReference(ani_ref etsObjRef) +{ + if (etsVm_ == nullptr || etsObjRef == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "etsVm_ or etsObjRef null"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_ERROR; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GetEnv status:%{public}d", status); + return; + } + if ((status = env->GlobalReference_Delete(etsObjRef)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "GlobalReference_Delete status: %{public}d", status); + } +} + +void EtsUIServiceExtensionConnection::RemoveConnectionObject() +{ + ReleaseObjectReference(etsConnectionObject_); + etsConnectionObject_ = nullptr; +} + +void EtsUIServiceExtensionConnection::CallEtsFailed(int32_t errorCode) +{ + TAG_LOGD(AAFwkTag::UISERVC_EXT, "CallEtsFailed"); + if (etsVm_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsVm"); + return; + } + if (etsConnectionObject_ == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "null etsConnectionObject_"); + return; + } + ani_env *env = nullptr; + ani_status status = ANI_OK; + if ((status = etsVm_->GetEnv(ANI_VERSION_1, &env)) != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "Failed to get env, status: %{public}d", status); + return; + } + if ((status = env->Object_CallMethodByName_Void(reinterpret_cast(etsConnectionObject_), + "onFailed", "I:V", static_cast(errorCode))) != ANI_OK) { + TAG_LOGE(AAFwkTag::UISERVC_EXT, "Failed to call onFailed, status: %{public}d", status); + } +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_instance.cpp b/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_instance.cpp new file mode 100644 index 00000000000..dddfa009576 --- /dev/null +++ b/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_instance.cpp @@ -0,0 +1,54 @@ +/* + * 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_service_extension_instance.h" + +#include +#include + +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "string_wrapper.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +const char *ETS_ANI_LIBNAME = "libui_service_extension_ani.z.so"; +const char *ETS_ANI_Create_FUNC = "OHOS_ETS_UI_SERVICE_Extension_Create"; +using CreateETSUIServiceExtensionFunc = UIServiceExtension*(*)(const std::unique_ptr&); +CreateETSUIServiceExtensionFunc g_etsCreateFunc = nullptr; +} + +UIServiceExtension *CreateETSUIServiceExtension(const std::unique_ptr &runtime) +{ + if (g_etsCreateFunc != nullptr) { + return g_etsCreateFunc(runtime); + } + auto handle = dlopen(ETS_ANI_LIBNAME, RTLD_LAZY); + if (handle == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "dlopen failed %{public}s, %{public}s", ETS_ANI_LIBNAME, dlerror()); + return nullptr; + } + auto symbol = dlsym(handle, ETS_ANI_Create_FUNC); + if (symbol == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "dlsym failed %{public}s, %{public}s", ETS_ANI_Create_FUNC, dlerror()); + dlclose(handle); + return nullptr; + } + g_etsCreateFunc = reinterpret_cast(symbol); + return g_etsCreateFunc(runtime); +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ets/@ohos.app.ability.UIServiceExtensionAbility.ets b/frameworks/ets/ets/@ohos.app.ability.UIServiceExtensionAbility.ets new file mode 100644 index 00000000000..40c6dce674b --- /dev/null +++ b/frameworks/ets/ets/@ohos.app.ability.UIServiceExtensionAbility.ets @@ -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. + */ + +import ExtensionAbility from '@ohos.app.ability.ExtensionAbility'; +import type Want from '@ohos.app.ability.Want'; +import type UIServiceExtensionContext from 'application.UIServiceExtensionContext'; +import type UIServiceHostProxy from 'application.UIServiceHostProxy'; +import window from '@ohos.window'; + +export default class UIServiceExtensionAbility extends ExtensionAbility { + + context: UIServiceExtensionContext = {}; + + onCreate(want: Want): void { + + } + + onRequest(want: Want, startId: number): void { + + } + + onConnect(want: Want, proxy: UIServiceHostProxy): void { + + } + + onDisconnect(want: Want, proxy: UIServiceHostProxy): void { + + } + + onWindowWillCreate(config: window.ExtensionWindowConfig): void { + + } + + onWindowDidCreate(window: window.Window): void { + + } + + onData(proxy: UIServiceHostProxy, data: Record): void { + + } + + onDestroy(): void { + + } +} \ No newline at end of file diff --git a/frameworks/ets/ets/BUILD.gn b/frameworks/ets/ets/BUILD.gn index 29f357b193c..d501043930e 100644 --- a/frameworks/ets/ets/BUILD.gn +++ b/frameworks/ets/ets/BUILD.gn @@ -1279,6 +1279,86 @@ ohos_prebuilt_etc("ability_runtime_running_multi_instance_info_abc_etc") { deps = [ ":ability_runtime_running_multi_instance_info_abc" ] } +generate_static_abc("ability_runtime_ui_service_extension_ability_abc") { + base_url = "./" + files = [ "./@ohos.app.ability.UIServiceExtensionAbility.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/ability_runtime_ui_service_extension_ability_abc.abc" +} + +ohos_prebuilt_etc("ability_runtime_ui_service_extension_ability_abc_etc") { + source = "$target_out_dir/ability_runtime_ui_service_extension_ability_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ability_runtime_ui_service_extension_ability_abc" ] +} + +generate_static_abc("ui_service_extension_context_abc") { + base_url = "./" + files = [ "./application/UIServiceExtensionContext.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/ui_service_extension_context_abc.abc" +} + +ohos_prebuilt_etc("ui_service_extension_context_abc_etc") { + source = "$target_out_dir/ui_service_extension_context_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ui_service_extension_context_abc" ] +} + +generate_static_abc("ui_service_host_proxy_abc") { + base_url = "./" + files = [ "./application/UIServiceHostProxy.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/ui_service_host_proxy_abc.abc" +} + +ohos_prebuilt_etc("ui_service_host_proxy_abc_etc") { + source = "$target_out_dir/ui_service_host_proxy_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ui_service_host_proxy_abc" ] +} + +generate_static_abc("ui_service_proxy_abc") { + base_url = "./" + files = [ "./application/UIServiceProxy.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/ui_service_proxy_abc.abc" +} + +ohos_prebuilt_etc("ui_service_proxy_abc_etc") { + source = "$target_out_dir/ui_service_proxy_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ui_service_proxy_abc" ] +} + +generate_static_abc("ui_service_extension_connect_callback_abc") { + base_url = "./" + files = [ "./application/UIServiceExtensionConnectCallback.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/ui_service_extension_connect_callback_abc.abc" +} + +ohos_prebuilt_etc("ui_service_extension_connect_callback_abc_etc") { + source = "$target_out_dir/ui_service_extension_connect_callback_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ui_service_extension_connect_callback_abc" ] +} + group("ets_packages") { deps = [ ":ability_application_abc_etc", @@ -1339,6 +1419,7 @@ group("ets_packages") { ":ability_runtime_trigger_info_abc_etc", ":ability_runtime_ui_ability_abc_etc", ":ability_runtime_ui_ability_context_abc_etc", + ":ability_runtime_ui_service_extension_ability_abc_etc", ":ability_runtime_multi_app_mode_abc_etc", ":ability_runtime_process_data_abc_etc", ":ability_runtime_process_information_abc_etc", @@ -1355,6 +1436,10 @@ group("ets_packages") { ":form_extension_ability_etc", ":service_extension_ability_abc_etc", ":ui_extension_ability_ani_etc", + ":ui_service_extension_context_abc_etc", + ":ui_service_proxy_abc_etc", + ":ui_service_host_proxy_abc_etc", + ":ui_service_extension_connect_callback_abc_etc", ":uri_permission_manager_abc_etc", ":form_extension_ability_etc", ":appRecovery_abc_etc", diff --git a/frameworks/ets/ets/application/ServiceExtensionContext.ets b/frameworks/ets/ets/application/ServiceExtensionContext.ets index 2bb11c2f1f5..828ae45bca9 100644 --- a/frameworks/ets/ets/application/ServiceExtensionContext.ets +++ b/frameworks/ets/ets/application/ServiceExtensionContext.ets @@ -72,6 +72,8 @@ export default class ServiceExtensionContext extends ExtensionContext { native nativeDisconnectServiceExtensionAbility(connection: long, callback: AsyncCallbackWrapper): void; + native nativeStartUIServiceExtensionAbility(want: Want, callback: AsyncCallbackWrapper): void; + terminateSelf(callback: AsyncCallback): void { let myCall = new AsyncCallbackWrapper(callback); taskpool.execute((): void => { @@ -191,4 +193,19 @@ export default class ServiceExtensionContext extends ExtensionContext { }) }); } + + startUIServiceExtensionAbility(want: Want): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartUIServiceExtensionAbility(want, myCall); + }); + }); + } } diff --git a/frameworks/ets/ets/application/UIAbilityContext.ets b/frameworks/ets/ets/application/UIAbilityContext.ets index 87d95d25f5b..078c53c4324 100644 --- a/frameworks/ets/ets/application/UIAbilityContext.ets +++ b/frameworks/ets/ets/application/UIAbilityContext.ets @@ -29,6 +29,8 @@ import { HapModuleInfo } from 'bundleManager.HapModuleInfo'; import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'; import AbilityStartCallback from 'application.AbilityStartCallback'; import AtomicServiceOptions from '@ohos.app.ability.AtomicServiceOptions'; +import UIServiceProxy from 'application.UIServiceProxy'; +import UIServiceExtensionConnectCallback from 'application.UIServiceExtensionConnectCallback'; import image from '@ohos.multimedia.image'; import { LocalStorage } from '@ohos.arkui.stateManagement'; @@ -142,7 +144,7 @@ export default class UIAbilityContext extends Context { private native nativeOpenAtomicService(appId: string, callback: AsyncCallbackWrapper, options?: AtomicServiceOptions): void; private native nativeOnSetRestoreEnabled(enabled: boolean): void; - + private native nativeConnectServiceExtensionAbilityWithAccount(want: Want, accountId: int, options: ConnectOptions): long; @@ -189,6 +191,10 @@ export default class UIAbilityContext extends Context { }); } + private native nativeConnectUIServiceExtensionAbility(want: Want, uiServiceExtConCallback: UIServiceExtensionConnectCallback, callback:AsyncCallbackWrapper): void; + private native nativeStartUIServiceExtensionAbility(want: Want, callback:AsyncCallbackWrapper): void; + private native nativeDisconnectUIServiceExtensionAbility(proxy: UIServiceProxy, callback:AsyncCallbackWrapper): void; + startAbility(want: Want, options: StartOptions, callback: AsyncCallback): void { let myCall = new AsyncCallbackWrapper(callback); taskpool.execute((): void => { @@ -471,7 +477,7 @@ export default class UIAbilityContext extends Context { callback(null, undefined); } else { callback(retError, undefined); - } + } }, (err: Error): void => { callback(err as BusinessError, undefined); }); @@ -825,4 +831,50 @@ hideAbility(): Promise { restoreWindowStage(localStorage: LocalStorage): void { this.nativeRestoreWindowStage(localStorage); } + + connectUIServiceExtensionAbility(want: Want, callback: UIServiceExtensionConnectCallback) : Promise { + return new Promise((resolve: (data: UIServiceProxy) => void, + reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null, data: UIServiceProxy | undefined) => { + if (err == null || err.code == 0) { + resolve(data as UIServiceProxy); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeConnectUIServiceExtensionAbility(want, callback, myCall); + }); + }); + } + + startUIServiceExtensionAbility(want: Want): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartUIServiceExtensionAbility(want, myCall); + }); + }); + } + + disconnectUIServiceExtensionAbility(proxy: UIServiceProxy): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeDisconnectUIServiceExtensionAbility(proxy, myCall); + }); + }); + } } diff --git a/frameworks/ets/ets/application/UIExtensionContext.ets b/frameworks/ets/ets/application/UIExtensionContext.ets index 6f2d70e1c4e..3ddceebc3c4 100644 --- a/frameworks/ets/ets/application/UIExtensionContext.ets +++ b/frameworks/ets/ets/application/UIExtensionContext.ets @@ -24,6 +24,8 @@ import OpenLinkOptions from '@ohos.app.ability.OpenLinkOptions'; import StartOptions from '@ohos.app.ability.StartOptions'; import Want from '@ohos.app.ability.Want'; import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'; +import UIServiceProxy from 'application.UIServiceProxy'; +import UIServiceExtensionConnectCallback from 'application.UIServiceExtensionConnectCallback'; class Cleaner { static callback(cleaner: Cleaner): void { @@ -75,6 +77,9 @@ export default class UIExtensionContext extends ExtensionContext { options?: OpenLinkOptions, callback?: AsyncCallbackWrapper): void; private native nativeOpenAtomicService(appId: string, syncCallback: AsyncCallbackWrapper, options?: AtomicServiceOptions): void; + private native nativeConnectUIServiceExtensionAbility(want: Want, uiServiceExtConCallback: UIServiceExtensionConnectCallback, callback:AsyncCallbackWrapper): void; + private native nativeStartUIServiceExtensionAbility(want: Want, callback:AsyncCallbackWrapper): void; + private native nativeDisconnectUIServiceExtensionAbility(proxy: UIServiceProxy, callback:AsyncCallbackWrapper): void; terminateSelf(callback:AsyncCallback): void { let myCall = new AsyncCallbackWrapper(callback); @@ -328,4 +333,50 @@ export default class UIExtensionContext extends ExtensionContext { }); }); } + + connectUIServiceExtensionAbility(want: Want, callback: UIServiceExtensionConnectCallback) : Promise { + return new Promise((resolve: (data: UIServiceProxy) => void, + reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null, data: UIServiceProxy | undefined) => { + if (err == null || err.code == 0) { + resolve(data as UIServiceProxy); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeConnectUIServiceExtensionAbility(want, callback, myCall); + }); + }); + } + + startUIServiceExtensionAbility(want: Want): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartUIServiceExtensionAbility(want, myCall); + }); + }); + } + + disconnectUIServiceExtensionAbility(proxy: UIServiceProxy): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeDisconnectUIServiceExtensionAbility(proxy, myCall); + }); + }); + } } \ No newline at end of file diff --git a/frameworks/ets/ets/application/UIServiceExtensionConnectCallback.ets b/frameworks/ets/ets/application/UIServiceExtensionConnectCallback.ets new file mode 100644 index 00000000000..ba3adf583d5 --- /dev/null +++ b/frameworks/ets/ets/application/UIServiceExtensionConnectCallback.ets @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 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. + */ + +export default interface UIServiceExtensionConnectCallback { + onData(data: Record): void; + onDisconnect(): void; +} + +export class UIServiceExtensionConnectCallbackImpl implements UIServiceExtensionConnectCallback { + onData(data: Record): void {} + onDisconnect(): void {} +} \ No newline at end of file diff --git a/frameworks/ets/ets/application/UIServiceExtensionContext.ets b/frameworks/ets/ets/application/UIServiceExtensionContext.ets new file mode 100644 index 00000000000..5e54c7c5e40 --- /dev/null +++ b/frameworks/ets/ets/application/UIServiceExtensionContext.ets @@ -0,0 +1,133 @@ +/* + * 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 ExtensionContext from 'application.ExtensionContext'; +import type Want from '@ohos.app.ability.Want'; +import type StartOptions from '@ohos.app.ability.StartOptions'; +import type AbilityStartCallback from 'application.AbilityStartCallback'; +import { ConnectOptions } from 'ability.connectOptions'; +import AsyncCallbackWrapper from '../utils/AbilityUtils'; +import { AsyncCallback } from '@ohos.base'; +import { BusinessError } from '@ohos.base'; + +class Cleaner { + static callback(cleaner: Cleaner): void { + cleaner.clean() + } + constructor(targetPtr: long) { + this.targetPtr = targetPtr + } + native clean(): void + private targetPtr: long = 0 +} +let destroyRegister = new FinalizationRegistry(Cleaner.callback) +let unregisterToken = new object() + +export default class UIServiceExtensionContext extends ExtensionContext +{ + nativeUiExtensionContext:long = 0; + private cleaner: Cleaner | null = null; + registerCleaner(ptr: long): void { + this.cleaner = new Cleaner(ptr) + destroyRegister.register(this, this.cleaner!, unregisterToken); + } + unregisterCleaner(): void { + destroyRegister.unregister(unregisterToken); + } + constructor(context:long) { + if(this.nativeUiExtensionContext == 0){ + this.nativeUiExtensionContext = context; + } + this.registerCleaner(this.nativeUiExtensionContext) + } + native nativeStartAbility(callback: AsyncCallbackWrapper, want: Want, options?: StartOptions): void; + native nativeTerminateSelf(callback: AsyncCallbackWrapper): void; + native nativeStartAbilityByType(type: string, wantParam: Record, + abilityStartCallback: AbilityStartCallback, callback: AsyncCallbackWrapper): void; + native nativeConnectServiceExtensionAbility(want: Want, options: ConnectOptions): long; + native nativeDisconnectServiceExtensionAbility(connectionId: long, callback: AsyncCallbackWrapper): void; + + startAbility(want: Want, options?: StartOptions): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartAbility(myCall, want, options); + }); + }); + return p; + } + + terminateSelf(): Promise { + let p = new Promise((resolve: (data: undefined) => void, reject:(err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeTerminateSelf(myCall); + }); + }); + return p; + } + + startAbilityByType(type: string, wantParam: Record, abilityStartCallback: AbilityStartCallback): Promise + { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartAbilityByType(type, wantParam, abilityStartCallback, myCall); + }); + }); + return p; + } + + connectServiceExtensionAbility(want: Want, options: ConnectOptions): long { + return this.nativeConnectServiceExtensionAbility(want, options); + } + + disconnectServiceExtensionAbility(connectionId: long): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeDisconnectServiceExtensionAbility(connectionId, myCall); + }); + }); + return p; + } +} \ No newline at end of file diff --git a/frameworks/ets/ets/application/UIServiceHostProxy.ets b/frameworks/ets/ets/application/UIServiceHostProxy.ets new file mode 100644 index 00000000000..2bcff65eb2d --- /dev/null +++ b/frameworks/ets/ets/application/UIServiceHostProxy.ets @@ -0,0 +1,25 @@ +/* + * 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. + */ +export default interface UIServiceHostProxy { + sendData(data: Record): void; +} + +export class UIServiceHostProxyImpl implements UIServiceHostProxy { + nativeServiceHostProxy:long = 0; + native nativeSendData(data: Record): void; + sendData(data: Record): void { + this.nativeSendData(data); + } +} \ No newline at end of file diff --git a/frameworks/ets/ets/application/UIServiceProxy.ets b/frameworks/ets/ets/application/UIServiceProxy.ets new file mode 100644 index 00000000000..8de5f471958 --- /dev/null +++ b/frameworks/ets/ets/application/UIServiceProxy.ets @@ -0,0 +1,25 @@ +/* + * 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. + */ +export default interface UIServiceProxy { + sendData(data: Record): void; +} + +export class UIServiceProxyImpl implements UIServiceProxy { + native nativeSendData(data: Record): void; + nativeServiceProxy:long = 0; + sendData(data: Record): void { + this.nativeSendData(data); + } +} \ No newline at end of file diff --git a/frameworks/native/ability/native/BUILD.gn b/frameworks/native/ability/native/BUILD.gn index f8410ce6f89..3825b1c0978 100644 --- a/frameworks/native/ability/native/BUILD.gn +++ b/frameworks/native/ability/native/BUILD.gn @@ -873,10 +873,12 @@ ohos_shared_library("ui_ability_ani") { "${ability_runtime_path}/frameworks/ets/ani/enum_convert", "${ability_runtime_path}/frameworks/ets/ani/ui_ability/include", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_callback/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/connection", "${ability_runtime_path}/interfaces/kits/native/ability/native", "${ability_runtime_path}/interfaces/kits/native/ability/native/ability_runtime", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", "${ability_runtime_path}/interfaces/kits/native/appkit/ability_delegator", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection", "${ability_runtime_path}/utils/global/time/include", "${ability_runtime_services_path}/common/include", ] @@ -884,6 +886,8 @@ ohos_shared_library("ui_ability_ani") { sources = [ "${ability_runtime_path}/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp", "${ability_runtime_path}/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_ability/src/ets_ui_ability_servicehost_stub_impl.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_ability/src/ets_uiservice_ability_connection.cpp", ] public_configs = [ ":uiability_config" ] @@ -894,6 +898,8 @@ ohos_shared_library("ui_ability_ani") { ":continuation_ipc", ":extensionkit_native", ":uiabilitykit_native", + ":ui_service_extension_connection", + ":ui_service_extension_connection_ani", "${ability_runtime_innerkits_path}/ability_manager:ability_connect_callback_stub", "${ability_runtime_innerkits_path}/ability_manager:ability_manager", "${ability_runtime_innerkits_path}/ability_manager:ability_start_options", @@ -1577,6 +1583,62 @@ ohos_shared_library("ui_service_extension_connection") { part_name = "ability_runtime" } +ohos_shared_library("ui_service_extension_connection_ani") { + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + branch_protector_ret = "pac_ret" + + defines = [ "AMS_LOG_TAG = \"Ability\"" ] + defines += [ "AMS_LOG_DOMAIN = 0xD001300" ] + include_dirs = [ + "${ability_runtime_path}/frameworks/ets/ani/ani_common/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/connection", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection", + ] + + sources = [ + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_host_proxy.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/connection/ets_ui_service_proxy.cpp", + ] + + deps = [ + ":ui_service_extension_connection", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + "${ability_runtime_native_path}/ability/native:ability_business_error", + ] + + external_deps = [ + "ability_base:want", + "access_token:libtokenid_sdk", + "runtime_core:ani", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "ets_runtime:libark_jsruntime", + "eventhandler:libeventhandler", + "hilog:libhilog", + "hitrace:hitrace_meter", + "ipc:ipc_core", + "ipc:ipc_napi", + "ipc:rpc_ani", + "napi:ace_napi", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + ] + + subsystem_name = "ability" + part_name = "ability_runtime" +} + ohos_shared_library("ui_service_extension") { sanitize = { integer_overflow = true @@ -1600,6 +1662,7 @@ ohos_shared_library("ui_service_extension") { "${ability_runtime_path}/utils/global/freeze/include", "${ability_runtime_path}/utils/global/time/include", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/include", ] sources = [ @@ -1607,6 +1670,7 @@ ohos_shared_library("ui_service_extension") { "${ability_runtime_native_path}/ability/native/ui_service_extension_ability/js_ui_service_extension_context.cpp", "${ability_runtime_native_path}/ability/native/ui_service_extension_ability/ui_service_extension.cpp", "${ability_runtime_path}/frameworks/native/appkit/ability_runtime/ui_service_extension_context.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_instance.cpp", ] deps = [ @@ -1670,6 +1734,108 @@ ohos_shared_library("ui_service_extension") { part_name = "ability_runtime" } +ohos_shared_library("ui_service_extension_ani") { + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + branch_protector_ret = "pac_ret" + + defines = [ "AMS_LOG_TAG = \"Ability\"" ] + defines += [ "AMS_LOG_DOMAIN = 0xD001300" ] + include_dirs = [ + "${ability_runtime_path}/frameworks/ets/ani/ani_common/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_extension_callback/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/connection", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime", + "${ability_runtime_path}/interfaces/kits/native/ability/native", + "${ability_runtime_path}/services/common/include", + "${ability_runtime_innerkits_path}/ability_manager/include", + "${ability_runtime_path}/utils/global/freeze/include", + "${ability_runtime_path}/utils/global/time/include", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection", + ] + + sources = [ + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension_context.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/src/ets_ui_service_extension.cpp", + ] + + deps = [ + ":abilitykit_native", + ":extensionkit_native", + ":ui_service_extension", + ":ui_service_extension_connection", + ":ui_service_extension_connection_ani", + "${ability_runtime_innerkits_path}/ability_manager:ability_connect_callback_stub", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/ability_manager:ability_start_options", + "${ability_runtime_innerkits_path}/ability_manager:process_options", + "${ability_runtime_innerkits_path}/app_manager:app_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + "${ability_runtime_path}/frameworks/ets/ani/ui_extension_callback:ani_ui_extension_callback", + "${ability_runtime_native_path}/ability:ability_context_native", + "${ability_runtime_native_path}/ability/native:ability_business_error", + "${ability_runtime_native_path}/ability/native:insight_intent_executor", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/appkit:app_context_utils", + "${ability_runtime_native_path}/appkit:appkit_delegator", + "${ability_runtime_native_path}/insight_intent/insight_intent_context:insightintentcontext", + "${ability_runtime_path}/utils/global/freeze:freeze_util", + ] + + external_deps = [ + "ability_base:base", + "ability_base:configuration", + "ability_base:want", + "ability_base:zuri", + "access_token:libtokenid_sdk", + "ace_engine:ace_uicontent", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "ets_runtime:libark_jsruntime", + "eventhandler:libeventhandler", + "hilog:libhilog", + "hitrace:hitrace_meter", + "ipc:ipc_core", + "ipc:ipc_napi", + "ipc:rpc_ani", + "napi:ace_napi", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + "window_manager:windowstageani_kit", + ] + + public_external_deps = [] + if (ability_runtime_graphics) { + external_deps += [ + "ace_engine:ace_uicontent", + "window_manager:libdm", + "window_manager:libwm", + "window_manager:libwsutils", + "window_manager:scene_session", + "window_manager:session_manager_lite", + "window_manager:window_native_kit", + "window_manager:windowstage_kit", + ] + + public_external_deps += [ "window_manager:window_native_kit" ] + } + + subsystem_name = "ability" + part_name = "ability_runtime" +} + ohos_shared_library("ui_service_extension_module") { sanitize = { integer_overflow = true @@ -2108,18 +2274,22 @@ ohos_shared_library("ui_extension_ani") { "${ability_runtime_path}/frameworks/ets/ani/ani_common/include", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_callback/include", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/include", + "${ability_runtime_path}/frameworks/ets/ani/ui_service_extension_ability/connection", "${ability_runtime_path}/frameworks/ets/ani/enum_convert", "${ability_runtime_path}/interfaces/inner_api/insight_intent/insight_intent_context", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection", ] sources = [ - "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_extension_common.cpp", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension.cpp", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_content_session.cpp", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_context.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_servicehost_stub_impl.cpp", + "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_uiservice_uiext_connection.cpp", ] public_configs = [] @@ -2128,7 +2298,9 @@ ohos_shared_library("ui_extension_ani") { ":abilitykit_native", ":extensionkit_native", ":ui_extension", + ":ui_service_extension_ani", ":ui_service_extension_connection", + ":ui_service_extension_connection_ani", "${ability_runtime_innerkits_path}/ability_manager:ability_manager", "${ability_runtime_innerkits_path}/ability_manager:ability_start_options", "${ability_runtime_innerkits_path}/runtime:runtime", diff --git a/frameworks/native/ability/native/ui_service_extension_ability/ui_service_extension.cpp b/frameworks/native/ability/native/ui_service_extension_ability/ui_service_extension.cpp index dd4a690ff18..ee3aaed17a3 100644 --- a/frameworks/native/ability/native/ui_service_extension_ability/ui_service_extension.cpp +++ b/frameworks/native/ability/native/ui_service_extension_ability/ui_service_extension.cpp @@ -21,6 +21,7 @@ #include "hilog_tag_wrapper.h" #include "hitrace_meter.h" #include "ability_delegator_registry.h" +#include "ets_ui_service_extension_instance.h" #include "napi_common_util.h" #include "runtime.h" #include "js_runtime_utils.h" @@ -44,6 +45,8 @@ UIServiceExtension* UIServiceExtension::Create(const std::unique_ptr& r switch (runtime->GetLanguage()) { case Runtime::Language::JS: return JsUIServiceExtension::Create(runtime); + case Runtime::Language::ETS: + return CreateETSUIServiceExtension(runtime); default: return new (std::nothrow) UIServiceExtension(); } -- Gitee