From c64e7894d49eac26f06fea663b56460008acb078 Mon Sep 17 00:00:00 2001 From: zhangzezhong Date: Wed, 28 May 2025 20:18:24 +0800 Subject: [PATCH] =?UTF-8?q?ani=20serviceextension=200411=E5=9B=9E=E5=90=88?= =?UTF-8?q?master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhangzezhong --- .../include/ets_service_extension.h | 179 ++++++++ .../src/ets_service_extension.cpp | 413 ++++++++++++++++++ ...os.app.ability.ServiceExtensionAbility.ets | 87 ++++ .../application/ServiceExtensionContext.ets | 19 + .../ability/native/service_extension.cpp | 4 +- 5 files changed, 701 insertions(+), 1 deletion(-) 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 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..78acca89b24 --- /dev/null +++ b/frameworks/ets/ani/service_extension_ability/include/ets_service_extension.h @@ -0,0 +1,179 @@ +/* + * 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 "service_extension.h" +#include "ets_runtime.h" +class ETSNativeReference; + +namespace OHOS { +namespace AbilityRuntime { +class ServiceExtension; +class ETSRuntime; +/** + * @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; + + bool HandleInsightIntent(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. + */ + 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 configuration changed, including system configuration and window configuration. + * + */ + void ConfigurationUpdated(); + + /** + * @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: + ani_ref CallObjectMethod(bool withResult, const char* name, const char* signature, ...); + + void BindContext(napi_env env, napi_value obj); + + void GetSrcPath(std::string &srcPath); + + bool CheckPromise(napi_value result); + + bool CallPromise(napi_value result, AppExecFwk::AbilityTransactionCallbackInfo<> *callbackInfo); + + void ListenWMS(); + + bool GetInsightIntentExecutorInfo(const Want &want, + const std::shared_ptr &executeParam, + InsightIntentExecutorInfo &executorInfo); + + bool OnInsightIntentExecuteDone(uint64_t intentId, const AppExecFwk::InsightIntentExecuteResult &result) override; + + ETSRuntime& etsRuntime_; + std::unique_ptr etsObj_; + std::shared_ptr shellContextRef_ = nullptr; + std::shared_ptr handler_ = nullptr; +}; +} // 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..9aa898297e4 --- /dev/null +++ b/frameworks/ets/ani/service_extension_ability/src/ets_service_extension.cpp @@ -0,0 +1,413 @@ +/* +* 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 "hitrace_meter.h" +#include "hilog_tag_wrapper.h" +#include "insight_intent_execute_param.h" +#include "insight_intent_execute_result.h" +#include "insight_intent_executor_info.h" +#include "insight_intent_executor_mgr.h" +#include "js_service_extension_context.h" +#ifdef SUPPORT_GRAPHICS +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "window_scene.h" +#endif + +namespace OHOS { +namespace AbilityRuntime { +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); +} + +using namespace OHOS::AppExecFwk; + +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 (record == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "record 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, "Failed to get etsObj"); + return; + } + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null env"); + return; + } + std::array functions = { + ani_native_function { "nativeOnDisconnectCallback", ":V", reinterpret_cast(DisconnectPromiseCallback) }, + ani_native_function { "nativeOnConnectCallback", nullptr, reinterpret_cast(ConnectPromiseCallback) }, + }; + ani_class cls = nullptr; + ani_status status = ANI_ERROR; + if ((status = env->FindClass("L@ohos/app/ability/ServiceExtensionAbility/ServiceExtensionAbility;", &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) { + 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; + } + + const char* signature = "L@ohos/app/ability/Want/Want;:V"; + CallObjectMethod(false, "onCreate", 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(); + const char* signature = ":V"; + CallObjectMethod(false, "onDestroy", 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", nullptr, 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", nullptr, wantRef); + auto aniRemoteobj = reinterpret_cast(aniRemoteRef); + ani_method method {}; + if ((status = env->Class_FindMethod(EtsObj_->aniCls, "checkPromise", nullptr, &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", nullptr, &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) { + 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; + } + const char* signature = "L@ohos/app/ability/Want/Want;:V"; + CallObjectMethod(false, "onDisconnect", 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, "env not found"); + 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", "L@ohos/app/ability/Want/Want;:V", 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) { + 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"); + } + ani_int iStartId = static_cast(startId); + const char* signature = "L@ohos/app/ability/Want/Want;D:V"; + CallObjectMethod(false, "onRequest", signature, wantRef, iStartId); + TAG_LOGD(AAFwkTag::SERVICE_EXT, "end"); + return; +} + +bool EtsServiceExtension::HandleInsightIntent(const AAFwk::Want &want) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "HandleInsightIntent"); + return true; +} + +bool EtsServiceExtension::GetInsightIntentExecutorInfo(const Want &want, + const std::shared_ptr &executeParam, + InsightIntentExecutorInfo &executorInfo) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "GetInsightIntentExecutorInfo"); + return true; +} + +bool EtsServiceExtension::OnInsightIntentExecuteDone(uint64_t intentId, + const AppExecFwk::InsightIntentExecuteResult &result) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "OnInsightIntentExecuteDone"); + return true; +} + +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) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "env not found Ability.ets"); + 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); + } + 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) +{ +} +} // AbilityRuntime +} // 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..f529723135b --- /dev/null +++ b/frameworks/ets/ets/@ohos.app.ability.ServiceExtensionAbility.ets @@ -0,0 +1,87 @@ +/* + * 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{ + console.log("onCreate"); + } + onDestroy(): void { + console.log("onDestroy"); + } + onRequest(want: Want, startld: double): void { + console.log("onRequest"); + } + onConnect(want: Want): rpc.RemoteObject | Promise { + console.log("onConnect"); + let myService: rpc.RemoteObject = new MyService("onConnect"); + return myService; + } + onDisconnect(want: Want): void { + console.log("onDisconnect"); + } + 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/application/ServiceExtensionContext.ets b/frameworks/ets/ets/application/ServiceExtensionContext.ets new file mode 100644 index 00000000000..6a325ab584b --- /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 { + +} 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(); } -- Gitee