diff --git a/frameworks/ets/ani/BUILD.gn b/frameworks/ets/ani/BUILD.gn index bb6c0efc9eebadaf386e3c5ae3deec9b2440268b..a99978ecb3d89bb2f9b66f0ead13b5bd91319659 100644 --- a/frameworks/ets/ani/BUILD.gn +++ b/frameworks/ets/ani/BUILD.gn @@ -32,6 +32,7 @@ group("ani_packages") { "${ability_runtime_path}/frameworks/ets/ani/native_constructor:context_ani", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_callback:ani_ui_extension_callback", "${ability_runtime_path}/frameworks/ets/ani/uri_permission_manager:uri_permission_manager_ani_kit", + "${ability_runtime_path}/frameworks/ets/ani/want:want_ani_kit", "${ability_runtime_path}/frameworks/ets/ani/wantagent:aniwantagent", "${ability_runtime_path}/frameworks/ets/ani/apprecovery:appRecovery_ani", ] diff --git a/frameworks/ets/ani/ani_common/src/ani_common_want.cpp b/frameworks/ets/ani/ani_common/src/ani_common_want.cpp index 0ef32751c5e89c9a02b764ce18f53b0a01d5c31c..c73cd3aaf89a8b5bbba7567ad5df1f20b9da27e5 100644 --- a/frameworks/ets/ani/ani_common/src/ani_common_want.cpp +++ b/frameworks/ets/ani/ani_common/src/ani_common_want.cpp @@ -241,7 +241,17 @@ bool UnwrapWant(ani_env *env, ani_object param, AAFwk::Want &want) AAFwk::WantParams wantParams; if (InnerUnwrapWantParams(env, param, wantParams)) { want.SetParams(wantParams); + wantParams.DumpInfo(0); } + + TAG_LOGI(AAFwkTag::ANI, "start InnerUnwrapWantParams"); + AAFwk::WantParams wantParams1; + for (int i = 0; i < 1000; i++) { + if (!InnerUnwrapWantParams(env, param, wantParams1)) { + TAG_LOGI(AAFwkTag::ANI, "InnerUnwrapWantParams failed, times%{public}d", i); + } + } + TAG_LOGI(AAFwkTag::ANI, "end InnerUnwrapWantParams"); return true; } @@ -267,27 +277,29 @@ bool UnwrapWantParams(ani_env *env, ani_ref param, AAFwk::WantParams &wantParams TAG_LOGE(AAFwkTag::ANI, "failed to get stringifyNoThrow method, status: %{public}d", status); return false; } - ani_ref wantParamsAniString; - status = env->Class_CallStaticMethod_Ref(cls, stringifyMethod, &wantParamsAniString, param); - if (status != ANI_OK) { + //ani_ref wantParamsAniString; + ani_boolean isSuccess; + ani_long wantParamsLong = reinterpret_cast(&wantParams); + status = env->Class_CallStaticMethod_Boolean(cls, stringifyMethod, &isSuccess, param, wantParamsLong); + if (status != ANI_OK || isSuccess != ANI_TRUE) { TAG_LOGE(AAFwkTag::ANI, "failed to call stringifyNoThrow method, status: %{public}d", status); return false; } - std::string wantParamsString; - if (!GetStdString(env, reinterpret_cast(wantParamsAniString), wantParamsString)) { - TAG_LOGE(AAFwkTag::ANI, "GetStdString failed"); - return false; - } - if (wantParamsString.empty()) { - TAG_LOGE(AAFwkTag::ANI, "wantParamsString empty"); - return false; - } - nlohmann::json wantParamsJson = nlohmann::json::parse(wantParamsString, nullptr, false); - if (wantParamsJson.is_discarded()) { - TAG_LOGE(AAFwkTag::ANI, "Failed to parse json string"); - return false; - } - from_json(wantParamsJson, wantParams); + //std::string wantParamsString; + //if (!GetStdString(env, reinterpret_cast(wantParamsAniString), wantParamsString)) { + // TAG_LOGE(AAFwkTag::ANI, "GetStdString failed"); + // return false; + //} + //if (wantParamsString.empty()) { + // TAG_LOGE(AAFwkTag::ANI, "wantParamsString empty"); + // return false; + //} + //nlohmann::json wantParamsJson = nlohmann::json::parse(wantParamsString, nullptr, false); + //if (wantParamsJson.is_discarded()) { + // TAG_LOGE(AAFwkTag::ANI, "Failed to parse json string"); + // return false; + //} + //from_json(wantParamsJson, wantParams); return true; } diff --git a/frameworks/ets/ani/app/ability_context/include/ets_ability_context_module.h b/frameworks/ets/ani/app/ability_context/include/ets_ability_context_module.h index 30a77115e8d9bdd4ae1085791693326891f3d9e6..46d7f78c5eb20d44f51ebabc60c40dce2dbd0ae5 100644 --- a/frameworks/ets/ani/app/ability_context/include/ets_ability_context_module.h +++ b/frameworks/ets/ani/app/ability_context/include/ets_ability_context_module.h @@ -17,8 +17,8 @@ #define OHOS_ABILITY_RUNTIME_ETS_ABILITY_CONTEXT_MODULE_H #include -#include "ani.h" #include "ability_context.h" +#include "ani.h" #include "native_engine/native_engine.h" namespace OHOS { diff --git a/frameworks/ets/ani/ui_ability/include/ets_ability_context.h b/frameworks/ets/ani/ui_ability/include/ets_ability_context.h index 4569545fc867a78fca33090d96567ce4aa31b00e..7b724b2ad741ead3133b9d4f5feccf985ab1118f 100644 --- a/frameworks/ets/ani/ui_ability/include/ets_ability_context.h +++ b/frameworks/ets/ani/ui_ability/include/ets_ability_context.h @@ -92,8 +92,6 @@ public: static void Clean(ani_env *env, ani_object object); static ani_object SetEtsAbilityContext(ani_env *env, std::shared_ptr context); static EtsAbilityContext *GetEtsAbilityContext(ani_env *env, ani_object aniObj); - static ani_object NativeTransferStatic(ani_env *env, ani_object aniObj, ani_object input); - static ani_object NativeTransferDynamic(ani_env *env, ani_object aniObj, ani_object input); static bool IsInstanceOf(ani_env *env, ani_object aniObj); private: diff --git a/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp b/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp index 378cf7218a7a6f76ddb150c422de4d76906cc6ba..69e5866b49ab9f5ba6362c251988add26b5b60fc 100644 --- a/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp +++ b/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp @@ -1173,85 +1173,6 @@ void EtsAbilityContext::ConfigurationUpdated(ani_env *env, std::shared_ptr *>(unwrapResult)->lock(); - if (context == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null context"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto abilityContext = Context::ConvertTo(context); - if (abilityContext == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null abilityContext"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto &bindingObj = abilityContext->GetBindingObject(); - if (bindingObj == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null bindingObj"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto staticContext = bindingObj->Get(); - if (staticContext == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null staticContext"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - return reinterpret_cast(*staticContext); -} - -ani_object EtsAbilityContext::NativeTransferDynamic(ani_env *env, ani_object, ani_object input) -{ - TAG_LOGD(AAFwkTag::UIABILITY, "transfer dynamic UIAbilityContext"); - if (!IsInstanceOf(env, input)) { - TAG_LOGE(AAFwkTag::UIABILITY, "not UIAbilityContext"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto context = ContextUtil::GetBaseContext(env, input); - if (context == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null context"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto abilityContext = Context::ConvertTo(context); - if (abilityContext == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null abilityContext"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto &bindingObj = abilityContext->GetBindingObject(); - if (bindingObj == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null bindingObj"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - auto dynamicContext = bindingObj->Get(); - if (dynamicContext == nullptr) { - TAG_LOGE(AAFwkTag::UIABILITY, "null dynamicContext"); - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; - } - // Not support yet - EtsErrorUtil::ThrowEtsTransferClassError(env); - return nullptr; -} - bool EtsAbilityContext::IsInstanceOf(ani_env *env, ani_object aniObj) { ani_class cls {}; diff --git a/frameworks/ets/ani/want/BUILD.gn b/frameworks/ets/ani/want/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e4f6e51d78bddf6975dbbf9db776d84f4c34f4e3 --- /dev/null +++ b/frameworks/ets/ani/want/BUILD.gn @@ -0,0 +1,56 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_shared_library("want_ani_kit") { + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + branch_protector_ret = "pac_ret" + + include_dirs = [ + "./include", + "${ability_runtime_services_path}/common/include", + ] + + sources = [ "src/ani_want_module.cpp" ] + + deps = [ + "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + ] + + external_deps = [ + "ability_base:want", + "ability_base:base", + "c_utils:utils", + "hilog:libhilog", + "runtime_core:ani", + ] + + cflags_cc = [] + if (os_dlp_part_enabled) { + cflags_cc += [ "-DWITH_DLP" ] + } + + innerapi_tags = [ "platformsdk" ] + subsystem_name = "ability" + part_name = "ability_runtime" +} diff --git a/frameworks/ets/ani/want/include/ani_want_module.h b/frameworks/ets/ani/want/include/ani_want_module.h new file mode 100644 index 0000000000000000000000000000000000000000..a8d97443e3f2fc79b1a3520564bd89e15118fc4e --- /dev/null +++ b/frameworks/ets/ani/want/include/ani_want_module.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_ABILITY_RUNTIME_ANI_WANT_MODULE_H +#define OHOS_ABILITY_RUNTIME_ANI_WANT_MODULE_H + +#include "ani.h" + +namespace OHOS::AppExecFwk { +class EtsWantParams { +public: + EtsWantParams() = default; + ~EtsWantParams() = default; + + static ani_long NativeCreate(ani_env *env, ani_object); + static void NativeDestroy(ani_env *env, ani_object, ani_long nativeWantParams); + static ani_boolean NativeSetStringParam(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_string value); + static ani_boolean NativeSetNumberParam(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_double value); + static ani_boolean NativeSetIntParam(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_int value); + static ani_boolean NativeSetWantParams(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_long value); +}; +} // namespace OHOS::AppExecFwk +#endif // OHOS_ABILITY_RUNTIME_ANI_WANT_MODULE_H diff --git a/frameworks/ets/ani/want/src/ani_want_module.cpp b/frameworks/ets/ani/want/src/ani_want_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2c63663d1d9292cb6e8e71466bc59dad0b589c0 --- /dev/null +++ b/frameworks/ets/ani/want/src/ani_want_module.cpp @@ -0,0 +1,261 @@ +/* + * 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 "ani_want_module.h" + +#include "ani_common_util.h" +#include "double_wrapper.h" +#include "hilog_tag_wrapper.h" +#include "int_wrapper.h" +#include "string_wrapper.h" +#include "want_params.h" +#include "want_params_wrapper.h" + +namespace OHOS::AppExecFwk { +namespace { +constexpr const char *ETS_NATIVE_WANT_PARAMS_CLASS_NAME = "L@ohos/app/ability/Want/NativeWantParams;"; +constexpr const char *ETS_NATIVE_WANT_PARAMS_CLEANER_CLASS_NAME = "L@ohos/app/ability/Want/NativeWantParamsCleaner;"; +} // namespace + +ani_long EtsWantParams::NativeCreate(ani_env *env, ani_object) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null env"); + return 0; + } + auto *params = new(std::nothrow) AAFwk::WantParams(); + if (params == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null params"); + return 0; + } + + return reinterpret_cast(params); +} + +void EtsWantParams::NativeDestroy(ani_env *env, ani_object, ani_long nativeWantParams) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null env"); + return; + } + + auto *params = reinterpret_cast(nativeWantParams); + if (params == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null nativeWantParams"); + return; + } + + delete params; +} + +ani_boolean EtsWantParams::NativeSetStringParam(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_string value) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null env"); + return false; + } + + auto *params = reinterpret_cast(nativeWantParams); + if (params == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null nativeWantParams"); + return false; + } + + std::string keyString; + if (!GetStdString(env, key, keyString)) { + TAG_LOGE(AAFwkTag::ANI, "get key failed"); + return false; + } + std::string valueString; + if (!GetStdString(env, value, valueString)) { + TAG_LOGE(AAFwkTag::ANI, "get key failed"); + return false; + } + params->SetParam(keyString, AAFwk::String::Box(valueString)); + return true; +} + +ani_boolean EtsWantParams::NativeSetNumberParam(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_double value) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null env"); + return false; + } + + auto *params = reinterpret_cast(nativeWantParams); + if (params == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null nativeWantParams"); + return false; + } + + std::string keyString; + if (!GetStdString(env, key, keyString)) { + TAG_LOGE(AAFwkTag::ANI, "get key failed"); + return false; + } + + params->SetParam(keyString, AAFwk::Double::Box(value)); + return true; +} + +ani_boolean EtsWantParams::NativeSetIntParam(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_int value) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null env"); + return false; + } + + auto *params = reinterpret_cast(nativeWantParams); + if (params == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null nativeWantParams"); + return false; + } + + std::string keyString; + if (!GetStdString(env, key, keyString)) { + TAG_LOGE(AAFwkTag::ANI, "get key failed"); + return false; + } + + params->SetParam(keyString, AAFwk::Integer::Box(value)); + return true; +} + +ani_boolean EtsWantParams::NativeSetWantParams(ani_env *env, ani_object, ani_long nativeWantParams, ani_string key, + ani_long value) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null env"); + return false; + } + + auto *params = reinterpret_cast(nativeWantParams); + if (params == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null nativeWantParams"); + return false; + } + + std::string keyString; + if (!GetStdString(env, key, keyString)) { + TAG_LOGE(AAFwkTag::ANI, "get key failed"); + return false; + } + + auto *valueWantParams = reinterpret_cast(value); + if (valueWantParams == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null valueWantParams"); + return false; + } + + params->SetParam(keyString, AAFwk::WantParamWrapper::Box(*valueWantParams)); + return true; +} + +ani_status BindNativeFunctions(ani_env *aniEnv) +{ + TAG_LOGD(AAFwkTag::WANT, "call"); + if (aniEnv == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null ani env"); + return ANI_INVALID_ARGS; + } + + ani_class nativeWantParamsCls = nullptr; + auto status = aniEnv->FindClass(ETS_NATIVE_WANT_PARAMS_CLASS_NAME, &nativeWantParamsCls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::WANT, "FindClass NativeWantParams failed status: %{public}d", status); + return status; + } + + std::array nativeFuncs = { + ani_native_function{ + "nativeCreate", ":J", + reinterpret_cast(EtsWantParams::NativeCreate) + }, + ani_native_function{ + "nativeSetStringParam", "JLstd/core/String;Lstd/core/String;:Z", + reinterpret_cast(EtsWantParams::NativeSetStringParam) + }, + ani_native_function{ + "nativeSetNumberParam", "JLstd/core/String;D:Z", + reinterpret_cast(EtsWantParams::NativeSetNumberParam) + }, + ani_native_function{ + "nativeSetIntParam", "JLstd/core/String;I:Z", + reinterpret_cast(EtsWantParams::NativeSetIntParam) + }, + ani_native_function{ + "nativeSetWantParams", "JLstd/core/String;J:Z", + reinterpret_cast(EtsWantParams::NativeSetWantParams) + }, + }; + status = aniEnv->Class_BindNativeMethods(nativeWantParamsCls, nativeFuncs.data(), nativeFuncs.size()); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::WANT, "Class_BindNativeMethods failed status: %{public}d", status); + return status; + } + + ani_class nativeWantParamsCleanerCls = nullptr; + status = aniEnv->FindClass(ETS_NATIVE_WANT_PARAMS_CLEANER_CLASS_NAME, &nativeWantParamsCleanerCls); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::WANT, "FindClass NativeWantParams failed status: %{public}d", status); + return status; + } + std::array cleanerNativeFuncs = { + ani_native_function{ + "nativeDestroy", "J:V", + reinterpret_cast(EtsWantParams::NativeDestroy) + }, + }; + status = aniEnv->Class_BindNativeMethods(nativeWantParamsCleanerCls, cleanerNativeFuncs.data(), + cleanerNativeFuncs.size()); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::WANT, "Class_BindNativeMethods failed status: %{public}d", status); + return status; + } + return ANI_OK; +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + TAG_LOGD(AAFwkTag::WANT, "ANI_Constructor"); + ani_env *env = nullptr; + if (vm == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "null vm"); + return ANI_NOT_FOUND; + } + ani_status status = vm->GetEnv(ANI_VERSION_1, &env); + if (status != ANI_OK || env == nullptr) { + TAG_LOGE(AAFwkTag::WANT, "GetEnv failed status: %{public}d, or null env", status); + return status; + } + if ((status = BindNativeFunctions(env)) != ANI_OK) { + TAG_LOGE(AAFwkTag::WANT, "BindNativeFunctions failed status: %{public}d", status); + return status; + } + *result = ANI_VERSION_1; + return ANI_OK; +} +} +} // namespace OHOS::AppExecFwk diff --git a/frameworks/ets/ets/@ohos.app.ability.Want.ets b/frameworks/ets/ets/@ohos.app.ability.Want.ets index 3d0cbd3d0084e838af8d128476217b44c9005d6c..90554bbe77c60b028727f10832968cc37b72abf5 100644 --- a/frameworks/ets/ets/@ohos.app.ability.Want.ets +++ b/frameworks/ets/ets/@ohos.app.ability.Want.ets @@ -14,18 +14,48 @@ */ import hilog from '@ohos.hilog' +import rpc from '@ohos.rpc'; type valueType = NullishType; const DOMAIN_ID = 0xD001300; const TAG = 'WantSerializeTool'; +const int32Max: number = Math.pow(2, 31) - 1; +const int32Min: number = -Math.pow(2, 31); + +const PARAM_RESV_WINDOW_MODE = 'ohos.aafwk.param.windowMode'; +const PARAM_RESV_DISPLAY_ID = 'ohos.aafwk.param.displayId'; + +interface ComplexArrayData { + intlist: Array; + boolList: Array; + doubleList: Array; + stringList: Array; + objectList: Array; +} class RecordWriter { private buffer = new StringBuilder(); private store = new Set(); + private wantParams: NativeWantParams | null = null; + + constructor(wantParams: long) { + this.wantParams = new NativeWantParams(wantParams); + } - public write(obj: Object): String { - this.writeObject(obj); - return this.buffer.toString(); + public write(obj: Object): boolean { + return this.parseRecord(obj); + // return this.buffer.toString(); + } + + private parseRecord(obj: NullishType): boolean { + if (obj instanceof Record) { + this.writeRecordToNative(obj as Object as Record, this.wantParams); + return true; + //return this.writeRecord(obj as Object as Record); + } else { + hilog.error(DOMAIN_ID, TAG, `input object not record type`); + return false; + } } private writeObject(obj: NullishType): void { @@ -51,6 +81,113 @@ class RecordWriter { } } + private tupleToArray(tuple: Tuple): Array { + let arr: Array = []; + + const objType = Type.of(tuple as Object) as ClassType; + const writableFields = this.getWritableFields(objType, reflect.Value.of(tuple as Object) as ClassValue); + for (let fieldIdx = 0; fieldIdx < writableFields.length; fieldIdx++) { + const fieldValue = writableFields[fieldIdx][1].getData(); + arr.push(fieldValue); + } + return arr; + } + + private writeObjectToNative(key: string, obj: NullishType, wantParams?: NativeWantParams | null): boolean { + let localWantParams = (wantParams === undefined) ? this.wantParams : wantParams; + // skip reserved param + if (this.blackListFilter(key)) { + //hilog.debug(DOMAIN_ID, TAG, `%{public}s is filtered.`, key); + hilog.error(DOMAIN_ID, TAG, `%{public}s is filtered.`, key); + return true; + } + if (obj === null) { + //this.buffer.append('null'); + //localWantParams?.setParam(key, 'null'); //1.1不收集null + return true; + } else if (obj === undefined) { + //this.buffer.append('undefined'); + //localWantParams?.setParam(key, 'undefined'); //1.1不收集undefined + return true; + } else if (obj instanceof String) { + //this.buffer.append(JSON.stringify(obj as String)); + localWantParams?.setParam(key, obj as String); + return true; + } else if (this.writeValueTypeToNative(localWantParams, key, obj as Object)) { + // nothing to do + return true; + } else if (obj instanceof Array) { + this.writeArrayToNative(key, obj as Object as Array, localWantParams); + return true; + } else if (obj instanceof Tuple) { + let tupleAsArray = this.tupleToArray(obj as Tuple); + this.writeArrayToNative(key, tupleAsArray as Object as Array, localWantParams); + return true; + } else if (obj instanceof Record) { + //this.writeRecord(obj as Object as Record); + let newWantParams = new NativeWantParams(); + if (this.writeRecordToNative(obj as Object as Record, newWantParams) != true) { + hilog.error(DOMAIN_ID, TAG, `write record to native failed`); + localWantParams?.setParam(key, '{}'); // when record is {} + } else { + localWantParams?.setParam(key, newWantParams); + } + return true; + else if (obj instanceof rpc.RemoteObject) { + localWantParams?.setParam(key, obj as rpc.RemoteObject); + return true; + } else { + const objType = Type.of(obj); + if (objType instanceof ClassType) { + // enum value are string + if (typeof obj === 'string') { + let value: string = String(obj as Object); + localWantParams?.setParam(key, value); + return true; + } + // enum value are number + if (typeof obj === 'number') { + //let num: number = Number(obj.unboxed()); + let value: string = String(obj as Object); + let num: number = Number(value); + if (Number.isInteger(num)) { + if (num > int32Min && num < int32Max) { + localWantParams?.setParam(key, num as int); + } else { + localWantParams?.setParam(key, num as double); + } + } else { + localWantParams?.setParam(key, num as double); + } + return true; + } + let newWantParams = new NativeWantParams(); + if (this.writeClassValueToNative(obj as Object, objType as ClassType, newWantParams) != true) { + localWantParams?.setParam(key, '{}'); // when class no write member + } else { + localWantParams?.setParam(key, newWantParams); + } + return true; + } else { + //this.buffer.append('null'); + //localWantParams?.setParam(key, 'null'); // 1.1不识别类型不收集 + hilog.error(DOMAIN_ID, TAG, `object type not support parse, key: %{public}s`, key); + } + return true; + } + return false; + } + + private blackListFilter(key: string): boolean { + if (key === PARAM_RESV_WINDOW_MODE) { + return true; + } + if (key === PARAM_RESV_DISPLAY_ID) { + return true; + } + return false; + } + private writeValueType(obj: Object): boolean { if (obj instanceof Boolean) { this.buffer.append(JSON.stringify(obj.unboxed())); @@ -84,6 +221,51 @@ class RecordWriter { } } + private writeValueTypeToNative(wantParams: NativeWantParams | null, key: string, obj: Object): boolean { + if (obj instanceof Boolean) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (typeof obj === 'boolean') { + wantParams?.setParam(key, obj as boolean); + return true; + } else if (obj instanceof Byte) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof Char) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof Short) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof Int) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof Long) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof Float) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof Double) { + //this.buffer.append(JSON.stringify(obj.unboxed())); + wantParams?.setParam(key, obj.unboxed()); + return true; + } else if (obj instanceof BigInt) { + //this.buffer.append(JSON.stringify(obj)); + //wantParams?.setParam(key, obj.toString()); // 1.1不收集BigInt类型 + return true; + } else { + return false; + } + } + private writeArray(arr: Array): void { this.buffer.append('['); const length = arr.length as int; @@ -103,6 +285,169 @@ class RecordWriter { this.buffer.append(']'); } + private unwrapArrayElementNumber(element: valueType, complexArray: ComplexArrayData): void { + if (element instanceof Int) { + if (complexArray.doubleList.length > 0) { + //complexArray.doubleList.push(...complexArray.intlist.map(num => Number(num))); + complexArray.intlist.forEach(num => { + complexArray.doubleList.push(Number(num)); + }); + complexArray.intlist = []; + complexArray.doubleList.push((element as number).unboxed()); + } else { + complexArray.intlist.push((element as int).unboxed()); + } + } else if (element instanceof Long) { + // consistent with napi behavior + const doubleValue = Number(element.unboxed()); + if (complexArray.intlist.length > 0) { + //complexArray.doubleList.push(...complexArray.intlist.map(num => Number(num))); + complexArray.intlist.forEach(num => { + complexArray.doubleList.push(Number(num)); + }); + complexArray.intlist = []; + } + complexArray.doubleList.push(doubleValue); + } else if (element instanceof Double) { + if (complexArray.intlist.length > 0) { + //complexArray.doubleList.push(...complexArray.intlist.map(num => Number(num))); + complexArray.intlist.forEach(num => { + complexArray.doubleList.push(Number(num)); + }); + complexArray.intlist = []; + } + complexArray.doubleList.push((element as number).unboxed()); + } else { + const objType = Type.of(element); + // enum value are number + if (objType instanceof ClassType) { + let value: string = String(element as Object); + let enumNum: number = Number(value); + if (Number.isInteger(enumNum) && enumNum > int32Min && enumNum < int32Max) { + if (complexArray.doubleList.length > 0) { + complexArray.intlist.forEach(num => { + complexArray.doubleList.push(Number(num)); + }); + complexArray.intlist = []; + complexArray.doubleList.push(enumNum as double); + } else { + complexArray.intlist.push(enumNum as int); + } + } else { + if (complexArray.intlist.length > 0) { + complexArray.intlist.forEach(num => { + complexArray.doubleList.push(Number(num)); + }); + complexArray.intlist = []; + } + complexArray.doubleList.push(enumNum as double); + } + } + } + } + + private unwrapArray(arr: Array, complexArray: ComplexArrayData): void { + const length = arr.length as int; + this.checkReferencesCycle(arr); + this.store.add(arr); + for (let idx = 0; idx < length; idx++) { + const element = arr[idx]; + if (element === null || element === undefined) { + continue; + } + const valueType = typeof element; + switch (valueType) { + case 'string': + if (element instanceof String) { + complexArray.stringList.push(element as string); + } else { + const objType = Type.of(element); + if (objType instanceof ClassType) { + let value: string = String(element as Object); + complexArray.stringList.push(value as string); + } + } + break; + case 'boolean': + complexArray.boolList.push((element as Boolean).unboxed()); + break; + case 'number': + try { + this.unwrapArrayElementNumber(element, complexArray); + } catch { + hilog.error(DOMAIN_ID, TAG, `unwrapArray number error: ${err}`); + } + break; + case 'object': + complexArray.objectList.push(element); + break; + default: + hilog.error(DOMAIN_ID, TAG, `unknown array value type: %{public}s`, JSON.stringify(arr[idx])); + break; + } + } + this.store.delete(arr); + } + + private handleArrayObject(obj: object, wantParams: NativeWantParams): boolean { + if (Array.isArray(obj)) { + let localArr: Array = obj as Array; + for (let i = 0; i < localArr.length; i++) { + this.writeObjectToNative(i.toString(), localArr[i] as Object, wantParams); + } + // consistent with napi behavior + wantParams.setParam("length", localArr.length as int); + } else if (obj instanceof Record) { + if (this.writeRecordToNative(obj as Object as Record, wantParams)!= true) { + hilog.error(DOMAIN_ID, TAG, `write array member To native failed`); + return false; + } + } else { + const objType = Type.of(obj); + if (objType instanceof ClassType) { + let newWantParams = new NativeWantParams(); + return this.writeClassValueToNative(obj, objType as ClassType, wantParams); + } else { + return false; + } + } + return true; + } + + private writeArrayToNative(key: string, arr: Array, wantParams: NativeWantParams | null): void { + let complexArray: ComplexArrayData = { + intlist: [], + boolList: [], + doubleList: [], + stringList: [], + objectList: [] + } + this.unwrapArray(arr, complexArray); + if (complexArray.objectList.length > 0) { + let wantParamsArray: Array = []; + let newWantParams: NativeWantParams; + for (let i = 0; i < complexArray.objectList.length; i++) { + newWantParams = new NativeWantParams(); + if (this.handleArrayObject(complexArray.objectList[i] as object, newWantParams)!= true) { + hilog.error(DOMAIN_ID, TAG, `write array member To native failed, index: %{public}d`, i); + continue; + } + wantParamsArray.push(newWantParams); + } + if (wantParamsArray.length > 0) { + wantParams?.SetArrayWantParams(key, wantParamsArray); + } + } else if (complexArray.stringList.length > 0) { + wantParams?.SetArrayStringParam(key, complexArray.stringList); + } else if (complexArray.intlist.length > 0) { + wantParams?.SetArrayIntParam(key, complexArray.intlist); + } else if (complexArray.boolList.length > 0) { + wantParams?.SetArrayBooleanParam(key, complexArray.boolList); + } else if (complexArray.doubleList.length > 0) { + wantParams?.SetArrayDoubleParam(key, complexArray.doubleList); + } + } + private writeBuildArray(arr: Object, arrayValue: ArrayValue): void { this.buffer.append('['); const length = arrayValue.getLength() as int; @@ -122,26 +467,137 @@ class RecordWriter { this.store.delete(arr); this.buffer.append(']'); } - - private writeRecord(rec: Record): void { - this.buffer.append('{'); + // 废弃 + private writeRecord(rec: Record): boolean { + //this.buffer.append('{'); this.checkReferencesCycle(rec); this.store.add(rec); - let isFirst = true; + let isSuccess = false; for (let key of rec.keys()) { if (rec[key] !== undefined) { - if (!isFirst) { - this.buffer.append(','); - } else { - isFirst = false; + //if (!isFirst) { + // this.buffer.append(','); + //} else { + // isFirst = false; + //} + //this.buffer.append(JSON.stringify(key as String)); + //this.buffer.append(':'); + //this.writeObject(rec[key]); + + isSuccess = this.writeObjectToNative(key as String, rec[key] as Object); + if (isSuccess != true) { + hilog.error(DOMAIN_ID, TAG, `write object to native failed, key: %{public}s`, JSON.stringify(key as String)); + return false; } - this.buffer.append(JSON.stringify(key as String)); - this.buffer.append(':'); - this.writeObject(rec[key]); } } this.store.delete(rec); - this.buffer.append('}'); + //this.buffer.append('}'); + return isSuccess; + } + + private writeRecordToNative(rec: Record, wantParams: NativeWantParams | null): boolean { + this.checkReferencesCycle(rec); + this.store.add(rec); + let isSuccess = false; + for (let key of rec.keys()) { + if (rec[key] == undefined) { + continue; + } + if (this.writeObjectToNative(key as String, rec[key] as Object, wantParams != true) { + hilog.error(DOMAIN_ID, TAG, `write object to native failed, key: %{public}s`, JSON.stringify(key as String)); + } + isSuccess = true; + } + this.store.delete(rec); + if (isSuccess != true) { + hilog.error(DOMAIN_ID, TAG, `null record`); + } + return isSuccess; + } + + private writeClassValueToNative(obj: Object, objType: ClassType, wantParams: NativeWantParams): boolean { + //const currentBuffer = this.buffer + // we need this local buffer to discard empty object prolog: '{','\n', and replace it with '{}' + //this.buffer = new StringBuilder("{") + + const writableFields = this.getWritableFields(objType, reflect.Value.of(obj) as ClassValue); + + let isSuccess = false; + if (writableFields.length > 0) { + //this.path.add(obj) + this.checkReferencesCycle(obj); + this.store.add(obj); + isSuccess = this.writeClassFields(writableFields, wantParams); + //this.path.delete(obj); + this.store.delete(obj); + } + + return isSuccess; + } + + private writeField(fieldName: string, fieldVal: NullishType, wantParams: NativeWantParams): boolean { + if (fieldVal === undefined) { + return false; + } + + const fieldValType = Type.of(fieldVal); + if (fieldValType instanceof LambdaType) { + return false; + } + + //this.checkReferencesCycle(fieldVal); + //this.writeObject(fieldVal) + return this.writeObjectToNative(fieldName, fieldVal, wantParams); + } + + private writeClassFields(writableFields: Array<[Field, reflect.Value]>, wantParams: NativeWantParams): boolean { + const FIELD_TYPE = 0 + const FIELD_VALUE = 1 + + let isSuccess = false; + for (let fieldIdx = 0; fieldIdx < writableFields.length; fieldIdx++) { + const fieldTypeValuePair = writableFields[fieldIdx]; + + const objField = fieldTypeValuePair[0] as Field; + const objFieldValue = fieldTypeValuePair[1] as reflect.Value; + + //isSuccess = this.writeField(objField.getName(), objFieldValue.getData(), wantParams, isSuccess); + if (this.writeField(objField.getName(), objFieldValue.getData(), wantParams) == true) { + isSuccess= true; + } + } + + return isSuccess; + } + + private findKeyIndex(field: Field, fields: Array<[Field, reflect.Value]>): number { + return fields.findIndex((value: [Field, reflect.Value], index: number, array:Array<[Field, reflect.Value]>) + => {return value[0].getName() == field.getName()}); + } + + private getWritableFields(classType: ClassType, classValue: ClassValue): Array<[Field, reflect.Value]> { + const writableFields = new Array<[Field, reflect.Value]>(); + let fieldRename : string | undefined = undefined; + + const fieldsCount = classValue.getFieldsNum(); + for (let fieldIdx = 0; fieldIdx < fieldsCount; fieldIdx++) { + const field = classType.getField(fieldIdx); + if (field.isStatic()) { + continue; + } + const index: number = this.findKeyIndex(field, writableFields); + const fieldValue = classValue.getField(fieldIdx); + + const fieldTypeValuePair: [Field, reflect.Value] = [field, fieldValue]; + if (fieldRename != undefined || index == -1) { + writableFields.push(fieldTypeValuePair); + } else { + writableFields[index] = fieldTypeValuePair; + } + } + + return writableFields; } private checkReferencesCycle(obj: Object): void { @@ -152,12 +608,12 @@ class RecordWriter { } export class RecordSerializeTool { - public static stringifyNoThrow(obj: Record): String { + public static stringifyNoThrow(obj: Record, nativeWantParams: long): boolean { try { - return RecordSerializeTool.stringify(obj as Object as Record); + return RecordSerializeTool.stringify(obj as Object as Record, nativeWantParams); } catch (err) { hilog.error(DOMAIN_ID, TAG, `RecordSerializeTool.stringify error: ${err}`); - return ''; + return false; } } @@ -170,8 +626,8 @@ export class RecordSerializeTool { } } - public static stringify(obj: Record): String { - return new RecordWriter().write(obj); + public static stringify(obj: Record, nativeWantParams: long): boolean { + return new RecordWriter(nativeWantParams).write(obj); } public static parse(text: string): Record { @@ -218,6 +674,177 @@ export class RecordSerializeTool { } } +class NativeWantParamsCleaner { + public nativeWantParams: long = 0; + + constructor(nativeWantParams: long) { + this.nativeWantParams = nativeWantParams; + } + + private static native nativeDestroy(nativeWantParams: long): void; + + public clean(): void { + NativeWantParamsCleaner.nativeDestroy(this.nativeWantParams); + this.nativeWantParams = 0; + } +} + +function nativeWantParamsCleanerCallback(cleaner: NativeWantParamsCleaner): void { + cleaner.clean(); +} + +let destroyRegister = new FinalizationRegistry(nativeWantParamsCleanerCallback); + +class NativeWantParams { + static { + loadLibrary("want_ani_kit.z"); + } + + private nativeWantParams: long = 0; + private cleaner: NativeWantParamsCleaner | null = null; + + constructor(wantParams?: long) { + if (wantParams === undefined) { + this.nativeWantParams = NativeWantParams.nativeCreate(); + this.registerCleaner(this.nativeWantParams) + return; + } + this.nativeWantParams = wantParams; + } + + private static native nativeCreate(): long; + + private static native nativeSetStringParam(nativeWantParams: long, key: string, value: string): boolean; + private static native nativeSetDoubleParam(nativeWantParams: long, key: string, value: double): boolean; + private static native nativeSetIntParam(nativeWantParams: long, key: string, value: int): boolean; + private static native nativeSetLongParam(nativeWantParams: long, key: string, value: long): boolean; + private static native nativeSetBooleanParam(nativeWantParams: long, key: string, value: boolean): boolean; + private static native nativeSetWantParams(nativeWantParams: long, key: string, value: long): boolean; + + private static native nativeSetArrayStringParam(nativeWantParams: long, key: string, value: Array): boolean; + private static native nativeSetArrayDoubleParam(nativeWantParams: long, key: string, value: Array): boolean; + private static native nativeSetArrayIntParam(nativeWantParams: long, key: string, value: Array): boolean; + private static native nativeSetArrayLongParam(nativeWantParams: long, key: string, value: Array): boolean; + private static native nativeSetArrayBooleanParam(nativeWantParams: long, key: string, value: Array): boolean; + private static native nativeSetArrayWantParams(nativeWantParams: long, key: string, value: Array): boolean; + + private static native nativeSetRemoteObjectParam( + nativeWantParams: long, key: string, value: rpc.RemoteObject): boolean; + + private registerCleaner(nativeWantParams: long): void { + this.cleaner = new NativeWantParamsCleaner(nativeWantParams); + destroyRegister.register(this, this.cleaner!, this); + } + + public setParam(key: string, value: string): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetStringParam(this.nativeWantParams, key, value); + } + + public setParam(key: string, value: double): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetDoubleParam(this.nativeWantParams, key, value); + } + + public setParam(key: string, value: int): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetIntParam(this.nativeWantParams, key, value); + } + + public setParam(key: string, value: long): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetLongParam(this.nativeWantParams, key, value); + } + + public setParam(key: string, value: boolean): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetBooleanParam(this.nativeWantParams, key, value); + } + + public setParam(key: string, value: NativeWantParams): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetWantParams(this.nativeWantParams, key, value.nativeWantParams); + } + + public SetParam(key: string, value: Array): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetArrayStringParam(this.nativeWantParams, key, value); + } + + public SetArrayDoubleParam(key: string, value: Array): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetArrayDoubleParam(this.nativeWantParams, key, value); + } + + public SetArrayIntParam(key: string, value: Array): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetArrayIntParam(this.nativeWantParams, key, value); + } + + public SetArrayLongParam(key: string, value: Array): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetArrayLongParam(this.nativeWantParams, key, value); + } + + public SetArrayBooleanParam(key: string, value: Array): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetArrayBooleanParam(this.nativeWantParams, key, value); + } + + public SetArrayWantParams(key: string, value: Array): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + let nativeWantParams: Array = new Array(); + for (let item of value) { + nativeWantParams.push(item.nativeWantParams); + } + return NativeWantParams.nativeSetArrayWantParams(this.nativeWantParams, key, nativeWantParams); + } + + public setParam(key: string, value: rpc.RemoteObject): boolean { + if (this.nativeWantParams == 0) { + hilog.error(DOMAIN_ID, TAG, `nativeWantParams null`); + return false; + } + return NativeWantParams.nativeSetRemoteObjectParam(this.nativeWantParams, key, value); + } +} + export default class Want { bundleName?: string; abilityName?: string;