diff --git a/bundle.json b/bundle.json index 9d65cca9ebbab45c0d2ed4f6f09c92c7ef7ce52b..5685a9502de9bccd5064a22a1ffe6bfd2c81a57a 100644 --- a/bundle.json +++ b/bundle.json @@ -40,6 +40,7 @@ "json", "jsoncpp", "napi", + "runtime_core", "openssl", "safwk", "samgr", @@ -70,8 +71,11 @@ "//foundation/filemanagement/app_file_service/interfaces/kits/js:fileshare", "//foundation/filemanagement/app_file_service/interfaces/kits/js:fileuri", "//foundation/filemanagement/app_file_service/interfaces/kits/js:backup", + "//foundation/filemanagement/app_file_service/interfaces/kits/ani/file_share:fileshare_ani_package", + "//foundation/filemanagement/app_file_service/interfaces/kits/ani/file_uri:fileuri_ani_package", "//foundation/filemanagement/app_file_service/interfaces/kits/ndk/fileuri/src:ohfileuri", - "//foundation/filemanagement/app_file_service/interfaces/kits/ndk/fileshare/src:ohfileshare" + "//foundation/filemanagement/app_file_service/interfaces/kits/ndk/fileshare/src:ohfileshare", + "//foundation/filemanagement/app_file_service/frameworks/native/backup_ext/ani:backup_ext_ani_package" ], "service_group": [ "//foundation/filemanagement/app_file_service:tgt_backup_extension", diff --git a/frameworks/native/backup_ext/BUILD.gn b/frameworks/native/backup_ext/BUILD.gn index a256453d8db83c95f57a1aa4f4e70e556fa371d1..dc3f244c95f68f298bc9189c0a32133793835f7a 100644 --- a/frameworks/native/backup_ext/BUILD.gn +++ b/frameworks/native/backup_ext/BUILD.gn @@ -28,6 +28,7 @@ ohos_shared_library("backup_extension_ability_native") { sources = [ "src/ext_backup.cpp", + "src/ext_backup_ani.cpp", "src/ext_backup_context.cpp", "src/ext_backup_context_js.cpp", "src/ext_backup_js.cpp", @@ -73,6 +74,7 @@ ohos_shared_library("backup_extension_ability_native") { "ipc:ipc_napi", "jsoncpp:jsoncpp", "napi:ace_napi", + "runtime_core:ani", "samgr:samgr_proxy", ] diff --git a/frameworks/native/backup_ext/ani/BUILD.gn b/frameworks/native/backup_ext/ani/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6d325e06dca001270d9b2a4513e6d6a76512b413 --- /dev/null +++ b/frameworks/native/backup_ext/ani/BUILD.gn @@ -0,0 +1,40 @@ +# 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/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") + +if (support_jsapi) { + group("backup_ext_ani_package") { + deps = [ + ":backup_ext_abc_etc", + ] + } + + generate_static_abc("backup_ext_abc") { + base_url = "./ets" + files = [ "./ets/@ohos.application.BackupExtensionAbility.ets" ] + dst_file = "$target_out_dir/backup_ext.abc" + out_puts = [ "$target_out_dir/backup_ext.abc" ] + is_boot_abc = "True" + device_dst_file = "/system/framework/backup_ext.abc" + } + + ohos_prebuilt_etc("backup_ext_abc_etc") { + source = "$target_out_dir/backup_ext.abc" + module_install_dir = "framework" + subsystem_name = "filemanagement" + part_name = "app_file_service" + deps = [ ":backup_ext_abc" ] + } +} diff --git a/frameworks/native/backup_ext/ani/ets/@ohos.application.BackupExtensionAbility.ets b/frameworks/native/backup_ext/ani/ets/@ohos.application.BackupExtensionAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..2941755b9a0310aa5b132925b55900e2b583c66c --- /dev/null +++ b/frameworks/native/backup_ext/ani/ets/@ohos.application.BackupExtensionAbility.ets @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +export interface BundleVersion { + code: number; + name: string; +} + +export class BundleVersionInner implements BundleVersion { + public code: number; + public name: string = ''; +} + +export default abstract class BackupExtensionAbility { + abstract onBackup(): void; + abstract onRestore(bundleVersion: BundleVersion): void; +} \ No newline at end of file diff --git a/frameworks/native/backup_ext/include/ani_utils.h b/frameworks/native/backup_ext/include/ani_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..ee8c9baa2ea8ead6dbf9ed54e1da78d6319f22f8 --- /dev/null +++ b/frameworks/native/backup_ext/include/ani_utils.h @@ -0,0 +1,320 @@ +/* + * 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 ANI_UTILS_H +#define ANI_UTILS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class AniObjectUtils { +public: + static ani_object Create(ani_env *env, const char *nsName, const char *clsName, ...) + { + ani_object nullobj {}; + + ani_namespace ns; + if (ANI_OK != env->FindNamespace(nsName, &ns)) { + std::cerr << "[ANI] Not found namespace " << nsName << std::endl; + return nullobj; + } + + ani_class cls; + if (ANI_OK != env->Namespace_FindClass(ns, clsName, &cls)) { + std::cerr << "[ANI] Not found class " << clsName << std::endl; + return nullobj; + } + + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", nullptr, &ctor)) { + std::cerr << "[ANI] Not found for class " << clsName << std::endl; + return nullobj; + } + + std::cerr << "[ANI] AniObjectUtils Object_New_V " << clsName << std::endl; + ani_object obj; + va_list args; + va_start(args, clsName); + ani_status status = env->Object_New_V(cls, ctor, &obj, args); + va_end(args); + if (ANI_OK != status) { + std::cerr << "[ANI] Failed to Object_New for class " << cls << std::endl; + return nullobj; + } + return obj; + } + + static ani_object Create(ani_env *env, const char *clsName, ...) + { + ani_object nullobj {}; + + ani_class cls; + if (ANI_OK != env->FindClass(clsName, &cls)) { + std::cerr << "[ANI] Not found class " << clsName << std::endl; + return nullobj; + } + + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", nullptr, &ctor)) { + std::cerr << "[ANI] Not found for class " << clsName << std::endl; + return nullobj; + } + + ani_object obj; + va_list args; + va_start(args, clsName); + ani_status status = env->Object_New_V(cls, ctor, &obj, args); + va_end(args); + if (ANI_OK != status) { + std::cerr << "[ANI] Failed to Object_New for class " << cls << std::endl; + return nullobj; + } + return obj; + } + + static ani_object Create(ani_env *env, ani_class cls, ...) + { + ani_object nullobj {}; + + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", nullptr, &ctor)) { + std::cerr << "[ANI] Not found for class" << std::endl; + return nullobj; + } + + ani_object obj; + va_list args; + va_start(args, cls); + ani_status status = env->Object_New_V(cls, ctor, &obj, args); + va_end(args); + if (ANI_OK != status) { + std::cerr << "[ANI] Failed to Object_New for class " << cls << std::endl; + return nullobj; + } + return obj; + } + + template + static ani_status Wrap(ani_env *env, ani_object object, T *nativePtr, const char *propName = "nativePtr") + { + return env->Object_SetFieldByName_Long(object, propName, reinterpret_cast(nativePtr)); + } + + template + static T *Unwrap(ani_env *env, ani_object object, const char *propName = "nativePtr") + { + ani_long nativePtr; + if (ANI_OK != env->Object_GetFieldByName_Long(object, propName, &nativePtr)) { + return nullptr; + } + return reinterpret_cast(nativePtr); + } +}; + +class AniStringUtils { +public: + static std::string ToStd(ani_env *env, ani_string ani_str) + { + ani_size strSize; + env->String_GetUTF8Size(ani_str, &strSize); + + std::vector buffer(strSize + 1); // +1 for null terminator + char *utf8_buffer = buffer.data(); + + // String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416 + ani_size bytes_written = 0; + env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written); + + if (bytes_written <= strSize) { + utf8_buffer[bytes_written] = '\0'; + } else { + utf8_buffer[strSize] = '\0'; + } + std::string content = std::string(utf8_buffer); + return content; + } + + static ani_string ToAni(ani_env *env, const std::string &str) + { + ani_string aniStr = nullptr; + if (ANI_OK != env->String_NewUTF8(str.data(), str.size(), &aniStr)) { + std::cerr << "[ANI] Unsupported ANI_VERSION_1" << std::endl; + return nullptr; + } + return aniStr; + } +}; + +class UnionAccessor { +public: + UnionAccessor(ani_env *env, ani_object &obj) : env_(env), obj_(obj) {} + + bool IsInstanceOf(const std::string &cls_name) + { + ani_class cls; + ani_status status = env_->FindClass(cls_name.c_str(), &cls); + if (status != ANI_OK) { + return false; + } + + ani_boolean ret; + env_->Object_InstanceOf(obj_, cls, &ret); + return ret; + } + + template + bool IsInstanceOfType(); + + template + bool TryConvert(T &value); + + template + bool TryConvertArray(std::vector &value); + +private: + ani_env *env_; + ani_object obj_; +}; + +template <> +inline bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/Boolean;"); +} + +template <> +inline bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/Int;"); +} + +template <> +inline bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/Double;"); +} + +template <> +inline bool UnionAccessor::IsInstanceOfType() +{ + return IsInstanceOf("Lstd/core/String;"); +} + +template <> +inline bool UnionAccessor::TryConvert(bool &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + ani_boolean aniValue; + auto ret = env_->Object_CallMethodByName_Boolean(obj_, "unboxed", nullptr, &aniValue); + if (ret != ANI_OK) { + return false; + } + value = static_cast(aniValue); + return true; +} + +template <> +inline bool UnionAccessor::TryConvert(int &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + ani_int aniValue; + auto ret = env_->Object_CallMethodByName_Int(obj_, "unboxed", nullptr, &aniValue); + if (ret != ANI_OK) { + return false; + } + value = static_cast(aniValue); + return true; +} + +template <> +inline bool UnionAccessor::TryConvert(double &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + ani_double aniValue; + auto ret = env_->Object_CallMethodByName_Double(obj_, "unboxed", nullptr, &aniValue); + if (ret != ANI_OK) { + return false; + } + value = static_cast(aniValue); + return true; +} + +template <> +inline bool UnionAccessor::TryConvert(std::string &value) +{ + if (!IsInstanceOfType()) { + return false; + } + + value = AniStringUtils::ToStd(env_, static_cast(obj_)); + return true; +} + +class OptionalAccessor { +public: + OptionalAccessor(ani_env *env, ani_object &obj) : env_(env), obj_(obj) {} + + bool IsUndefined() + { + ani_boolean isUndefined; + ani_status status = env_->Reference_IsUndefined(obj_, &isUndefined); + if (status != ANI_OK) { + return false; + } + return isUndefined; + } + + template + std::optional Convert(); + +private: + ani_env *env_; + ani_object obj_; +}; + +template <> +inline std::optional OptionalAccessor::Convert() +{ + if (IsUndefined()) { + return std::nullopt; + } + + ani_double aniValue; + auto ret = env_->Object_CallMethodByName_Double(obj_, "doubleValue", nullptr, &aniValue); + if (ret != ANI_OK) { + return std::nullopt; + } + auto value = static_cast(aniValue); + return value; +} + +#endif diff --git a/frameworks/native/backup_ext/include/ext_backup_ani.h b/frameworks/native/backup_ext/include/ext_backup_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..32d996699d44e39f22729ede24e2de59702ab620 --- /dev/null +++ b/frameworks/native/backup_ext/include/ext_backup_ani.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXT_BACKUP_ANI_H +#define EXT_BACKUP_ANI_H + +#include "ext_backup_context.h" +#include "ext_backup.h" +#include "runtime.h" +#include "ets_runtime.h" + +namespace OHOS::FileManagement::Backup { + +class ExtBackupAni : public ExtBackup { +public: + void Init(const std::shared_ptr &record, + const std::shared_ptr &application, + std::shared_ptr &handler, + const sptr &token) override; + +public: + static ExtBackupAni *Create(const std::unique_ptr &runtime); + + ErrCode OnBackup(std::function callback, + std::function callbackEx) override; + + ErrCode OnRestore(std::function callback, + std::function callbackEx) override; + +public: + explicit ExtBackupAni(AbilityRuntime::Runtime &runtime); + virtual ~ExtBackupAni() override; + +private: + ErrCode CallEtsOnBackup(); + ErrCode CallEtsOnRestore(); + +private: + AbilityRuntime::ETSRuntime &stsRuntime_; + std::unique_ptr etsObj_; +}; + +} // namespace OHOS::FileManagement::Backup + +#endif // EXT_BACKUP_ANI_H \ No newline at end of file diff --git a/frameworks/native/backup_ext/src/ext_backup.cpp b/frameworks/native/backup_ext/src/ext_backup.cpp index fc37b3296bfde94427df8120e971e2fb95ca5e7c..c5771225050a1d18bcee7d7227559372587cc212 100644 --- a/frameworks/native/backup_ext/src/ext_backup.cpp +++ b/frameworks/native/backup_ext/src/ext_backup.cpp @@ -31,6 +31,7 @@ #include "b_json/b_json_cached_entity.h" #include "b_json/b_json_entity_extension_config.h" #include "b_resources/b_constants.h" +#include "ext_backup_ani.h" #include "ext_backup_js.h" #include "ext_extension.h" #include "filemgmt_libhilog.h" @@ -76,7 +77,9 @@ ExtBackup *ExtBackup::Create(const unique_ptr &runtime) case AbilityRuntime::Runtime::Language::JS: HILOGD("Create as BackupExtensionAbility(JS)"); return ExtBackupJs::Create(runtime); - + case AbilityRuntime::Runtime::Language::ETS: + HILOGD("Create as BackupExtensionAbility(ETS)"); + return ExtBackupAni::Create(runtime); default: HILOGD("Create as BackupExtensionAbility(base)"); return new ExtBackup(); diff --git a/frameworks/native/backup_ext/src/ext_backup_ani.cpp b/frameworks/native/backup_ext/src/ext_backup_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffb67e5a4610f869448e29ead24092c401a202e5 --- /dev/null +++ b/frameworks/native/backup_ext/src/ext_backup_ani.cpp @@ -0,0 +1,154 @@ +/* + * 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 "ext_backup_ani.h" + +#include "ani_utils.h" +#include "b_error/b_error.h" +#include "b_error/b_excep_utils.h" +#include "filemgmt_libhilog.h" + +namespace OHOS::FileManagement::Backup { +using namespace std; + +ExtBackupAni::ExtBackupAni(AbilityRuntime::Runtime &runtime) + : stsRuntime_(static_cast(runtime)) +{ + HILOGI("Create as an BackupExtensionAbility"); +} + +ExtBackupAni::~ExtBackupAni() +{ + HILOGI("ExtBackupAni::~ExtBackupAni."); +} + +void ExtBackupAni::Init(const std::shared_ptr &record, + const std::shared_ptr &application, + std::shared_ptr &handler, + const sptr &token) +{ + HILOGI("Init the BackupExtensionAbility"); + try { + if (record == nullptr) { + HILOGE("record null"); + return; + } + Extension::Init(record, application, handler, token); + if (Extension::abilityInfo_ == nullptr || Extension::abilityInfo_->srcEntrance.empty()) { + HILOGE("BackupExtensionAbility 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_ = stsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath, + abilityInfo_->compileMode == AppExecFwk::CompileMode::ES_MODULE, false, + abilityInfo_->srcEntrance); + if (etsObj_ == nullptr) { + HILOGE("Failed to get etsObj"); + return; + } + } catch (const BError &e) { + HILOGE("%{public}s", e.what()); + } catch (const exception &e) { + HILOGE("%{public}s", e.what()); + } +} + +ErrCode ExtBackupAni::CallEtsOnBackup() +{ + ani_vm *vm = nullptr; + if (ANI_OK != stsRuntime_.GetAniEnv()->GetVM(&vm)) { + return EINVAL; + } + ani_env *env = nullptr; + ani_options aniArgs {0, nullptr}; + if (ANI_OK != vm->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env)) { + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + return EINVAL; + } + } + if (env == nullptr) { + HILOGE("Failed to get environment"); + vm->DetachCurrentThread(); + return EINVAL; + } + if (ANI_OK != env->Object_CallMethodByName_Void(etsObj_->aniObj, "onBackup", nullptr)) { + HILOGE("Failed to call the method: onBackup"); + vm->DetachCurrentThread(); + return EINVAL; + } + vm->DetachCurrentThread(); + return ERR_OK; +} + +ErrCode ExtBackupAni::CallEtsOnRestore() +{ + ani_vm *vm = nullptr; + if (ANI_OK != stsRuntime_.GetAniEnv()->GetVM(&vm)) { + return EINVAL; + } + ani_env *env = nullptr; + ani_options aniArgs {0, nullptr}; + if (ANI_OK != vm->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env)) { + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + return EINVAL; + } + } + const std::string className = "L@ohos/application/BackupExtensionAbility/BundleVersionInner;"; + ani_object bundleVersionObj = AniObjectUtils::Create(env, className.c_str()); + if (nullptr == bundleVersionObj) { + HILOGE("Failed to Create the BundleVersionInner"); + return EINVAL; + } + if (ANI_OK != env->Object_CallMethodByName_Void(etsObj_->aniObj, "onRestore", nullptr, bundleVersionObj)) { + HILOGE("Failed to call the method: onRestore"); + return EINVAL; + } + vm->DetachCurrentThread(); + return ERR_OK; +} + +ExtBackupAni *ExtBackupAni::Create(const std::unique_ptr &runtime) +{ + HILOGI("Create as an BackupExtensionAbility"); + return new ExtBackupAni(*runtime); +} + +ErrCode ExtBackupAni::OnBackup(function callback, + std::function callbackEx) +{ + HILOGI("ExtBackupAni OnBackup"); + BExcepUltils::BAssert(etsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK, + "The app does not provide the onBackup interface."); + return CallEtsOnBackup(); +} + +ErrCode ExtBackupAni::OnRestore(std::function callback, + std::function callbackEx) +{ + HILOGI("ExtBackupAni OnRestore"); + BExcepUltils::BAssert(etsObj_, BError::Codes::EXT_BROKEN_FRAMEWORK, + "The app does not provide the OnRestore interface."); + return CallEtsOnRestore(); +} + +} // namespace OHOS::FileManagement::Backup \ No newline at end of file diff --git a/interfaces/kits/ani/file_share/BUILD.gn b/interfaces/kits/ani/file_share/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6acb23f62750469fb55eff6dad6a911af87100b4 --- /dev/null +++ b/interfaces/kits/ani/file_share/BUILD.gn @@ -0,0 +1,89 @@ +# 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/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/filemanagement/app_file_service/app_file_service.gni") + +ohos_shared_library("fileshare_ani") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + ubsan = true + cfi = true + cfi_cross_dso = true + debug = false + } + + cflags_cc = [ "-Wno-unused-function" ] + sources = [ "src/ani_file_share.cpp" ] + + include_dirs = [ + "include", + "../../../common/include", + "${app_file_service_path}/interfaces/innerkits/native/file_share/include", + ] + + deps = + [ "${app_file_service_path}/interfaces/innerkits/native:fileuri_native" ] + + external_deps = [ + "ability_base:want", + "ability_base:zuri", + "ability_runtime:abilitykit_native", + "ability_runtime:extensionkit_native", + "ability_runtime:uri_permission_mgr", + "access_token:libaccesstoken_sdk", + "access_token:libtokenid_sdk", + "bundle_framework:appexecfwk_base", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "data_share:datashare_common", + "data_share:datashare_consumer", + "file_api:filemgmt_libhilog", + "file_api:filemgmt_libn", + "file_api:remote_uri_native", + "hilog:libhilog", + "init:libbegetutil", + "ipc:ipc_core", + "napi:ace_napi", + "runtime_core:ani", + ] + part_name = "app_file_service" + subsystem_name = "filemanagement" + output_extension = "so" +} + +generate_static_abc("fileshare_abc") { + base_url = "./ets" + files = [ "./ets/@ohos.fileshare.ets" ] + dst_file = "$target_out_dir/fileshare.abc" + out_puts = [ "$target_out_dir/fileshare.abc" ] + is_boot_abc = "True" + device_dst_file = "/system/framework/fileshare.abc" +} + +ohos_prebuilt_etc("fileshare_abc_etc") { + source = "$target_out_dir/fileshare.abc" + module_install_dir = "framework" + subsystem_name = "filemanagement" + part_name = "app_file_service" + deps = [ ":fileshare_abc" ] +} + +group("fileshare_ani_package") { + deps = [ + ":fileshare_abc_etc", + ":fileshare_ani", + ] +} diff --git a/interfaces/kits/ani/file_share/ets/@ohos.fileshare.ets b/interfaces/kits/ani/file_share/ets/@ohos.fileshare.ets new file mode 100644 index 0000000000000000000000000000000000000000..03dfb2a81360c3e2ae40c894fb34c7ffafc430f4 --- /dev/null +++ b/interfaces/kits/ani/file_share/ets/@ohos.fileshare.ets @@ -0,0 +1,80 @@ +/* + * 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 type { AsyncCallback } from '@ohos.base'; +import { BusinessError } from '@ohos.base'; +import type wantConstant from '@ohos.app.ability.wantConstant'; +import hilog from '@ohos.hilog' + +export namespace fileShare { + loadLibrary("fileshare_ani") + + export enum OperationMode { + READ_MODE = 0b1, + WRITE_MODE = 0b10 + } + export interface PolicyInfo { + uri: string; + operationMode: int; + } + + class PolicyInfo_inner implements PolicyInfo { + uri: string; + operationMode: int; + } + + export function grantUriPermission(uri: string, bundleName: string, flag: wantConstant.Flags, + callback: AsyncCallback): void { + hilog.info(0x0000, 'grantUriPermission', "Start grantUriPermission in callback main thread."); + let p1 = taskpool.execute(grantUriPermissionSync, uri, bundleName, flag); + p1.then((data: NullishType) => { + let ret = data as int; + hilog.info(0x0000, 'grantUriPermission', "grantUriPermission ret = " + ret); + let error: BusinessError; + callback(error, undefined); + }); + p1.catch((err: NullishType) => { + hilog.info(0x0000, 'grantUriPermission', "grantUriPermission catch in callback thread."); + let error = err as BusinessError; + callback(error, undefined); + }); + } + + export function grantUriPermission(uri: string, bundleName: string, flag: wantConstant.Flags): Promise { + hilog.info(0x0000, 'grantUriPermission', "Start grantUriPermission in promise main thread"); + let p = new Promise((resolve: (value: undefined) => void, reject: (error: Object) => void): void => { + let p1 = taskpool.execute(grantUriPermissionSync, uri, bundleName, flag); + p1.then((e: NullishType): void => { + resolve(undefined); + }); + p1.catch((e: Error): void => { + hilog.info(0x0000, 'grantUriPermission', "grantUriPermission catch in promise thread."); + reject(e); + }); + }); + return p; + } + + export function grantUriPermissionSync(uri: string, bundleName: string, flag: wantConstant.Flags): int { + try { + grantUriPermissionInner(uri, bundleName, flag); + } catch (error) { + hilog.info(0x0000, 'grantUriPermission', "grantUriPermissionInner catch error"); + return -1; + } + return 0; + } + native function grantUriPermissionInner(uri: string, bundleName: string, flag: wantConstant.Flags):void; +} diff --git a/interfaces/kits/ani/file_share/include/ani_file_share.h b/interfaces/kits/ani/file_share/include/ani_file_share.h new file mode 100644 index 0000000000000000000000000000000000000000..33c5ce44dd6b40a886a2db0195d3a6e709aa215c --- /dev/null +++ b/interfaces/kits/ani/file_share/include/ani_file_share.h @@ -0,0 +1,69 @@ + /* + * 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 ANI_FILE_SHARE_H +#define ANI_FILE_SHARE_H + +#include +#include "iremote_broker.h" + +namespace OHOS { +namespace AppFileService { +namespace ModuleFileShare { + +const std::string MEDIA_GRANT_URI_PERMISSION = + "datashare:///media/bundle_permission_insert_operation/bundle_permission_insert_operation"; +const std::string MEDIALIBRARY_DATA_URI = "datashare:///media"; +const std::string MEDIA_FILEMODE_READONLY = "r"; +const std::string GRANT_URI_NAME = "file_share_grant_uri_permission"; +const std::string MEDIA_API_VERSION_10 = "?api_version=10"; +const std::string PERMISSION_BUNDLE_NAME = "bundle_name"; +const std::string PERMISSION_FILE_ID = "file_id"; +const std::string PERMISSION_MODE = "mode"; +const std::string MEDIA_AUTHORITY = "media"; +const std::string FILE_SCHEME = "file"; +const std::string PERMISSION_TABLE_TYPE = "table_type"; +const std::string FILE_MANAGER_AUTHORITY = "docs"; +const std::string MEDIA_FILE_URI_PHOTO_PREFIX = "file://media/Photo/"; +const std::string MEDIA_FILE_URI_AUDIO_PREFIX = "file://media/Audio/"; +const std::string MEDIA_FILE_URI_VIDEO_PREFIX = "file://media/video/"; +const std::string MEDIA_FILE_URI_IMAGE_PREFIX = "file://media/image/"; +const std::string MEDIA_FILE_URI_FILE_PREFIX = "file://media/file/"; +const std::string MEDIA_FILE_URI_AUDIO_PREFIX_L = "file://media/audio/"; + +class FileShareGrantToken : public IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"ohos.fileshare.grantUriPermission"); + + FileShareGrantToken() = default; + virtual ~FileShareGrantToken() noexcept = default; +}; + +enum MediaFileTable { + FILE_TABLE = 0, + PHOTO_TABLE = 1, + AUDIO_TABLE = 2, +}; + +struct UriPermissionInfo { + unsigned int flag; + std::string mode; + std::string bundleName; + std::string uri; +}; + +} // namespace ModuleFileShare +} // namespace AppFileService +} // namespace OHOS +#endif // ANI_FILE_SHARE_H diff --git a/interfaces/kits/ani/file_share/src/ani_file_share.cpp b/interfaces/kits/ani/file_share/src/ani_file_share.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c20eb40d64a424d0add548b03b9b2002739b468 --- /dev/null +++ b/interfaces/kits/ani/file_share/src/ani_file_share.cpp @@ -0,0 +1,325 @@ + /* + * 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_file_share.h" + +#include +#include +#include +#include "ability.h" +#include "datashare_helper.h" +#include "datashare_values_bucket.h" +#include "ipc_skeleton.h" +#include "log.h" +#include "n_error.h" +#include "remote_uri.h" +#include "tokenid_kit.h" +#include "uri.h" +#include "uri_permission_manager_client.h" +#include "want.h" + +using namespace OHOS::DataShare; +using namespace OHOS::DistributedFS::ModuleRemoteUri; +using namespace OHOS::FileManagement::LibN; + +namespace OHOS { +namespace AppFileService { +namespace ModuleFileShare { + +static std::string ParseObjToStr(ani_env *env, ani_string stringObj) +{ + ani_size strSize; + env->String_GetUTF8Size(stringObj, &strSize); + std::vector buffer(strSize + 1); + char* utf8Buffer = buffer.data(); + + ani_size byteswritten = 0; + env->String_GetUTF8(stringObj, utf8Buffer, strSize + 1, &byteswritten); + + if (byteswritten <= strSize) { + utf8Buffer[byteswritten] = '\0'; + } else { + utf8Buffer[strSize] = '\0'; + } + std::string path = std::string(utf8Buffer); + return path; +} + +static ani_int ParseEnumToInt(ani_env *env, ani_enum_item enumItem) +{ + ani_int intValue = -1; + if (ANI_OK != env->EnumItem_GetValue_Int(enumItem, &intValue)) { + LOGE("%{public}s: EnumItem_GetValue_Int FAILD.", __func__); + return -1; + } + LOGD("%{public}s: Enum Value: %{public}d.", __func__, intValue); + return intValue; +} + +static void ThrowBusinessError(ani_env *env, int errCode, std::string&& errMsg) +{ + LOGD("Begin ThrowBusinessError."); + static const char *errorClsName = "L@ohos/base/BusinessError;"; + ani_class cls {}; + if (ANI_OK != env->FindClass(errorClsName, &cls)) { + LOGE("find class BusinessError %{public}s failed", errorClsName); + return; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", ":V", &ctor)) { + LOGE("find method BusinessError.constructor failed"); + return; + } + ani_object errorObject; + if (ANI_OK != env->Object_New(cls, ctor, &errorObject)) { + LOGE("create BusinessError object failed"); + return; + } + ani_double aniErrCode = static_cast(errCode); + ani_string errMsgStr; + if (ANI_OK != env->String_NewUTF8(errMsg.c_str(), errMsg.size(), &errMsgStr)) { + LOGE("convert errMsg to ani_string failed"); + return; + } + if (ANI_OK != env->Object_SetFieldByName_Double(errorObject, "code", aniErrCode)) { + LOGE("set error code failed"); + return; + } + if (ANI_OK != env->Object_SetPropertyByName_Ref(errorObject, "message", errMsgStr)) { + LOGE("set error message failed"); + return; + } + env->ThrowError(static_cast(errorObject)); + return; +} + +static bool IsSystemApp() +{ + uint64_t fullTokenId = OHOS::IPCSkeleton::GetCallingFullTokenID(); + return OHOS::Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId); +} + +static int32_t GetIdFromUri(string uri) +{ + std::replace(uri.begin(), uri.end(), '/', ' '); + std::stringstream ss(uri); + std::string tmp; + int fileId = -1; + ss >> tmp >> tmp >> tmp >> fileId; + return fileId; +} + +static bool CheckValidPublicUri(const std::string &inputUri) +{ + Uri uri(inputUri); + std::string scheme = uri.GetScheme(); + if (scheme != FILE_SCHEME) { + return false; + } + + std::string authority = uri.GetAuthority(); + if (authority != MEDIA_AUTHORITY && authority != FILE_MANAGER_AUTHORITY) { + return false; + } + + return true; +} + +static std::string GetModeFromFlag(unsigned int flag) +{ + std::string mode = ""; + if (flag & OHOS::AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION) { + mode += "r"; + } + if (flag & OHOS::AAFwk::Want::FLAG_AUTH_WRITE_URI_PERMISSION) { + mode += "w"; + } + return mode; +} + +static int32_t GetMediaTypeAndApiFromUri(const std::string &uri, bool &isApi10) +{ + if (uri.find(MEDIA_FILE_URI_PHOTO_PREFIX) == 0) { + isApi10 = true; + return MediaFileTable::PHOTO_TABLE; + } else if (uri.find(MEDIA_FILE_URI_VIDEO_PREFIX) == 0 || + uri.find(MEDIA_FILE_URI_IMAGE_PREFIX) == 0) { + return MediaFileTable::PHOTO_TABLE; + } else if (uri.find(MEDIA_FILE_URI_AUDIO_PREFIX) == 0) { + isApi10 = true; + return MediaFileTable::AUDIO_TABLE; + } else if (uri.find(MEDIA_FILE_URI_AUDIO_PREFIX_L) == 0) { + return MediaFileTable::AUDIO_TABLE; + } else if (uri.find(MEDIA_FILE_URI_FILE_PREFIX) == 0) { + return MediaFileTable::FILE_TABLE; + } + + return MediaFileTable::FILE_TABLE; +} + +static int InitValuesBucket(const UriPermissionInfo &uriPermInfo, Uri &uri, bool &isApi10, + DataShareValuesBucket &valuesBucket) +{ + int32_t fileId = GetIdFromUri(uriPermInfo.uri); + if (fileId == -1) { + LOGE("%{public}s: get fileId parameter failed!", __func__); + return -EINVAL; + } + + int32_t filesType = GetMediaTypeAndApiFromUri(uri.ToString(), isApi10); + valuesBucket.Put(PERMISSION_FILE_ID, fileId); + valuesBucket.Put(PERMISSION_BUNDLE_NAME, uriPermInfo.bundleName); + valuesBucket.Put(PERMISSION_MODE, uriPermInfo.mode); + valuesBucket.Put(PERMISSION_TABLE_TYPE, filesType); + return 0; +} + +static int InsertByDatashare(const DataShareValuesBucket &valuesBucket, bool isApi10) +{ + int ret = -1; + std::shared_ptr dataShareHelper = nullptr; + sptr remote = new (std::nothrow) IRemoteStub(); + if (remote == nullptr) { + LOGE("%{public}s: get remoteObject failed!", __func__); + return -ret; + } + + dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI); + if (!dataShareHelper) { + LOGE("%{public}s: connect to datashare failed!", __func__); + delete remote; + return ret; + } + string uriStr = MEDIA_GRANT_URI_PERMISSION; + if (isApi10) { + uriStr += MEDIA_API_VERSION_10; + } + + Uri uri(uriStr); + ret = dataShareHelper->Insert(uri, valuesBucket); + if (ret < 0) { + LOGE("%{public}s: insert failed with error code %{public}d!", __func__, ret); + return ret; + } + return ret; +} + +static int GrantInMediaLibrary(const UriPermissionInfo &uriPermInfo, Uri &uri) +{ + bool isApi10 = false; + DataShareValuesBucket valuesBucket; + int ret = InitValuesBucket(uriPermInfo, uri, isApi10, valuesBucket); + if (ret < 0) { + LOGE("%{public}s: InitValuesBucket failed!", __func__); + return ret; + } + + ret = InsertByDatashare(valuesBucket, isApi10); + if (ret < 0) { + LOGE("%{public}s: InsertByDatashare failed!", __func__); + return ret; + } + return 0; +} + +static int DoGrantUriPermission(const UriPermissionInfo &uriPermInfo) +{ + Uri uri(uriPermInfo.uri); + std::string authority = uri.GetAuthority(); + std::string path = uri.GetPath(); + if (authority == MEDIA_AUTHORITY && path.find(".") == string::npos) { + return GrantInMediaLibrary(uriPermInfo, uri); + } else { + auto& uriPermissionClient = OHOS::AAFwk::UriPermissionManagerClient::GetInstance(); + int ret = uriPermissionClient.GrantUriPermission(uri, uriPermInfo.flag, uriPermInfo.bundleName); + if (ret != 0) { + LOGD("%{public}s: uriPermissionClient.GrantUriPermission by uri permission client failed!", __func__); + return GrantInMediaLibrary(uriPermInfo, uri); + } + } + + return 0; +} + +static void GrantUriPermission(ani_env *env, ani_string uri, ani_string bundleName, ani_object enumIndex) +{ + LOGD("Enter GrantUriPermission."); + if (!IsSystemApp()) { + LOGE("%{public}s: GrantUriPermission is not System App!", __func__); + ThrowBusinessError(env, E_PERMISSION_SYS, "FileShare::GrantUriPermission is not System App!"); + return; + } + + UriPermissionInfo uriPermInfo; + std::string uriStr = ParseObjToStr(env, uri); + if (!CheckValidPublicUri(uriStr)) { + LOGE("%{public}s: GrantUriPermission uri is not valid!", __func__); + ThrowBusinessError(env, EINVAL, "GrantUriPermission uri is not valid!"); + return; + } + uriPermInfo.uri = uriStr; + std::string bundleNameStr = ParseObjToStr(env, bundleName); + uriPermInfo.bundleName = bundleNameStr; + + ani_int wantConstantFlag = ParseEnumToInt(env, static_cast(enumIndex)); + if (wantConstantFlag < 0) { + LOGE("%{public}s: GrantUriPermission ParseEnumToInt Faild!", __func__); + ThrowBusinessError(env, EINVAL, "GrantUriPermission is not System App!"); + return; + } + uriPermInfo.flag = static_cast(wantConstantFlag); + uriPermInfo.mode = GetModeFromFlag(wantConstantFlag); + LOGD("GrantUriPermission uri: %{public}s, bundleName: %{public}s, flag: %{public}d, mode: %{public}s", + uriStr.c_str(), bundleNameStr.c_str(), wantConstantFlag, uriPermInfo.mode.c_str()); + + int ret = DoGrantUriPermission(uriPermInfo); + if (ret < 0) { + LOGE("FileShare::GrantUriPermission DoGrantUriPermission failed with %{public}d", ret); + ThrowBusinessError(env, -ret, "GrantUriPermission failed"); + return; + } + LOGD("FileShare::GrantUriPermission DoGrantUriPermission successfully!"); +} +} // namespace ModuleFileShare +} // namespace AppFileService +} // namespace OHOS + +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + LOGD("Enter ANI_Constructor."); + ani_env *env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + LOGE("Unsupported ANI_VERSION_1"); + return ANI_ERROR; + } + + static const char *nsName = "L@ohos/fileshare/fileShare;"; + ani_namespace ns; + if (ANI_OK != env->FindNamespace(nsName, &ns)) { + LOGE("Not found namespace %{public}s.", nsName); + return ANI_NOT_FOUND; + } + std::array nsMethods = { + ani_native_function {"grantUriPermissionInner", nullptr, + reinterpret_cast(OHOS::AppFileService::ModuleFileShare::GrantUriPermission)}, + }; + if (ANI_OK != env->Namespace_BindNativeFunctions(ns, nsMethods.data(), nsMethods.size())) { + LOGE("Cannot bind native methods to namespace %{public}s.", nsName); + return ANI_ERROR; + }; + + *result = ANI_VERSION_1; + return ANI_OK; +} diff --git a/interfaces/kits/ani/file_uri/BUILD.gn b/interfaces/kits/ani/file_uri/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8537ed38e68c1e1116c5f840c438edf10dcb39a5 --- /dev/null +++ b/interfaces/kits/ani/file_uri/BUILD.gn @@ -0,0 +1,85 @@ +# 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/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/filemanagement/app_file_service/app_file_service.gni") + +ohos_shared_library("fileuri_ani") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + ubsan = true + cfi = true + cfi_cross_dso = true + debug = false + } + + cflags_cc = [ "-Wno-unused-function" ] + sources = [ + "../../../common/src/common_func.cpp", + "src/ani_file_uri.cpp", + ] + + include_dirs = [ + "include", + "../../../common/include", + "../../js/file_uri", + "${app_file_service_path}/interfaces/innerkits/native/file_uri/include", + ] + + deps = + [ "${app_file_service_path}/interfaces/innerkits/native:fileuri_native" ] + + external_deps = [ + "ability_base:zuri", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "file_api:filemgmt_libhilog", + "file_api:filemgmt_libn", + "hilog:libhilog", + "ipc:ipc_core", + "napi:ace_napi", + "runtime_core:ani", + "samgr:samgr_proxy", + ] + use_exceptions = true + part_name = "app_file_service" + subsystem_name = "filemanagement" + output_extension = "so" +} + +generate_static_abc("fileuri_abc") { + base_url = "./ets" + files = [ "./ets/@ohos.file.fileuri.ets" ] + dst_file = "$target_out_dir/fileuri.abc" + out_puts = [ "$target_out_dir/fileuri.abc" ] + is_boot_abc = "True" + device_dst_file = "/system/framework/fileuri.abc" +} + +ohos_prebuilt_etc("fileuri_abc_etc") { + source = "$target_out_dir/fileuri.abc" + module_install_dir = "framework" + subsystem_name = "filemanagement" + part_name = "app_file_service" + deps = [ ":fileuri_abc" ] +} + +group("fileuri_ani_package") { + deps = [ + ":fileuri_abc_etc", + ":fileuri_ani", + ] +} diff --git a/interfaces/kits/ani/file_uri/ets/@ohos.file.fileuri.ets b/interfaces/kits/ani/file_uri/ets/@ohos.file.fileuri.ets new file mode 100644 index 0000000000000000000000000000000000000000..19160e5bcbd2681d57fa60b66bf127778017260e --- /dev/null +++ b/interfaces/kits/ani/file_uri/ets/@ohos.file.fileuri.ets @@ -0,0 +1,82 @@ +/* + * 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 uri from '@ohos.uri'; + +class Cleaner { + static callback(cleaner: Cleaner): void { + console.println("enter Cleaner.callback"); + cleaner.clean() + } + + constructor(targetPtr: long) { + this.targetPtr = targetPtr + } + + native clean(): void + + private targetPtr: long = 0 +}; + +class FinalizationAgent { + constructor(obj: T, ptr: long) { + this.register(obj, ptr); + } + + register(obj: T, ptr: long): void { + if (this.unregisterToken) { + this.unregister(); + } + this.unregisterToken = {}; + this.cleaner = new Cleaner(ptr); + finalizer.register(obj, this.cleaner!, this.unregisterToken); + } + + unregister(): void { + finalizer.unregister(this.unregisterToken); + } + + private cleaner: Cleaner | null = null; + private unregisterToken: object = {}; +} + +let finalizer = new FinalizationRegistry(Cleaner.callback) + +export default namespace fileUri { + loadLibrary("fileuri_ani") + + export class FileUri { + native constructor(uriOrPath: string); + getFileUriEntity():long { + return this.fileUriEntity_; + } + + private fileUriEntity_: long = 0; + private acquireFileUriEntity(fileUriEntity: long) { + this.fileUriEntity_ = fileUriEntity; + if (this.fzAgent) { + this.fzAgent.unregister(); + } + this.fzAgent = new FinalizationAgent(this, this.fileUriEntity_); + } + + unregisterCleaner(): void { + this.fzAgent.unregister(); + } + + private fzAgent: FinalizationAgent; + }; + export native function getUriFromPath(path: string): string; +} diff --git a/interfaces/kits/ani/file_uri/include/ani_util_native_ptr.h b/interfaces/kits/ani/file_uri/include/ani_util_native_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..577f8453076c43c0423705201bc9926ccbaf6628 --- /dev/null +++ b/interfaces/kits/ani/file_uri/include/ani_util_native_ptr.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANI_UTIL_OBJECT_H +#define ANI_UTIL_OBJECT_H + +#include +#include + +#include +#include + +class NativeObject { +public: + virtual ~NativeObject() = default; +}; + +template +class StdSharedPtrHolder : public NativeObject { +public: + StdSharedPtrHolder(const std::shared_ptr &sptr) : sptr_(sptr) {} + + std::shared_ptr Get() + { + return sptr_; + } + + std::shared_ptr GetOrDefault() + { + if (!sptr_) { + sptr_ = std::make_shared(); + } + return sptr_; + } + +private: + std::shared_ptr sptr_; +}; + +template +class OhSharedPtrHolder : public NativeObject { +public: + OhSharedPtrHolder(const OHOS::sptr &sptr) : sptr_(sptr) {} + + OHOS::sptr Get() + { + return sptr_; + } + + OHOS::sptr GetOrDefault() + { + if (!sptr_) { + sptr_ = OHOS::sptr::MakeSptr(); + } + return sptr_; + } + +private: + OHOS::sptr sptr_; +}; + +class NativePtrWrapper { +public: + NativePtrWrapper(ani_env *env, ani_object object, const char *propName = "nativePtr") + : env_(env), obj_(object), propName_(propName) + { + } + + template + ani_status Wrap(T *nativePtr) + { + return env_->Object_SetFieldByName_Long(obj_, propName_.c_str(), reinterpret_cast(nativePtr)); + } + + template + T *Unwrap() + { + ani_long nativePtr; + if (ANI_OK != env_->Object_GetFieldByName_Long(obj_, propName_.c_str(), &nativePtr)) { + return nullptr; + } + return reinterpret_cast(nativePtr); + } + +private: + ani_env *env_ = nullptr; + ani_object obj_ = nullptr; + std::string propName_; +}; + +class NativePtrCleaner { +public: + static void Clean([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object) + { + ani_long ptr = 0; + if (ANI_OK != env->Object_GetFieldByName_Long(object, "targetPtr", &ptr)) { + return; + } + delete reinterpret_cast(ptr); + } + + NativePtrCleaner(ani_env *env) : env_(env) {} + + ani_status Bind(ani_class cls) + { + std::array methods = { + ani_native_function {"clean", nullptr, reinterpret_cast(NativePtrCleaner::Clean)}, + }; + + if (ANI_OK != env_->Class_BindNativeMethods(cls, methods.data(), methods.size())) { + return (ani_status)ANI_ERROR; + }; + + return ANI_OK; + } + +private: + ani_env *env_ = nullptr; +}; + +#endif diff --git a/interfaces/kits/ani/file_uri/src/ani_file_uri.cpp b/interfaces/kits/ani/file_uri/src/ani_file_uri.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f9c4f1f328ef7a9028277d248b72806ddc2324b --- /dev/null +++ b/interfaces/kits/ani/file_uri/src/ani_file_uri.cpp @@ -0,0 +1,219 @@ +/* + * 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_util_native_ptr.h" +#include "common_func.h" +#include "file_uri_entity.h" +#include "file_utils.h" +#include "log.h" +#include "n_error.h" +#include +#include +#include + +using namespace OHOS::AppFileService; + +static std::string ParseObjToStr(ani_env *env, ani_string stringObj) +{ + ani_size strSize; + env->String_GetUTF8Size(stringObj, &strSize); + std::vector buffer(strSize + 1); + char *utf8Buffer = buffer.data(); + + ani_size byteswritten = 0; + env->String_GetUTF8(stringObj, utf8Buffer, strSize + 1, &byteswritten); + + utf8Buffer[byteswritten] = '\0'; + std::string path = std::string(utf8Buffer); + return path; +} + +static ModuleFileUri::FileUriEntity *unwrapp(ani_env *env, ani_object object) +{ + ani_long fileUriEntityHolder_; + if (ANI_OK != env->Object_GetFieldByName_Long(object, "fileUriEntity_", &fileUriEntityHolder_)) { + LOGE("Get fileuriEntityHolder_ failed"); + return nullptr; + } + auto fileUriHolder = reinterpret_cast *>(fileUriEntityHolder_); + if (!fileUriHolder) { + LOGE("Get fileuriEntityHolder by long ptr failed"); + return nullptr; + } + return reinterpret_cast(fileUriHolder->Get().get()); +} + +static void ThrowBusinessError(ani_env *env, int errCode, std::string &&errMsg) +{ + LOGD("Begin ThrowBusinessError."); + static const char *errorClsName = "L@ohos/base/BusinessError;"; + ani_class cls {}; + if (ANI_OK != env->FindClass(errorClsName, &cls)) { + LOGE("find class BusinessError %{public}s failed", errorClsName); + return; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", ":V", &ctor)) { + LOGE("find method BusinessError.constructor failed"); + return; + } + ani_object errorObject; + if (ANI_OK != env->Object_New(cls, ctor, &errorObject)) { + LOGE("create BusinessError object failed"); + return; + } + ani_string errMsgStr; + if (ANI_OK != env->String_NewUTF8(errMsg.c_str(), errMsg.size(), &errMsgStr)) { + LOGE("convert errMsg to ani_string failed"); + return; + } + if (ANI_OK != env->Object_SetFieldByName_Double(errorObject, "code", static_cast(errCode))) { + LOGE("set error code failed"); + return; + } + if (ANI_OK != env->Object_SetPropertyByName_Ref(errorObject, "message", errMsgStr)) { + LOGE("set error message failed"); + return; + } + if (ANI_OK != env->ThrowError(static_cast(errorObject))) { + LOGE("throw error object failed"); + } + return; +} + +static ani_string GetUriFromPath(ani_env *env, ani_string stringObj) +{ + LOGD("Enter GetUriFromPath"); + ani_string uriObj = nullptr; + std::string path = ParseObjToStr(env, stringObj); + + std::string uri = CommonFunc::GetUriFromPath(path); + if (uri == "") { + LOGE("CommonFunc::GetUriFromPath failed!"); + ThrowBusinessError(env, OHOS::FileManagement::LibN::E_PARAMS, "CommonFunc::GetUriFromPath failed!"); + return uriObj; + } + LOGD("GetUriFromPath uri: %{public}s", uri.c_str()); + + ani_status status = env->String_NewUTF8(uri.c_str(), uri.size(), &uriObj); + if (status != ANI_OK) { + LOGE("String_NewUTF8 failed with status: %{public}d", status); + return nullptr; + } + + return uriObj; +} + +static void FileUriConstructor(ani_env *env, ani_object obj, ani_string stringObj) +{ + LOGD("Enter FileUriConstructor"); + std::string path = ParseObjToStr(env, stringObj); + if (path == "") { + LOGE("FileUriConstructor get path parameter failed!"); + ThrowBusinessError(env, EINVAL, "Failed to get path"); + return; + } + auto fileuriEntity = OHOS::FileManagement::CreateUniquePtr(path); + if (fileuriEntity == nullptr) { + LOGE("Failed to request heap memory."); + ThrowBusinessError(env, ENOMEM, "Failed to request heap memory."); + return; + } + StdSharedPtrHolder *holder = + new StdSharedPtrHolder(std::move(fileuriEntity)); + if (holder == nullptr) { + return; + } + + ani_namespace ns; + if (env->FindNamespace("L@ohos/file/fileuri/fileUri;", &ns) != ANI_OK) { + LOGE("Namespace L@ohos/file/fileuri/fileUri not found."); + delete holder; + ThrowBusinessError(env, EPERM, "Namespace L@ohos/file/fileuri/fileUri not found."); + return; + }; + + ani_class cls; + static const char *className = "LFileUri;"; + if (env->Namespace_FindClass(ns, className, &cls) != ANI_OK) { + LOGE("Not found class LFileUri in Namespace L@ohos/file/fileuri/fileUri."); + delete holder; + ThrowBusinessError(env, EPERM, "Class LFileUri not found."); + return; + } + + ani_method acquireObj; + if (ANI_OK != env->Class_FindMethod(cls, "acquireFileUriEntity", "J:V", &acquireObj)) { + LOGE("Not found method acquireFileUriEntity in class LFileUri."); + delete holder; + ThrowBusinessError(env, EPERM, "Method acquireFileUriEntity not found."); + return; + } + + if (ANI_OK != env->Object_CallMethod_Void(obj, acquireObj, reinterpret_cast(holder))) { + LOGE("Call method acquireFileUriEntity failed."); + delete holder; + ThrowBusinessError(env, EPERM, "Call method acquireFileUriEntity failed."); + return; + } +} + +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + LOGD("Enter ANI_Constructor."); + ani_env *env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + LOGE("Unsupported ANI_VERSION_1"); + return ANI_ERROR; + } + + static const char *nsName = "L@ohos/file/fileuri/fileUri;"; + ani_namespace ns; + if (ANI_OK != env->FindNamespace(nsName, &ns)) { + LOGE("Not found namespace %{public}s.", nsName); + return ANI_NOT_FOUND; + } + std::array nsMethods = { + ani_native_function {"getUriFromPath", nullptr, reinterpret_cast(GetUriFromPath)}, + }; + if (ANI_OK != env->Namespace_BindNativeFunctions(ns, nsMethods.data(), nsMethods.size())) { + LOGE("Cannot bind native methods to namespace %{public}s.", nsName); + return ANI_ERROR; + }; + + static const char *className = "LFileUri;"; + ani_class fileUriClass; + if (ANI_OK != env->Namespace_FindClass(ns, className, &fileUriClass)) { + LOGE("Not found class %{public}s in %{public}s.", nsName, nsName); + return ANI_NOT_FOUND; + } + std::array classMethods = { + ani_native_function {"", "Lstd/core/String;:V", reinterpret_cast(FileUriConstructor)}, + }; + if (ANI_OK != env->Class_BindNativeMethods(fileUriClass, classMethods.data(), classMethods.size())) { + LOGE("Cannot bind native methods to class %{public}s.", className); + return ANI_ERROR; + }; + + ani_class cleanerCls; + if (ANI_OK != env->FindClass("L@ohos/file/fileuri/Cleaner;", &cleanerCls)) { + LOGE("Not found class @ohos/file/fileuri/Cleaner;."); + return ANI_NOT_FOUND; + } + NativePtrCleaner(env).Bind(cleanerCls); + + *result = ANI_VERSION_1; + return ANI_OK; +} diff --git a/test/fuzztest/backupext_fuzzer/BUILD.gn b/test/fuzztest/backupext_fuzzer/BUILD.gn index d298eeae0992a0ac0fdd9e1e18925229043ef4f6..4ec3f847b038ee1310177f9bc03c4259f0a25a38 100644 --- a/test/fuzztest/backupext_fuzzer/BUILD.gn +++ b/test/fuzztest/backupext_fuzzer/BUILD.gn @@ -37,6 +37,7 @@ ohos_fuzztest("BackupExtFuzzTest") { ] sources = [ "${path_backup}/frameworks/native/backup_ext/src/ext_backup.cpp", + "${path_backup}/frameworks/native/backup_ext/src/ext_backup_ani.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", @@ -71,6 +72,7 @@ ohos_fuzztest("BackupExtFuzzTest") { "ipc:ipc_napi", "jsoncpp:jsoncpp", "napi:ace_napi", + "runtime_core:ani", ] defines = [ diff --git a/tests/unittests/backup_ext/BUILD.gn b/tests/unittests/backup_ext/BUILD.gn index 139df97bb1c8dcebb72cc4f9797e5788c9ef20aa..011a04f92550ec408ab8ea60bed34152eaf068d8 100644 --- a/tests/unittests/backup_ext/BUILD.gn +++ b/tests/unittests/backup_ext/BUILD.gn @@ -130,6 +130,7 @@ ohos_unittest("tar_file_test") { sources = [ "${path_backup}/frameworks/native/backup_ext/src/ext_backup.cpp", + "${path_backup}/frameworks/native/backup_ext/src/ext_backup_ani.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", @@ -181,6 +182,7 @@ ohos_unittest("tar_file_test") { "ipc:ipc_napi", "jsoncpp:jsoncpp", "napi:ace_napi", + "runtime_core:ani", "samgr:samgr_proxy", ] @@ -203,6 +205,7 @@ ohos_unittest("untar_file_sup_test") { sources = [ "${path_backup}/frameworks/native/backup_ext/src/ext_backup.cpp", + "${path_backup}/frameworks/native/backup_ext/src/ext_backup_ani.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", @@ -259,6 +262,7 @@ ohos_unittest("untar_file_sup_test") { "ipc:ipc_napi", "jsoncpp:jsoncpp", "napi:ace_napi", + "runtime_core:ani", "samgr:samgr_proxy", ] @@ -280,6 +284,7 @@ ohos_unittest("untar_file_test") { sources = [ "${path_backup}/frameworks/native/backup_ext/src/ext_backup.cpp", + "${path_backup}/frameworks/native/backup_ext/src/ext_backup_ani.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_context_js.cpp", "${path_backup}/frameworks/native/backup_ext/src/ext_backup_js.cpp", @@ -331,6 +336,7 @@ ohos_unittest("untar_file_test") { "ipc:ipc_napi", "jsoncpp:jsoncpp", "napi:ace_napi", + "runtime_core:ani", "samgr:samgr_proxy", ]