diff --git a/frameworks/ets/ets/@ohos.app.ability.UIAbility.ets b/frameworks/ets/ets/@ohos.app.ability.UIAbility.ets index f53af8d5f3a52ec32b5a2d7f95d02ff0d5803e17..63fd48d05b8d4aa40a159e3f0e7102b88e0f1004 100644 --- a/frameworks/ets/ets/@ohos.app.ability.UIAbility.ets +++ b/frameworks/ets/ets/@ohos.app.ability.UIAbility.ets @@ -14,6 +14,7 @@ */ import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import UIAbilityContext from 'application.UIAbilityContext'; import Want from '@ohos.app.ability.Want'; import window from '@ohos.window'; import { AbilityUtils } from './utils/AbilityUtils'; @@ -43,6 +44,7 @@ export default class UIAbility { return false; } + context: UIAbilityContext = new UIAbilityContext(); launchWant: Want = new Want(); lastRequestWant: Want = new Want(); diff --git a/frameworks/ets/ets/BUILD.gn b/frameworks/ets/ets/BUILD.gn index 8326af80f8cb493e56142176a7158c9d36a2b34b..fe486b15d54251e641de6473b8b723f35a51b279 100644 --- a/frameworks/ets/ets/BUILD.gn +++ b/frameworks/ets/ets/BUILD.gn @@ -226,6 +226,23 @@ ohos_prebuilt_etc("ability_runtime_ui_ability_abc_etc") { deps = [ ":ability_runtime_ui_ability_abc" ] } +generate_static_abc("ability_runtime_ui_ability_context_abc") { + base_url = "./" + files = [ "./application/UIAbilityContext.ets" ] + + is_boot_abc = "True" + device_dst_file = + "/system/framework/ability_runtime_ui_ability_context_abc.abc" +} + +ohos_prebuilt_etc("ability_runtime_ui_ability_context_abc_etc") { + source = "$target_out_dir/ability_runtime_ui_ability_context_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ability_runtime_ui_ability_context_abc" ] +} + group("ets_packages") { deps = [ ":ability_runtime_ability_result_abc_etc", @@ -241,5 +258,6 @@ group("ets_packages") { ":ability_runtime_want_constant_abc_etc", ":ability_runtime_extension_context_abc_etc", ":ability_runtime_ui_ability_abc_etc", + ":ability_runtime_ui_ability_context_abc_etc", ] } diff --git a/frameworks/ets/ets/application/UIAbilityContext.ets b/frameworks/ets/ets/application/UIAbilityContext.ets new file mode 100644 index 0000000000000000000000000000000000000000..4c515cb3f6a11462bc4b9ac32a9fa30064b482d3 --- /dev/null +++ b/frameworks/ets/ets/application/UIAbilityContext.ets @@ -0,0 +1,222 @@ +/* + * 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 { AbilityInfo } from 'bundleManager.AbilityInfo'; +import Want from '@ohos.app.ability.Want'; +import StartOptions from '@ohos.app.ability.StartOptions'; +import { BusinessError, AsyncCallback } from '@ohos.base'; +import { AbilityResult } from 'ability.abilityResult'; +import { Configuration } from '@ohos.app.ability.Configuration'; +import Context from 'application.Context'; +import window from '@ohos.window'; + +export class AsyncCallbackWrapper { + myFun_: AsyncCallback = (err: BusinessError, data: T) => { + } + + constructor(myFun: AsyncCallback) { + console.log("AsyncCallbackWrapper"); + this.myFun_ = myFun; + } + + invoke(err: BusinessError, data: T): void { + this.myFun_(err, data); + } +} + +export default class UIAbilityContext extends Context { + static { + loadLibrary("context_ani"); + } + + config: Configuration; + abilityInfo: AbilityInfo; + windowStage: window.WindowStage; + + native constructor(); + + constructor(config: Configuration, abilityInfo: AbilityInfo, windowStage: window.WindowStage) { + super(); + this.config = config; + this.abilityInfo = abilityInfo; + this.windowStage = windowStage; + } + + private native nativeStartAbilitySync(want: Want, callback: AsyncCallbackWrapper): void; + + private native nativeStartAbilitySync(want: Want, options: StartOptions, callback: AsyncCallbackWrapper): void; + + private native nativeStartAbilityForResult(want: Want, callback: AsyncCallbackWrapper): void; + + private native nativeStartAbilityForResult(want: Want, startOptions: StartOptions, + callback: AsyncCallbackWrapper): void; + + private native nativeTerminateSelfSync(callback: AsyncCallbackWrapper): void; + + private native nativeTerminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallbackWrapper): void; + + private native nativeReportDrawnCompletedSync(callback: AsyncCallbackWrapper): void; + + startAbility(want: Want, callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeStartAbilitySync(want, myCall); + }); + } + + startAbility(want: Want, options: StartOptions, callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeStartAbilitySync(want, options, myCall); + }); + } + + startAbility(want: Want): Promise { + let p: Promise = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError) => { + if (err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartAbilitySync(want, myCall); + }); + }); + return p; + } + + 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) => { + if (err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartAbilitySync(want, options, myCall); + }); + }); + return p; + } + + startAbilityForResult(want: Want, callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeStartAbilityForResult(want, myCall); + }); + } + + startAbilityForResult(want: Want, startOptions: StartOptions, callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeStartAbilityForResult(want, startOptions, myCall); + }); + } + + startAbilityForResult(want: Want): Promise { + let p = new Promise((resolve: (data: AbilityResult) => void, + reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError, data: AbilityResult) => { + if (err.code == 0) { + resolve(data); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartAbilityForResult(want, myCall); + }); + }); + return p; + } + + startAbilityForResult(want: Want, startOptions: StartOptions): Promise { + let p = new Promise((resolve: (data: AbilityResult) => void, + reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError, data: AbilityResult) => { + if (err.code == 0) { + resolve(data); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartAbilityForResult(want, startOptions, myCall); + }); + }); + return p; + } + + terminateSelf(callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeTerminateSelfSync(myCall); + }); + } + + terminateSelf(): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError) => { + if (err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeTerminateSelfSync(myCall); + }); + }); + return p; + } + + terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeTerminateSelfWithResult(parameter, myCall); + }); + } + + terminateSelfWithResult(parameter: AbilityResult): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let myCall = new AsyncCallbackWrapper((err: BusinessError) => { + if (err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeTerminateSelfWithResult(parameter, myCall); + }); + }); + return p; + } + + reportDrawnCompleted(callback: AsyncCallback): void { + let myCall = new AsyncCallbackWrapper(callback); + taskpool.execute((): void => { + this.nativeReportDrawnCompletedSync(myCall); + }); + } +} diff --git a/frameworks/native/ability/native/BUILD.gn b/frameworks/native/ability/native/BUILD.gn index 2dc9d73d532def138fcf63ac399dfca51a52f6ee..0981fde52533475057ebcd180ffea9fb8de76b3f 100644 --- a/frameworks/native/ability/native/BUILD.gn +++ b/frameworks/native/ability/native/BUILD.gn @@ -287,6 +287,7 @@ ohos_shared_library("abilitykit_native") { branch_protector_ret = "pac_ret" include_dirs = [ + "${ability_runtime_path}/frameworks/ets/ani/ani_common/include", "${ability_runtime_path}/utils/global/time/include", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_service_extension_ability/connection", ] @@ -302,6 +303,7 @@ ohos_shared_library("abilitykit_native") { "${ability_runtime_native_path}/ability/native/ability_loader.cpp", "${ability_runtime_native_path}/ability/native/ability_post_event_timeout.cpp", "${ability_runtime_native_path}/ability/native/ability_process.cpp", + "${ability_runtime_native_path}/ability/native/ability_runtime/ets_ability_context.cpp", "${ability_runtime_native_path}/ability/native/ability_runtime/js_ability.cpp", "${ability_runtime_native_path}/ability/native/ability_runtime/js_ability_context.cpp", "${ability_runtime_native_path}/ability/native/ability_runtime/js_caller_complex.cpp", @@ -359,6 +361,7 @@ ohos_shared_library("abilitykit_native") { "${ability_runtime_native_path}/appkit:app_context_utils", "${ability_runtime_native_path}/appkit:appkit_delegator", "${ability_runtime_native_path}/appkit:appkit_manager_helper", + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", "${ability_runtime_path}/utils/global/freeze:freeze_util", "${ability_runtime_services_path}/common:app_util", "${ability_runtime_services_path}/common:event_report", @@ -374,6 +377,7 @@ ohos_shared_library("abilitykit_native") { "access_token:libtoken_callback_sdk", "access_token:libtokenid_sdk", "bundle_framework:appexecfwk_base", + "bundle_framework:bms_ani_common", "c_utils:utils", "common_event_service:cesfwk_innerkits", "eventhandler:libeventhandler", @@ -387,6 +391,7 @@ ohos_shared_library("abilitykit_native") { "json:nlohmann_json_static", "napi:ace_napi", "resource_management:global_resmgr", + "runtime_core:ani", "samgr:samgr_proxy", "window_manager:windowstage_kit", ] diff --git a/frameworks/native/ability/native/ability_runtime/ets_ability_context.cpp b/frameworks/native/ability/native/ability_runtime/ets_ability_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e8f211e7276e6c3ab4c77c99f115b72ff63b60c --- /dev/null +++ b/frameworks/native/ability/native/ability_runtime/ets_ability_context.cpp @@ -0,0 +1,473 @@ +/* + * 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 "ability_runtime/ets_ability_context.h" + +#include "ani_common_ability_result.h" +#include "ani_common_configuration.h" +#include "ani_common_start_options.h" +#include "ani_common_want.h" +#include "app_utils.h" +#include "common_fun_ani.h" +#include "hilog_tag_wrapper.h" +#include "hitrace_meter.h" +#include "ets_context_utils.h" +#include "ets_error_utils.h" +#include "want.h" + +namespace OHOS { +namespace AbilityRuntime { +std::mutex EtsAbilityContext::requestCodeMutex_; +namespace { +static std::once_flag g_bindNativeMethodsFlag; +constexpr const char *UI_ABILITY_CONTEXT_CLASS_NAME = "Lapplication/UIAbilityContext/UIAbilityContext;"; +constexpr const char *CLASSNAME_ASYNC_CALLBACK_WRAPPER = "Lutils/AbilityUtils/AsyncCallbackWrapper;"; +} // namespace + +std::shared_ptr EtsAbilityContext::GetAbilityContext(ani_env *env, ani_object aniObj) +{ + if (env == nullptr || aniObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env or aniObj"); + return nullptr; + } + ani_long nativeContextLong; + ani_status status = env->Object_GetFieldByName_Long(aniObj, "nativeContext", &nativeContextLong); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_GetFieldByName_Long status: %{public}d", status); + return nullptr; + } + auto weakContext = reinterpret_cast *>(nativeContextLong); + return weakContext != nullptr ? weakContext->lock() : nullptr; +} + +ani_object EtsAbilityContext::SetAbilityContext(ani_env *env, const std::shared_ptr &context) +{ + if (env == nullptr || context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env or context"); + return nullptr; + } + ani_class cls = nullptr; + ani_object contextObj = nullptr; + ani_method method = nullptr; + ani_status status = env->FindClass(UI_ABILITY_CONTEXT_CLASS_NAME, &cls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass status: %{public}d", status); + return nullptr; + } + if ((status = env->Class_FindMethod(cls, "", ":V", &method)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Class_FindMethod status: %{public}d", status); + return nullptr; + } + if ((status = env->Object_New(cls, method, &contextObj)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_New status: %{public}d", status); + return nullptr; + } + + auto workContext = new (std::nothrow) std::weak_ptr(context); + if (workContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "workContext nullptr"); + return nullptr; + } + ani_long nativeContextLong = (ani_long)workContext; + if ((status = env->Object_SetFieldByName_Long(contextObj, "nativeContext", nativeContextLong)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_SetFieldByName_Long status: %{public}d", status); + delete workContext; + workContext = nullptr; + return nullptr; + } + return contextObj; +} + +// to be done: free install +void EtsAbilityContext::StartAbility(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object call) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "StartAbility called"); + GetInstance().OnStartAbility(env, aniObj, wantObj, nullptr, call); +} + +void EtsAbilityContext::StartAbilityWithOptions( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object opt, ani_object call) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "StartAbilityWithOptions called"); + GetInstance().OnStartAbility(env, aniObj, wantObj, opt, call); +} + +// to be done: free install +void EtsAbilityContext::StartAbilityForResult(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "StartAbilityForResult called"); + GetInstance().OnStartAbilityForResult(env, aniObj, wantObj, nullptr, callback); +} + +// to be done: free install +void EtsAbilityContext::StartAbilityForResultWithOptions( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object startOptionsObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "StartAbilityForResultWithOptions called"); + GetInstance().OnStartAbilityForResult(env, aniObj, wantObj, startOptionsObj, callback); +} + +void EtsAbilityContext::TerminateSelf(ani_env *env, ani_object aniObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "TerminateSelf called"); + GetInstance().OnTerminateSelf(env, aniObj, callback); +} + +void EtsAbilityContext::TerminateSelfWithResult( + ani_env *env, ani_object aniObj, ani_object abilityResult, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "TerminateSelfWithResult called"); + GetInstance().OnTerminateSelfWithResult(env, aniObj, abilityResult, callback); +} + +void EtsAbilityContext::ReportDrawnCompleted(ani_env *env, ani_object aniObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "ReportDrawnCompleted called"); + GetInstance().OnReportDrawnCompleted(env, aniObj, callback); +} + +int32_t EtsAbilityContext::GenerateRequestCode() +{ + static int32_t curRequestCode_ = 0; + std::lock_guard lock(requestCodeMutex_); + curRequestCode_ = (curRequestCode_ == INT_MAX) ? 0 : (curRequestCode_ + 1); + return curRequestCode_; +} + +void EtsAbilityContext::InheritWindowMode(ani_env *env, ani_object aniObj, AAFwk::Want &want) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "InheritWindowMode"); +#ifdef SUPPORT_SCREEN + // only split mode need inherit + auto context = GetAbilityContext(env, aniObj); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "context null"); + return; + } + auto windowMode = context->GetCurrentWindowMode(); + if (AAFwk::AppUtils::GetInstance().IsInheritWindowSplitScreenMode() && + (windowMode == AAFwk::AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_PRIMARY || + windowMode == AAFwk::AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_SECONDARY)) { + want.SetParam(AAFwk::Want::PARAM_RESV_WINDOW_MODE, windowMode); + } + TAG_LOGD(AAFwkTag::CONTEXT, "window mode is %{public}d", windowMode); +#endif +} + +bool EtsAbilityContext::AsyncCallback(ani_env *env, ani_object call, ani_object error, ani_object result) +{ + if (env == nullptr || call == nullptr) { + TAG_LOGE(AAFwkTag::JSNAPI, "env or call is nullptr"); + return false; + } + ani_class clsCall = nullptr; + ani_status status = env->FindClass(CLASSNAME_ASYNC_CALLBACK_WRAPPER, &clsCall); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass status: %{public}d", status); + return false; + } + ani_method method = nullptr; + if ((status = env->Class_FindMethod(clsCall, "invoke", "L@ohos/base/BusinessError;Lstd/core/Object;:V", &method)) != + ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Class_FindMethod status: %{public}d", status); + return false; + } + if (error == nullptr) { + ani_ref nullRef = nullptr; + env->GetNull(&nullRef); + error = reinterpret_cast(nullRef); + } + if (result == nullptr) { + ani_ref undefinedRef = nullptr; + env->GetUndefined(&undefinedRef); + result = reinterpret_cast(undefinedRef); + } + if ((status = env->Object_CallMethod_Void(call, method, error, result)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return false; + } + return true; +} + +void EtsAbilityContext::OnStartAbility( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object opt, ani_object call) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + AAFwk::Want want; + if (!OHOS::AppExecFwk::UnwrapWant(env, wantObj, want)) { + ThrowEtsInvalidParamError(env, "Parse param want failed, must be a Want"); + return; + } + InheritWindowMode(env, aniObj, want); + auto context = EtsAbilityContext::GetAbilityContext(env, aniObj); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + ThrowEtsInvalidParamError(env, "null context"); + return; + } + if ((want.GetFlags() & AAFwk::Want::FLAG_INSTALL_ON_DEMAND) == AAFwk::Want::FLAG_INSTALL_ON_DEMAND) { + std::string startTime = std::to_string( + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count()); + want.SetParam(AAFwk::Want::PARAM_RESV_START_TIME, startTime); + } + ErrCode innerErrCode = ERR_OK; + if (opt != nullptr) { + AAFwk::StartOptions startOptions; + if (!OHOS::AppExecFwk::UnwrapStartOptions(env, opt, startOptions)) { + ThrowEtsInvalidParamError(env, "Parse param startOptions failed, startOptions must be StartOptions."); + TAG_LOGE(AAFwkTag::CONTEXT, "invalid options"); + return; + } + innerErrCode = context->StartAbility(want, startOptions, -1); + } else { + innerErrCode = context->StartAbility(want, -1); + } + ani_object aniObject = CreateEtsError(env, AbilityErrorCode::ERROR_OK); + if (innerErrCode != ERR_OK) { + aniObject = CreateEtsErrorByNativeErr(env, innerErrCode); + } + if ((want.GetFlags() & AAFwk::Want::FLAG_INSTALL_ON_DEMAND) == AAFwk::Want::FLAG_INSTALL_ON_DEMAND) { + // to be done: free install + } else { + AsyncCallback(env, call, aniObject, nullptr); + } +} + +// to be done: free install +void EtsAbilityContext::OnStartAbilityForResult( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object startOptionsObj, ani_object callback) +{ + auto context = EtsAbilityContext::GetAbilityContext(env, aniObj); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetAbilityContext is nullptr"); + ThrowEtsErrorByNativeErr(env, static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT)); + return; + } + AAFwk::Want want; + OHOS::AppExecFwk::UnwrapWant(env, wantObj, want); + AAFwk::StartOptions startOptions; + if (startOptionsObj) { + OHOS::AppExecFwk::UnwrapStartOptions(env, startOptionsObj, startOptions); + } + TAG_LOGE(AAFwkTag::CONTEXT, "displayId:%{public}d", startOptions.GetDisplayID()); + std::string startTime = std::to_string( + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count()); + ani_ref callbackRef = nullptr; + env->GlobalReference_Create(callback, &callbackRef); + ani_vm *etsVm = nullptr; + ani_status status = ANI_ERROR; + if ((status = env->GetVM(&etsVm)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return; + } + RuntimeTask task = [etsVm, callbackRef, element = want.GetElement(), flags = want.GetFlags(), startTime]( + int resultCode, const AAFwk::Want &want, bool isInner) { + TAG_LOGD(AAFwkTag::CONTEXT, "start async callback"); + ani_status status = ANI_ERROR; + ani_env *env = nullptr; + if ((status = etsVm->GetEnv(ANI_VERSION_1, &env)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "status: %{public}d", status); + return; + } + std::string bundleName = element.GetBundleName(); + std::string abilityName = element.GetAbilityName(); + ani_object abilityResult = AppExecFwk::WrapAbilityResult(env, resultCode, want); + if (abilityResult == nullptr) { + TAG_LOGW(AAFwkTag::CONTEXT, "null abilityResult"); + isInner = true; + resultCode = ERR_INVALID_VALUE; + } + auto errCode = isInner ? resultCode : 0; + AsyncCallback(env, reinterpret_cast(callbackRef), + OHOS::AbilityRuntime::CreateEtsErrorByNativeErr(env, errCode), abilityResult); + }; + auto requestCode = GenerateRequestCode(); + (startOptionsObj == nullptr) ? context->StartAbilityForResult(want, requestCode, std::move(task)) + : context->StartAbilityForResult(want, startOptions, requestCode, std::move(task)); + return; +} + +void EtsAbilityContext::OnTerminateSelf(ani_env *env, ani_object aniObj, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env or aniObj"); + ani_object aniObject = CreateEtsInvalidParamError(env, "env null"); + AsyncCallback(env, callback, aniObject, nullptr); + return; + } + ani_object aniObject = nullptr; + auto context = EtsAbilityContext::GetAbilityContext(env, aniObj); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + aniObject = CreateEtsInvalidParamError(env, "context null"); + AsyncCallback(env, callback, aniObject, nullptr); + return; + } + ErrCode ret = context->TerminateSelf(); + if (ret == static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT) || ret == ERR_OK) { + aniObject = CreateEtsError(env, static_cast(ret)); + } else { + aniObject = CreateEtsErrorByNativeErr(env, static_cast(ret)); + } + AsyncCallback(env, callback, aniObject, nullptr); +} + +void EtsAbilityContext::OnTerminateSelfWithResult( + ani_env *env, ani_object aniObj, ani_object abilityResult, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + ani_object aniObject = CreateEtsInvalidParamError(env, "env null"); + AsyncCallback(env, callback, aniObject, nullptr); + return; + } + ani_object aniObject = nullptr; + auto context = EtsAbilityContext::GetAbilityContext(env, aniObj); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "GetAbilityContext is nullptr"); + aniObject = CreateEtsInvalidParamError(env, "context null"); + AsyncCallback(env, callback, aniObject, nullptr); + return; + } + AAFwk::Want want; + int resultCode = 0; + OHOS::AppExecFwk::UnWrapAbilityResult(env, abilityResult, resultCode, want); + context->SetTerminating(true); + ErrCode ret = context->TerminateAbilityWithResult(want, resultCode); + if (ret == static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT) || ret == ERR_OK) { + aniObject = CreateEtsError(env, static_cast(ret)); + } else { + aniObject = CreateEtsErrorByNativeErr(env, static_cast(ret)); + } + AsyncCallback(env, callback, aniObject, nullptr); +} + +void EtsAbilityContext::OnReportDrawnCompleted(ani_env *env, ani_object aniObj, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + ani_object aniObject = CreateEtsInvalidParamError(env, "env null"); + AsyncCallback(env, callback, aniObject, nullptr); + return; + } + ani_object aniObject = nullptr; + auto context = GetAbilityContext(env, aniObj); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "context null"); + aniObject = CreateEtsInvalidParamError(env, "context null"); + AsyncCallback(env, callback, aniObject, nullptr); + return; + } + ErrCode ret = context->ReportDrawnCompleted(); + if (ret == ERR_OK) { + aniObject = CreateEtsError(env, static_cast(ret)); + } else { + aniObject = CreateEtsErrorByNativeErr(env, static_cast(ret)); + } + AsyncCallback(env, callback, aniObject, nullptr); +} + +namespace { +bool BindNativeMethods(ani_env *env, ani_class &cls) +{ + ani_status status = env->FindClass(UI_ABILITY_CONTEXT_CLASS_NAME, &cls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "FindClass status: %{public}d", status); + return false; + } + std::call_once(g_bindNativeMethodsFlag, [&status, env, cls]() { + std::array functions = { + ani_native_function { "nativeStartAbilitySync", + "L@ohos/app/ability/Want/Want;Lapplication/UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::StartAbility) }, + ani_native_function { "nativeStartAbilitySync", + "L@ohos/app/ability/Want/Want;L@ohos/app/ability/StartOptions/StartOptions;Lapplication/" + "UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::StartAbilityWithOptions) }, + ani_native_function { "nativeStartAbilityForResult", + "L@ohos/app/ability/Want/Want;Lapplication/UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::StartAbilityForResult) }, + ani_native_function { "nativeStartAbilityForResult", + "L@ohos/app/ability/Want/Want;L@ohos/app/ability/StartOptions/StartOptions;Lapplication/" + "UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::StartAbilityForResultWithOptions) }, + ani_native_function { "nativeTerminateSelfSync", "Lapplication/UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::TerminateSelf) }, + ani_native_function { "nativeTerminateSelfWithResult", + "Lability/abilityResult/AbilityResult;Lapplication/UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::TerminateSelfWithResult) }, + ani_native_function { "nativeReportDrawnCompletedSync", + "Lapplication/UIAbilityContext/AsyncCallbackWrapper;:V", + reinterpret_cast(EtsAbilityContext::ReportDrawnCompleted) }, + }; + status = env->Class_BindNativeMethods(cls, functions.data(), functions.size()); + }); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Class_BindNativeMethods status: %{public}d", status); + return false; + } + return true; +} +} // namespace + +ani_object CreateEtsAbilityContext( + ani_env *env, const std::shared_ptr &context, const std::shared_ptr &application) +{ + if (env == nullptr || context == nullptr || application == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env or context or application"); + return nullptr; + } + ani_class cls = nullptr; + if (!BindNativeMethods(env, cls)) { + TAG_LOGE(AAFwkTag::CONTEXT, "BindNativeMethods failed"); + return nullptr; + } + ani_object contextObj = EtsAbilityContext::SetAbilityContext(env, context); + if (contextObj == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null contextObj"); + return nullptr; + } + ContextUtil::CreateEtsBaseContext(env, cls, contextObj, context); + + auto abilityInfo = context->GetAbilityInfo(); + if (abilityInfo == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null abilityInfo"); + return nullptr; + } + ani_ref abilityInfoRef = AppExecFwk::CommonFunAni::ConvertAbilityInfo(env, *abilityInfo); + ani_status status = env->Object_SetFieldByName_Ref(contextObj, "abilityInfo", abilityInfoRef); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_SetFieldByName_Ref status: %{public}d", status); + return nullptr; + } + + auto configuration = context->GetConfiguration(); + if (configuration == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null configuration"); + return nullptr; + } + ani_object configurationObj = OHOS::AppExecFwk::WrapConfiguration(env, *configuration); + if ((status = env->Object_SetFieldByName_Ref(contextObj, "config", configurationObj)) != ANI_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "Object_SetFieldByName_Ref status: %{public}d", status); + return nullptr; + } + return contextObj; +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/native/ability/native/ability_runtime/ets_ui_ability.cpp b/frameworks/native/ability/native/ability_runtime/ets_ui_ability.cpp index 4ea83ccfef5f1e814f039124a44dec0e591e1640..10bf994a197e926ccdcc00b839d07f17c6d36756 100644 --- a/frameworks/native/ability/native/ability_runtime/ets_ui_ability.cpp +++ b/frameworks/native/ability/native/ability_runtime/ets_ui_ability.cpp @@ -21,6 +21,7 @@ #include "app_recovery.h" #include "connection_manager.h" #include "display_util.h" +#include "ets_ability_context.h" #include "ets_data_struct_converter.h" #include "hilog_tag_wrapper.h" #include "hitrace_meter.h" @@ -187,6 +188,35 @@ void EtsUIAbility::SetAbilityContext(std::shared_ptr abilityInfo, s TAG_LOGE(AAFwkTag::UIABILITY, "null etsAbilityObj_ or abilityContext_ or want"); return; } + int32_t screenMode = want->GetIntParam(AAFwk::SCREEN_MODE_KEY, AAFwk::ScreenMode::IDLE_SCREEN_MODE); + CreateEtsContext(screenMode, application); +} + +void EtsUIAbility::CreateEtsContext(int32_t screenMode, const std::shared_ptr &application) +{ + auto env = etsRuntime_.GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null env"); + return; + } + if (screenMode == AAFwk::IDLE_SCREEN_MODE) { + ani_object contextObj = CreateEtsAbilityContext(env, abilityContext_, application); + if (contextObj == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null contextObj"); + return; + } + ani_ref contextGlobalRef = nullptr; + env->GlobalReference_Create(contextObj, &contextGlobalRef); + ani_status status = env->Object_SetFieldByName_Ref(etsAbilityObj_->aniObj, "context", contextGlobalRef); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::UIABILITY, "Object_SetFieldByName_Ref status: %{public}d", status); + return; + } + shellContextRef_ = std::make_shared(); + shellContextRef_->aniObj = contextObj; + shellContextRef_->aniRef = contextGlobalRef; + } + // to be done: CreateAniEmbeddableUIAbilityContext } void EtsUIAbility::OnStart(const Want &want, sptr sessionInfo) diff --git a/interfaces/kits/native/ability/native/ability_runtime/ets_ability_context.h b/interfaces/kits/native/ability/native/ability_runtime/ets_ability_context.h new file mode 100644 index 0000000000000000000000000000000000000000..6185058833858447880719f9e8f0f76f8aa8ba8f --- /dev/null +++ b/interfaces/kits/native/ability/native/ability_runtime/ets_ability_context.h @@ -0,0 +1,65 @@ +/* + * 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_SIMULATOR_ETS_ABILITY_CONTEXT_H +#define OHOS_ABILITY_RUNTIME_SIMULATOR_ETS_ABILITY_CONTEXT_H + +#include "ability_context.h" +#include "configuration.h" +#include "ets_runtime.h" +#include "ohos_application.h" + +namespace OHOS { +namespace AbilityRuntime { +using OHOSApplication = AppExecFwk::OHOSApplication; +class EtsAbilityContext final { +public: + static EtsAbilityContext &GetInstance() + { + static EtsAbilityContext instance; + return instance; + } + static void StartAbility(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object call); + static void StartAbilityWithOptions( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object opt, ani_object call); + static void StartAbilityForResult(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object callback); + static void StartAbilityForResultWithOptions( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object startOptionsObj, ani_object callback); + static void TerminateSelf(ani_env *env, ani_object aniObj, ani_object callback); + static void TerminateSelfWithResult(ani_env *env, ani_object aniObj, ani_object abilityResult, ani_object callback); + static void ReportDrawnCompleted(ani_env *env, ani_object aniObj, ani_object call); + + static ani_object SetAbilityContext(ani_env *env, const std::shared_ptr &context); + static std::shared_ptr GetAbilityContext(ani_env *env, ani_object aniObj); + +private: + static bool AsyncCallback(ani_env *env, ani_object call, ani_object error, ani_object result); + void InheritWindowMode(ani_env *env, ani_object aniObj, AAFwk::Want &want); + void OnStartAbility(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object opt, ani_object call); + void OnStartAbilityForResult( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_object startOptionsObj, ani_object callback); + void OnTerminateSelf(ani_env *env, ani_object aniObj, ani_object callback); + void OnTerminateSelfWithResult(ani_env *env, ani_object aniObj, ani_object abilityResult, ani_object callback); + void OnReportDrawnCompleted(ani_env *env, ani_object aniObj, ani_object call); + int32_t GenerateRequestCode(); + + static std::mutex requestCodeMutex_; +}; + +ani_object CreateEtsAbilityContext( + ani_env *env, const std::shared_ptr &context, const std::shared_ptr &application); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SIMULATOR_ETS_ABILITY_CONTEXT_H diff --git a/interfaces/kits/native/ability/native/ability_runtime/ets_ui_ability.h b/interfaces/kits/native/ability/native/ability_runtime/ets_ui_ability.h index cecb7e7a835c7cfd6d908adde75cc8033c6135ab..53cb348a6efc5d750eef7f187b81471fc637a01d 100644 --- a/interfaces/kits/native/ability/native/ability_runtime/ets_ui_ability.h +++ b/interfaces/kits/native/ability/native/ability_runtime/ets_ui_ability.h @@ -156,6 +156,7 @@ private: void DoOnForegroundForSceneIsNull(const Want &want); void UpdateAbilityObj(std::shared_ptr abilityInfo, const std::string &moduleName, const std::string &srcPath); + void CreateEtsContext(int32_t screenMode, const std::shared_ptr &application); bool BindNativeMethods(); ETSRuntime &etsRuntime_;