From 296c10d3ce6a8ff719c7108efd3cd4800919fbd2 Mon Sep 17 00:00:00 2001 From: zhangzezhong Date: Tue, 10 Jun 2025 14:26:37 +0800 Subject: [PATCH] xts test Signed-off-by: zhangzezhong --- .../include/ets_service_extension.h | 149 +++++++ .../src/ets_service_extension.cpp | 387 ++++++++++++++++++ ...os.app.ability.ServiceExtensionAbility.ets | 97 +++++ frameworks/ets/ets/BUILD.gn | 38 ++ .../ets/ets/application/ExtensionContext.ets | 3 - .../application/ServiceExtensionContext.ets | 19 + frameworks/native/ability/BUILD.gn | 3 + .../ability_runtime/ets_extension_context.cpp | 78 ++++ frameworks/native/ability/native/BUILD.gn | 6 + .../ability/native/service_extension.cpp | 4 +- .../ability_runtime/ets_extension_context.h | 29 ++ 11 files changed, 809 insertions(+), 4 deletions(-) create mode 100644 frameworks/ets/ani/service_extension_ability/include/ets_service_extension.h create mode 100644 frameworks/ets/ani/service_extension_ability/src/ets_service_extension.cpp create mode 100644 frameworks/ets/ets/@ohos.app.ability.ServiceExtensionAbility.ets create mode 100644 frameworks/ets/ets/application/ServiceExtensionContext.ets create mode 100644 frameworks/native/ability/ability_runtime/ets_extension_context.cpp create mode 100644 interfaces/kits/native/ability/ability_runtime/ets_extension_context.h diff --git a/frameworks/ets/ani/service_extension_ability/include/ets_service_extension.h b/frameworks/ets/ani/service_extension_ability/include/ets_service_extension.h new file mode 100644 index 00000000000..a148c2bf2ee --- /dev/null +++ b/frameworks/ets/ani/service_extension_ability/include/ets_service_extension.h @@ -0,0 +1,149 @@ +/* + * 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_SERVICE_EXTENSION_H +#define OHOS_ABILITY_RUNTIME_ETS_SERVICE_EXTENSION_H + +#include "configuration.h" +#include "ets_runtime.h" +#include "service_extension.h" + +namespace OHOS { +namespace AbilityRuntime { +/** + * @brief Basic service components. + */ +class EtsServiceExtension : public ServiceExtension { +public: + explicit EtsServiceExtension(ETSRuntime &etsRuntime); + virtual ~EtsServiceExtension() override; + + /** + * @brief Create EtsServiceExtension. + * + * @param runtime The runtime. + * @return The EtsServiceExtension instance. + */ + static EtsServiceExtension *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. + */ + virtual void OnStart(const AAFwk::Want &want) 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. + * @return Returns a pointer to the sid of the connected Service extension. + */ + virtual sptr OnConnect(const AAFwk::Want &want) 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. + */ + virtual 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. + * + */ + virtual void OnDisconnect(const AAFwk::Want &want) 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. + */ + virtual void OnCommand(const AAFwk::Want &want, bool restart, int startId) 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. + */ + virtual void OnStop() 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 extension need dump info. + * + * @param params The params from service. + * @param info The dump info to show. + */ + virtual void Dump(const std::vector ¶ms, std::vector &info) override; + +private: + void ConfigurationUpdated(); + ani_ref CallObjectMethod(bool withResult, const char *name, const char *signature, ...); + + ETSRuntime &etsRuntime_; + std::unique_ptr etsObj_; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_SERVICE_EXTENSION_H \ No newline at end of file diff --git a/frameworks/ets/ani/service_extension_ability/src/ets_service_extension.cpp b/frameworks/ets/ani/service_extension_ability/src/ets_service_extension.cpp new file mode 100644 index 00000000000..a6e97772b8c --- /dev/null +++ b/frameworks/ets/ani/service_extension_ability/src/ets_service_extension.cpp @@ -0,0 +1,387 @@ +/* + * 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_service_extension.h" + +#include "ability_info.h" +#include "ability_manager_client.h" +#include "ani_common_want.h" +#include "ani_remote_object.h" +#include "configuration_utils.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "js_service_extension_context.h" + +namespace OHOS { +namespace AbilityRuntime { +using namespace OHOS::AppExecFwk; +namespace { +constexpr const char *CLASSNAME_SERVICE_ABILITY = + "L@ohos/app/ability/ServiceExtensionAbility/ServiceExtensionAbility;"; +constexpr const char *NATIVE_ONCONNECT_CALLBACK_SIGNATURE = "L@ohos/rpc/rpc/RemoteObject;:Z"; +constexpr const char *ON_CREATE_SIGNATURE = "L@ohos/app/ability/Want/Want;:V"; +constexpr const char *VOID_SIGNATURE = ":V"; +constexpr const char *ON_CONNECT_SIGNATURE = "L@ohos/app/ability/Want/Want;:Lstd/core/Object;"; +constexpr const char *CHECK_PROMISE_SIGNATURE = "Lstd/core/Object;:Z"; +constexpr const char *CALL_PROMISE_SIGNATURE = "Lstd/core/Object;:Z"; +constexpr const char *ON_DISCONNECT_SIGNATURE = "L@ohos/app/ability/Want/Want;:V"; +constexpr const char *ON_REQUEST_SIGNATURE = "L@ohos/app/ability/Want/Want;D:V"; + +void DisconnectPromiseCallback(ani_env *env, ani_object aniObj) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "DisconnectPromiseCallback"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + ani_long disconnectCallbackPoint = 0; + ani_status status = ANI_ERROR; + if ((status = env->Object_GetFieldByName_Long(aniObj, "disconnectCallbackPoint", &disconnectCallbackPoint)) != + ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "status : %{public}d", status); + return; + } + auto *callbackInfo = reinterpret_cast *>(disconnectCallbackPoint); + if (callbackInfo == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null callbackInfo"); + return; + } + callbackInfo->Call(); + AppExecFwk::AbilityTransactionCallbackInfo<>::Destroy(callbackInfo); +} + +void ConnectPromiseCallback(ani_env *env, ani_object aniObj, ani_object obj) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "ConnectPromiseCallback"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + ani_long connectCallbackPoint = 0; + ani_status status = ANI_ERROR; + if ((status = env->Object_GetFieldByName_Long(aniObj, "connectCallbackPoint", &connectCallbackPoint)) != ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "status : %{public}d", status); + return; + } + auto remoteObject = AniGetNativeRemoteObject(env, obj); + if (remoteObject == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null remoteObject"); + } + auto *callbackInfo = + reinterpret_cast> *>(connectCallbackPoint); + if (callbackInfo == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null callbackInfo"); + return; + } + + callbackInfo->Call(remoteObject); + AppExecFwk::AbilityTransactionCallbackInfo>::Destroy(callbackInfo); +} +} // namespace + +EtsServiceExtension *EtsServiceExtension::Create(const std::unique_ptr &runtime) +{ + return new EtsServiceExtension(static_cast(*runtime)); +} + +EtsServiceExtension::EtsServiceExtension(ETSRuntime &etsRuntime) : etsRuntime_(etsRuntime) {} +EtsServiceExtension::~EtsServiceExtension() +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "EtsServiceExtension destory"); + auto context = GetContext(); + if (context) { + context->Unbind(); + } +} + +void EtsServiceExtension::Init(const std::shared_ptr &record, + const std::shared_ptr &application, std::shared_ptr &handler, + const sptr &token) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "EtsServiceExtension init"); + if ((token == nullptr) || (application == nullptr) || (handler == nullptr) || (record == nullptr)) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "init failed, some obj null"); + return; + } + Extension::Init(record, application, handler, token); + if (Extension::abilityInfo_ == nullptr || Extension::abilityInfo_->srcEntrance.empty()) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "EtsServiceExtension Init abilityInfo error"); + return; + } + std::string srcPath(Extension::abilityInfo_->moduleName + "/"); + srcPath.append(Extension::abilityInfo_->srcEntrance); + auto pos = srcPath.rfind("."); + if (pos != std::string::npos) { + srcPath.erase(pos); + srcPath.append(".abc"); + } + std::string moduleName(Extension::abilityInfo_->moduleName); + moduleName.append("::").append(abilityInfo_->name); + etsObj_ = etsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath, + abilityInfo_->compileMode == AppExecFwk::CompileMode::ES_MODULE, false, abilityInfo_->srcEntrance); + if (etsObj_ == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null etsObj"); + return; + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + std::array functions = { + ani_native_function { + "nativeOnDisconnectCallback", VOID_SIGNATURE, reinterpret_cast(DisconnectPromiseCallback) }, + ani_native_function { "nativeOnConnectCallback", NATIVE_ONCONNECT_CALLBACK_SIGNATURE, + reinterpret_cast(ConnectPromiseCallback) }, + }; + ani_class cls = nullptr; + ani_status status = ANI_ERROR; + if ((status = env->FindClass(CLASSNAME_SERVICE_ABILITY, &cls)) != ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "status: %{public}d", status); + return; + } + if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "Class_BindNativeMethods is fail %{public}d", status); + return; + } +} + +void EtsServiceExtension::OnStart(const AAFwk::Want &want) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnStart"); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "env not found Ability.ets"); + return; + } + ani_ref wantRef = OHOS::AppExecFwk::WrapWant(env, want); + if (wantRef == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null wantRef"); + return; + } + + CallObjectMethod(false, "onCreate", ON_CREATE_SIGNATURE, wantRef); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "end"); +} + +void EtsServiceExtension::OnStop() +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnStop"); + ServiceExtension::OnStop(); + CallObjectMethod(false, "onDestroy", VOID_SIGNATURE); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "end"); +} + +sptr EtsServiceExtension::OnConnect(const AAFwk::Want &want) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnConnect"); + Extension::OnConnect(want); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return nullptr; + } + ani_ref wantRef = OHOS::AppExecFwk::WrapWant(env, want); + if (wantRef == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null wantRef"); + return nullptr; + } + ani_ref result = CallObjectMethod(true, "onConnect", ON_CONNECT_SIGNATURE, wantRef); + auto obj = reinterpret_cast(result); + auto remoteObj = AniGetNativeRemoteObject(env, obj); + if (remoteObj == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "remoteObj null"); + return nullptr; + } + TAG_LOGD(AAFwkTag::SERVICE_EXT, "end"); + return remoteObj; +} + +sptr EtsServiceExtension::OnConnect(const AAFwk::Want &want, + AppExecFwk::AbilityTransactionCallbackInfo> *callbackInfo, bool &isAsyncCallback) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnConnect"); + Extension::OnConnect(want); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return nullptr; + } + ani_ref wantRef = OHOS::AppExecFwk::WrapWant(env, want); + if (wantRef == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null wantRef"); + return nullptr; + } + ani_long connectCallbackPoint = (ani_long)callbackInfo; + ani_status status = ANI_ERROR; + ani_field callbackField = nullptr; + if ((status = env->Class_FindField(etsObj_->aniCls, "connectCallbackPoint", &callbackField)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return nullptr; + } + if ((status = env->Object_SetField_Long(etsObj_->aniObj, callbackField, connectCallbackPoint)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return nullptr; + } + ani_ref aniRemoteRef = CallObjectMethod(true, "onConnect", ON_CONNECT_SIGNATURE, wantRef); + auto aniRemoteobj = reinterpret_cast(aniRemoteRef); + ani_method method {}; + if ((status = env->Class_FindMethod(etsObj_->aniCls, "checkPromise", CHECK_PROMISE_SIGNATURE, &method)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return nullptr; + } + ani_boolean isPromise = false; + if ((status = env->Object_CallMethod_Boolean(etsObj_->aniObj, method, &isPromise, aniRemoteobj)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return nullptr; + } + if (!isPromise) { + isAsyncCallback = false; + auto remoteObj = AniGetNativeRemoteObject(env, aniRemoteobj); + if (remoteObj == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null remoteObj"); + } + return remoteObj; + } + if ((status = env->Class_FindMethod(etsObj_->aniCls, "callPromise", CALL_PROMISE_SIGNATURE, &method)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return nullptr; + } + ani_boolean callResult = false; + if ((status = env->Object_CallMethod_Boolean(etsObj_->aniObj, method, &callResult, aniRemoteobj)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return nullptr; + } + isAsyncCallback = callResult; + return nullptr; +} + +void EtsServiceExtension::OnDisconnect(const AAFwk::Want &want) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnDisconnect"); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + ani_ref wantRef = OHOS::AppExecFwk::WrapWant(env, want); + if (wantRef == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null wantRef"); + return; + } + CallObjectMethod(false, "onDisconnect", ON_DISCONNECT_SIGNATURE, wantRef); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "end"); +} + +void EtsServiceExtension::OnDisconnect( + const AAFwk::Want &want, AppExecFwk::AbilityTransactionCallbackInfo<> *callbackInfo, bool &isAsyncCallback) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnDisconnect"); + auto env = etsRuntime_.GetAniEnv(); + if (env) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + ani_ref wantRef = OHOS::AppExecFwk::WrapWant(env, want); + if (wantRef == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null wantRef"); + return; + } + if (callbackInfo == nullptr) { + isAsyncCallback = false; + OnDisconnect(want); + return; + } + ani_long disconnectCallbackPoint = (ani_long)callbackInfo; + ani_status status = ANI_ERROR; + ani_field field = nullptr; + if ((status = env->Class_FindField(etsObj_->aniCls, "disconnectCallbackPoint", &field)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return; + } + if ((status = env->Object_SetField_Long(etsObj_->aniObj, field, disconnectCallbackPoint)) != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "status : %{public}d", status); + return; + } + CallObjectMethod(false, "callOnDisconnect", ON_DISCONNECT_SIGNATURE, wantRef); +} + +void EtsServiceExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnCommand"); + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + ani_ref wantRef = OHOS::AppExecFwk::WrapWant(env, want); + if (wantRef == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null wantRef"); + return; + } + ani_int iStartId = static_cast(startId); + CallObjectMethod(false, "onRequest", ON_REQUEST_SIGNATURE, wantRef, iStartId); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "end"); + return; +} + +ani_ref EtsServiceExtension::CallObjectMethod(bool withResult, const char *name, const char *signature, ...) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, std::string("CallObjectMethod:") + name); + ani_status status = ANI_ERROR; + ani_method method = nullptr; + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return nullptr; + } + if ((status = env->Class_FindMethod(etsObj_->aniCls, name, signature, &method)) != ANI_OK) { + return nullptr; + } + if (method == nullptr) { + return nullptr; + } + ani_ref res = nullptr; + va_list args; + if (withResult) { + va_start(args, signature); + if ((status = env->Object_CallMethod_Ref_V(etsObj_->aniObj, method, &res, args)) != ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "status : %{public}d", status); + return nullptr; + } + va_end(args); + return res; + } + va_start(args, signature); + if ((status = env->Object_CallMethod_Void_V(etsObj_->aniObj, method, args)) != ANI_OK) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "status : %{public}d", status); + } + va_end(args); + return nullptr; +} +void EtsServiceExtension::OnConfigurationUpdated(const AppExecFwk::Configuration &configuration) {} + +void EtsServiceExtension::ConfigurationUpdated() {} + +void EtsServiceExtension::Dump(const std::vector ¶ms, std::vector &info) {} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ets/@ohos.app.ability.ServiceExtensionAbility.ets b/frameworks/ets/ets/@ohos.app.ability.ServiceExtensionAbility.ets new file mode 100644 index 00000000000..c17298b63eb --- /dev/null +++ b/frameworks/ets/ets/@ohos.app.ability.ServiceExtensionAbility.ets @@ -0,0 +1,97 @@ +/* + * 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 rpc from '@ohos.rpc'; +import Want from '@ohos.app.ability.Want'; +import ServiceExtensionContext from 'application.ServiceExtensionContext'; +import { Configuration } from '@ohos.app.ability.Configuration' +import hilog from '@ohos.hilog'; + +class MyService extends rpc.RemoteObject { + constructor(descriptor: string) { + super(descriptor); + } + + public onRemoteMessageRequest( + code: number, + data: rpc.MessageSequence, + reply: rpc.MessageSequence, + options: rpc.MessageOption + ): boolean | Promise { + return false; + } +} + +export default class ServiceExtensionAbility { + private connectCallbackPoint: long; + + private native nativeOnConnectCallback(service: rpc.RemoteObject): boolean; + + private checkPromise(obj: NullishType): boolean { + if (obj instanceof Promise) { + return true; + } + return false; + } + + private callPromise(p: Promise): boolean { + let remoteObj: rpc.RemoteObject = await p; + return this.nativeOnConnectCallback(remoteObj); + } + + private isOnDisconnectAsync: boolean = true; + private disconnectCallbackPoint: long; + + private native nativeOnDisconnectCallback(): void; + + private callOnDisconnect(want: Want): void { + let p = this.onDisconnectAsync(want); + if (this.isOnDisconnectAsync) { + p.then((a: undefined): void => { + this.nativeOnDisconnectCallback(); + }); + } else { + this.onDisconnect(want); + } + } + + launchWant: Want = new Want(); + lastRequestWant: Want = new Want(); + context: ServiceExtensionContext = {}; + + onCreate(want: Want): void { + } + + onDestroy(): void { + } + + onRequest(want: Want, startld: double): void { + } + + onConnect(want: Want): rpc.RemoteObject | Promise { + let myService: rpc.RemoteObject = new MyService("onConnect"); + return myService; + } + + onDisconnect(want: Want): void { + } + + onDisconnectAsync(want: Want): Promise { + console.log("onDisconnectAsync"); + this.isOnDisconnectAsync = false; + return new Promise((resolve: (a: undefined) => void, reject: (err: Error) => void): void => { + }); + } +} \ No newline at end of file diff --git a/frameworks/ets/ets/BUILD.gn b/frameworks/ets/ets/BUILD.gn index 9f07fdb1852..cf5f23340a7 100644 --- a/frameworks/ets/ets/BUILD.gn +++ b/frameworks/ets/ets/BUILD.gn @@ -111,6 +111,42 @@ ohos_prebuilt_etc("ability_runtime_want_constant_abc_etc") { deps = [ ":ability_runtime_want_constant_abc" ] } +generate_static_abc("ability_runtime_extension_context_abc") { + base_url = "./" + files = [ "./application/ExtensionContext.ets" ] + + is_boot_abc = "True" + device_dst_file = + "/system/framework/ability_runtime_extension_context_abc.abc" +} + +ohos_prebuilt_etc("ability_runtime_extension_context_abc_etc") { + source = "$target_out_dir/ability_runtime_extension_context_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ability_runtime_extension_context_abc" ] +} + +generate_static_abc("service_extension_ability") { + base_url = "./" + files = [ + "./@ohos.app.ability.ServiceExtensionAbility.ets", + "./application/ServiceExtensionContext.ets", + ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/service_extension_ability.abc" +} + +ohos_prebuilt_etc("service_extension_ability_abc_etc") { + source = "$target_out_dir/service_extension_ability.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":service_extension_ability" ] +} + group("ets_packages") { deps = [ ":ability_runtime_ability_constant_abc_etc", @@ -119,5 +155,7 @@ group("ets_packages") { ":ability_runtime_start_options_abc_etc", ":ability_runtime_want_abc_etc", ":ability_runtime_want_constant_abc_etc", + ":ability_runtime_extension_context_abc_etc", + ":service_extension_ability_abc_etc", ] } diff --git a/frameworks/ets/ets/application/ExtensionContext.ets b/frameworks/ets/ets/application/ExtensionContext.ets index 4b6cbf03130..2d46b66d2ad 100644 --- a/frameworks/ets/ets/application/ExtensionContext.ets +++ b/frameworks/ets/ets/application/ExtensionContext.ets @@ -17,9 +17,6 @@ import Context from 'application.Context' import {ExtensionAbilityInfo} from 'bundleManager.ExtensionAbilityInfo' export default class ExtensionContext extends Context { - static { - loadLibrary("context_ani"); - } extensionAbilityInfo: ExtensionAbilityInfo; native constructor(); constructor(extensionAbilityInfo: ExtensionAbilityInfo) { diff --git a/frameworks/ets/ets/application/ServiceExtensionContext.ets b/frameworks/ets/ets/application/ServiceExtensionContext.ets new file mode 100644 index 00000000000..8c9fe395ffa --- /dev/null +++ b/frameworks/ets/ets/application/ServiceExtensionContext.ets @@ -0,0 +1,19 @@ +/* + * 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' + +export default class ServiceExtensionContext extends ExtensionContext { + +} \ No newline at end of file diff --git a/frameworks/native/ability/BUILD.gn b/frameworks/native/ability/BUILD.gn index e4f5a06ba62..e9212cdc826 100644 --- a/frameworks/native/ability/BUILD.gn +++ b/frameworks/native/ability/BUILD.gn @@ -59,6 +59,7 @@ ohos_shared_library("ability_context_native") { "ability_runtime/connection_manager.cpp", "ability_runtime/dialog_request_callback_impl.cpp", "ability_runtime/dialog_ui_extension_callback.cpp", + "ability_runtime/ets_extension_context.cpp", "ability_runtime/js_extension_context.cpp", "ability_runtime/local_call_container.cpp", "ability_runtime/local_call_record.cpp", @@ -92,6 +93,8 @@ ohos_shared_library("ability_context_native") { "image_framework:image_native", "ipc:ipc_core", "napi:ace_napi", + "runtime_core:ani", + "bundle_framework:bms_ani_common", ] public_external_deps = [ "ability_base:extractortool", diff --git a/frameworks/native/ability/ability_runtime/ets_extension_context.cpp b/frameworks/native/ability/ability_runtime/ets_extension_context.cpp new file mode 100644 index 00000000000..cdeb556cd9c --- /dev/null +++ b/frameworks/native/ability/ability_runtime/ets_extension_context.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ets_extension_context.h" + +#include "common_fun_ani.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AbilityRuntime { + +bool SetExtensionAbilityInfo(ani_env *aniEnv, ani_class contextClass, ani_object contextObj, + std::shared_ptr context, std::shared_ptr abilityInfo) +{ + bool iRet = false; + if (aniEnv == nullptr || context == nullptr || abilityInfo == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "aniEnv or context or abilityInfo is nullptr"); + return iRet; + } + auto hapModuleInfo = context->GetHapModuleInfo(); + ani_status status = ANI_OK; + if (abilityInfo && hapModuleInfo) { + auto isExist = [&abilityInfo](const AppExecFwk::ExtensionAbilityInfo &info) { + TAG_LOGD(AAFwkTag::CONTEXT, "%{public}s, %{public}s", info.bundleName.c_str(), info.name.c_str()); + return info.bundleName == abilityInfo->bundleName && info.name == abilityInfo->name; + }; + auto infoIter = + std::find_if(hapModuleInfo->extensionInfos.begin(), hapModuleInfo->extensionInfos.end(), isExist); + if (infoIter == hapModuleInfo->extensionInfos.end()) { + TAG_LOGE(AAFwkTag::CONTEXT, "set extensionAbilityInfo fail"); + return iRet; + } + ani_field extensionAbilityInfoField; + status = aniEnv->Class_FindField(contextClass, "extensionAbilityInfo", &extensionAbilityInfoField); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return iRet; + } + ani_object extAbilityInfoObj = AppExecFwk::CommonFunAni::ConvertExtensionInfo(aniEnv, *infoIter); + status = aniEnv->Object_SetField_Ref( + contextObj, extensionAbilityInfoField, reinterpret_cast(extAbilityInfoObj)); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return iRet; + } + iRet = true; + } + return iRet; +} + +void CreatEtsExtensionContext(ani_env *aniEnv, ani_class contextClass, ani_object contextObj, + std::shared_ptr context, std::shared_ptr abilityInfo) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "CreatEtsExtensionContext Call"); + if (aniEnv == nullptr || context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "aniEnv or context is nullptr"); + return; + } + + if (!SetExtensionAbilityInfo(aniEnv, contextClass, contextObj, context, abilityInfo)) { + TAG_LOGE(AAFwkTag::CONTEXT, "SetExtensionAbilityInfo fail"); + return; + } +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/ability/native/BUILD.gn b/frameworks/native/ability/native/BUILD.gn index f250b62c7ee..23311d3293f 100644 --- a/frameworks/native/ability/native/BUILD.gn +++ b/frameworks/native/ability/native/BUILD.gn @@ -1113,6 +1113,8 @@ ohos_shared_library("service_extension") { 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/service_extension_ability/include", "${ability_runtime_path}/interfaces/kits/native/ability/native", "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime", "${ability_runtime_path}/utils/global/freeze/include", @@ -1123,6 +1125,7 @@ ohos_shared_library("service_extension") { "${ability_runtime_native_path}/ability/native/js_service_extension_context.cpp", "${ability_runtime_native_path}/ability/native/service_extension.cpp", "${ability_runtime_native_path}/appkit/ability_runtime/service_extension_context.cpp", + "${ability_runtime_path}/frameworks/ets/ani/service_extension_ability/src/ets_service_extension.cpp", ] deps = [ @@ -1139,6 +1142,7 @@ ohos_shared_library("service_extension") { "${ability_runtime_native_path}/appkit:app_context", "${ability_runtime_native_path}/insight_intent/insight_intent_context:insightintentcontext", "${ability_runtime_path}/utils/global/freeze:freeze_util", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", ] external_deps = [ @@ -1152,7 +1156,9 @@ ohos_shared_library("service_extension") { "hitrace:hitrace_meter", "ipc:ipc_core", "ipc:ipc_napi", + "ipc:rpc_ani", "napi:ace_napi", + "runtime_core:ani", "safwk:system_ability_fwk", "samgr:samgr_proxy", ] diff --git a/frameworks/native/ability/native/service_extension.cpp b/frameworks/native/ability/native/service_extension.cpp index 0838b03f9aa..cbf2ad871c8 100644 --- a/frameworks/native/ability/native/service_extension.cpp +++ b/frameworks/native/ability/native/service_extension.cpp @@ -21,6 +21,7 @@ #include "js_service_extension.h" #include "runtime.h" #include "service_extension_context.h" +#include "ets_service_extension.h" namespace OHOS { namespace AbilityRuntime { @@ -46,7 +47,8 @@ ServiceExtension* ServiceExtension::Create(const std::unique_ptr& runti switch (runtime->GetLanguage()) { case Runtime::Language::JS: return JsServiceExtension::Create(runtime); - + case Runtime::Language::ETS: + return EtsServiceExtension::Create(runtime); default: return new ServiceExtension(); } diff --git a/interfaces/kits/native/ability/ability_runtime/ets_extension_context.h b/interfaces/kits/native/ability/ability_runtime/ets_extension_context.h new file mode 100644 index 00000000000..12c99e56649 --- /dev/null +++ b/interfaces/kits/native/ability/ability_runtime/ets_extension_context.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_STS_EXTENSION_CONTEXT_H +#define OHOS_ABILITY_RUNTIME_STS_EXTENSION_CONTEXT_H + +#include "extension_context.h" +#include "ani.h" + +namespace OHOS { +namespace AbilityRuntime { +void CreatEtsExtensionContext(ani_env* aniEnv, ani_class contextClass, ani_object contextObj, + std::shared_ptr context, + std::shared_ptr abilityInfo); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_STS_EXTENSION_CONTEXT_H \ No newline at end of file -- Gitee