diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 2901748d0a0b362cb12a791cb6acbfe9d22440b1..f12c564b2047596887bfb10de8de7cc37a27becb 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -675,6 +675,8 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/class_stream/ani", "src/mod_fs/class_tasksignal", "src/mod_fs/class_tasksignal/ani", + "src/mod_fs/class_watcher", + "src/mod_fs/class_watcher/ani", "src/mod_fs/properties", "src/mod_fs/properties/ani", "src/mod_fs/properties/copy_listener", @@ -704,6 +706,12 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/class_stream/stream_instantiator.cpp", "src/mod_fs/class_tasksignal/ani/task_signal_ani.cpp", "src/mod_fs/class_tasksignal/task_signal_entity_core.cpp", + "src/mod_fs/class_watcher/ani/fs_watcher_ani.cpp", + "src/mod_fs/class_watcher/ani/fs_watcher_wrapper.cpp", + "src/mod_fs/class_watcher/ani/watch_event_listener.cpp", + "src/mod_fs/class_watcher/ani/watch_event_wrapper.cpp", + "src/mod_fs/class_watcher/fs_file_watcher.cpp", + "src/mod_fs/class_watcher/fs_watcher.cpp", "src/mod_fs/fs_utils.cpp", "src/mod_fs/properties/access_core.cpp", "src/mod_fs/properties/ani/access_ani.cpp", @@ -735,6 +743,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/ani/truncate_ani.cpp", "src/mod_fs/properties/ani/unlink_ani.cpp", "src/mod_fs/properties/ani/utimes_ani.cpp", + "src/mod_fs/properties/ani/watcher_ani.cpp", "src/mod_fs/properties/ani/write_ani.cpp", "src/mod_fs/properties/ani/xattr_ani.cpp", "src/mod_fs/properties/close_core.cpp", @@ -769,6 +778,7 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/properties/truncate_core.cpp", "src/mod_fs/properties/unlink_core.cpp", "src/mod_fs/properties/utimes_core.cpp", + "src/mod_fs/properties/watcher_core.cpp", "src/mod_fs/properties/write_core.cpp", "src/mod_fs/properties/xattr_core.cpp", ] @@ -791,6 +801,7 @@ ohos_shared_library("ani_fs_class") { "data_share:datashare_consumer", "dfs_service:distributed_file_daemon_kit_inner", "dfs_service:libdistributedfileutils", + "eventhandler:libeventhandler", "hilog:libhilog", "hisysevent:libhisysevent", "ipc:ipc_core", diff --git a/interfaces/kits/js/src/common/ani_helper/ani_helper.h b/interfaces/kits/js/src/common/ani_helper/ani_helper.h index 27ef05d38a58a9c0a57b0f1f1a19166494630636..df93167fb9af830daff087f8a2a9effaaf151cef 100644 --- a/interfaces/kits/js/src/common/ani_helper/ani_helper.h +++ b/interfaces/kits/js/src/common/ani_helper/ani_helper.h @@ -22,12 +22,17 @@ #include +#include "event_handler.h" +#include "event_runner.h" +#include "file_utils.h" #include "filemgmt_libhilog.h" #include "type_converter.h" namespace OHOS::FileManagement::ModuleFileIO::ANI { using namespace std; +static thread_local shared_ptr mainHandler; + class AniHelper { public: template @@ -129,15 +134,21 @@ public: return { true, make_optional(move(encoding)) }; } - static ani_env* GetThreadEnv(ani_vm *vm) + static ani_env *&GetThreadEnvStorage() + { + static thread_local ani_env *env { nullptr }; + return env; + } + + static ani_env *GetThreadEnv(ani_vm *vm) { - static thread_local ani_env *env {nullptr}; + auto &env = GetThreadEnvStorage(); if (env != nullptr) { return env; } - ani_option interopEnabled {"--interop=enable", nullptr}; - ani_options aniArgs {1, &interopEnabled}; + ani_option interopEnabled { "--interop=enable", nullptr }; + ani_options aniArgs { 1, &interopEnabled }; auto status = vm->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env); if (status != ANI_OK) { status = vm->GetEnv(ANI_VERSION_1, &env); @@ -151,6 +162,45 @@ public: return env; } + + static void DetachThreadEnv(ani_vm *vm) + { + if (vm && GetThreadEnvStorage()) { + auto status = vm->DetachCurrentThread(); + if (status != ANI_OK) { + HILOGE("Detach thread env from vm failed! status: %{private}d", status); + return; + } + GetThreadEnvStorage() = nullptr; + } + } + + static bool SendEventToMainThread(const function func) + { + if (func == nullptr) { + HILOGE("func is nullptr!"); + return false; + } + + if (mainHandler == nullptr) { + shared_ptr runner = OHOS::AppExecFwk::EventRunner::GetMainEventRunner(); + if (!runner) { + HILOGE("get main event runner failed!"); + return false; + } + mainHandler = CreateSharedPtr(runner); + if (mainHandler == nullptr) { + HILOGE("Failed to request heap memory."); + return false; + } + } + bool succ = mainHandler->PostTask(func, "", 0, OHOS::AppExecFwk::EventQueue::Priority::HIGH, {}); + if (!succ) { + HILOGE("Failed to post task to main thread."); + return false; + } + return true; + } }; } // namespace OHOS::FileManagement::ModuleFileIO::ANI diff --git a/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp b/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp index 2d7283c7d9887f878763c47d22f689e782262a7d..6f1f375328c4fb8c0cb00e795943389d99d4fa14 100644 --- a/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp +++ b/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp @@ -33,6 +33,7 @@ #include "file_ani.h" #include "filemgmt_libhilog.h" #include "fsync_ani.h" +#include "fs_watcher_ani.h" #include "listfile_ani.h" #include "lseek_ani.h" #include "lstat_ani.h" @@ -55,6 +56,7 @@ #include "truncate_ani.h" #include "unlink_ani.h" #include "utimes_ani.h" +#include "watcher_ani.h" #include "write_ani.h" #include "xattr_ani.h" @@ -75,6 +77,18 @@ static ani_status BindRafFileMethods(ani_env *env) return BindClass(env, className, methods); } +static ani_status BindWatcherClassMethods(ani_env *env) +{ + static const char *className = "L@ohos/file/fs/WatcherInner;"; + + std::array methods = { + ani_native_function { "start", nullptr, reinterpret_cast(FsWatcherAni::Start) }, + ani_native_function { "stop", nullptr, reinterpret_cast(FsWatcherAni::Stop) }, + }; + + return BindClass(env, className, methods); +} + static ani_status BindFileMethods(ani_env *env) { static const char *className = "L@ohos/file/fs/FileInner;"; @@ -158,6 +172,7 @@ static ani_status BindStaticMethods(ani_env *env) reinterpret_cast(CreateRandomAccessFileAni::CreateRandomAccessFileSync) }, ani_native_function { "createStreamSync", nullptr, reinterpret_cast(CreateStreamAni::CreateStreamSync) }, + ani_native_function { "createWatcherSync", nullptr, reinterpret_cast(WatcherAni::CreateWatcherSync) }, ani_native_function { "disConnectDfs", nullptr, reinterpret_cast(DisConnectDfsAni::DisConnectDfsSync) }, ani_native_function { "doAccessSync", nullptr, reinterpret_cast(AccessAni::AccessSync3) }, ani_native_function { "dup", nullptr, reinterpret_cast(DupAni::Dup) }, @@ -245,6 +260,11 @@ ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) return status; }; + if ((status = BindWatcherClassMethods(env)) != ANI_OK) { + HILOGE("Cannot bind native methods for Watcher Class"); + return status; + }; + *result = ANI_VERSION_1; return ANI_OK; } diff --git a/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets b/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets index 94bcf4694bedf653190a7882692907d27ff6deaf..fa2d2daf8679d4e5b23aebc0a526b5c155805d80 100644 --- a/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets +++ b/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets @@ -1105,6 +1105,7 @@ function createStream(path: string, mode: string, callback: AsyncCallback): void { let promise = taskpool.execute((target: string, srcPath: string): undefined => { @@ -2058,6 +2062,46 @@ enum WhenceType { SEEK_END = 2 } +export interface Watcher { + start(): void; + stop(): void; +} + +class WatcherInner implements Watcher { + private nativePtr: long = 0; + + constructor(ptr: long) { + if (this.nativePtr === 0) { + this.nativePtr = ptr; + } + } + + native start(): void; + + native stop(): void; +} + +export interface WatchEvent { + fileName: string; + event: number; + cookie: number; +} + +class WatchEventInner implements WatchEvent { + fileName: string; + event: number; + cookie: number; + + constructor(fileName: string, event: number, cookie: number) { + this.fileName = fileName; + this.event = event; + this.cookie = cookie; + } + +} + +export type WatchEventListener = (event: WatchEvent) => void; + class FileIoImpl { static { @@ -2084,6 +2128,8 @@ class FileIoImpl { static native createStreamSync(path: string, mode: string): Stream; + static native createWatcherSync(path: string, events: number, listener: WatchEventListener): Watcher; + static native fdopenStreamSync(fd: number, mode: string): Stream; static native dup(fd: number): File; diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_ani.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39d11f4968ca81336a673866d5a003b442a103d6 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_ani.cpp @@ -0,0 +1,68 @@ +/* + * 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 "fs_watcher_ani.h" + +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "fs_watcher.h" +#include "fs_watcher_wrapper.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace std; + +void FsWatcherAni::Start(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto watcher = FsWatcherWrapper::Unwrap(env, object); + if (watcher == nullptr) { + HILOGE("Cannot unwrap watcher!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + auto ret = watcher->Start(); + if (!ret.IsSuccess()) { + HILOGE("Cannot start watcher!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +void FsWatcherAni::Stop(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto watcher = FsWatcherWrapper::Unwrap(env, object); + if (watcher == nullptr) { + HILOGE("Cannot unwrap watcher!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + auto ret = watcher->Stop(); + if (!ret.IsSuccess()) { + HILOGE("Cannot stop watcher!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_ani.h b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..e6badfc7b61fd3428ea621f57c95fe21d421c297 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_ani.h @@ -0,0 +1,36 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_FS_WATCHER_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_FS_WATCHER_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class FsWatcherAni final { +public: + static void Start(ani_env *env, [[maybe_unused]] ani_object object); + static void Stop(ani_env *env, [[maybe_unused]] ani_object object); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_FS_WATCHER_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_wrapper.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca5212e4b0f86d6794671c490b31d7b846fc23c9 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_wrapper.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fs_watcher_wrapper.h" + +#include "filemgmt_libhilog.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace std; + +FsWatcher *FsWatcherWrapper::Unwrap(ani_env *env, ani_object object) +{ + ani_long nativePtr; + auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &nativePtr); + if (ret != ANI_OK) { + HILOGE("Unwrap fsWatcher err: %{private}d", ret); + return nullptr; + } + uintptr_t ptrValue = static_cast(nativePtr); + FsWatcher *watcher = reinterpret_cast(ptrValue); + return watcher; +} + +ani_object FsWatcherWrapper::Wrap(ani_env *env, const FsWatcher *watcher) +{ + static const char *className = "L@ohos/file/fs/WatcherInner;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", "J:V", &ctor)) { + HILOGE("Cannot find constructor method for class %s", className); + return nullptr; + } + ani_long ptr = static_cast(reinterpret_cast(watcher)); + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, ptr)) { + HILOGE("New %s obj Failed!", className); + return nullptr; + } + return obj; +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_wrapper.h b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..15d24f94e5453c6eafe1e95c6664c2e05bc11ef4 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/fs_watcher_wrapper.h @@ -0,0 +1,37 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_FS_WATCHER_WRAPPER_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_FS_WATCHER_WRAPPER_H + +#include +#include "fs_watcher.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class FsWatcherWrapper final { +public: + static FsWatcher *Unwrap(ani_env *env, ani_object object); + static ani_object Wrap(ani_env *env, const FsWatcher *watcher); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_FS_WATCHER_WRAPPER_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_listener.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_listener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..960dc4352ffc80138f172d2b1a0d4a577b38939d --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_listener.cpp @@ -0,0 +1,115 @@ +/* + * 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 "watch_event_listener.h" + +#include +#include "ani_helper.h" +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "type_converter.h" +#include "watch_event_wrapper.h" + +namespace OHOS::FileManagement::ModuleFileIO::ANI { +using namespace std; + +bool WatchEventListener::IsStrictEquals(const shared_ptr &other) const +{ + if (other->GetClassName() != className_) { + return false; + } + + const auto otherListener = static_pointer_cast(other); + if (!otherListener) { + HILOGE("Cannot convert IWatcherCallback to WatchEventListener"); + return false; + } + + ani_env *env = AniHelper::GetThreadEnv(vm); + if (!env) { + HILOGE("Current object's env is null"); + return false; + } + + ani_boolean isSame = false; + ani_status status = env->Reference_StrictEquals(callback, otherListener->callback, &isSame); + AniHelper::DetachThreadEnv(vm); + if (status != ANI_OK) { + HILOGE("Compare ref for strict equality failed. status = %{public}d", static_cast(status)); + return false; + } + return isSame; +} + +void WatchEventListener::InvokeCallback(const string &fileName, uint32_t event, uint32_t cookie) const +{ + auto watchEvent = CreateSharedPtr(); + if (watchEvent == nullptr) { + HILOGE("Failed to request heap memory."); + return; + } + + watchEvent->fileName = fileName; + watchEvent->event = event; + watchEvent->cookie = cookie; + SendWatchEvent(*watchEvent); + AniHelper::DetachThreadEnv(vm); +} + +inline static const int32_t ANI_SCOPE_SIZE = 16; + +void WatchEventListener::SendWatchEvent(const WatchEvent &watchEvent) const +{ + if (vm == nullptr) { + HILOGE("Cannot send WatchEvent because the vm is null."); + return; + } + if (callback == nullptr) { + HILOGE("Cannot send WatchEvent because the callback is null."); + return; + } + ani_size scopeSize = ANI_SCOPE_SIZE; + ani_env *env = AniHelper::GetThreadEnv(vm); + if (env == nullptr) { + HILOGE("Cannot send WatchEvent because the env is null."); + return; + } + ani_status status = env->CreateLocalScope(scopeSize); + if (status != ANI_OK) { + HILOGE("Failed to creat local scope, status: %{public}d", static_cast(status)); + return; + } + auto evtObj = WatchEventWrapper::Wrap(env, watchEvent); + if (evtObj == nullptr) { + HILOGE("Create WatchEvent obj failed!"); + return; + } + vector args = { static_cast(evtObj) }; + auto argc = args.size(); + ani_ref result; + auto cbObj = static_cast(callback); + status = env->FunctionalObject_Call(cbObj, argc, args.data(), &result); + if (status != ANI_OK) { + HILOGE("Failed to call FunctionalObject_Call, status: %{public}d", static_cast(status)); + // continue execution and not exit. + } + status = env->DestroyLocalScope(); + if (status != ANI_OK) { + HILOGE("Failed to destroy local scope, status: %{public}d", static_cast(status)); + return; + } +} + +} // namespace OHOS::FileManagement::ModuleFileIO::ANI diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_listener.h b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..919739228b992670dccb5441781c8fe0db0fe63c --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_listener.h @@ -0,0 +1,48 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_WATCH_EVENT_LISTENER_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_WATCH_EVENT_LISTENER_H + +#include + +#include + +#include "fs_watch_entity.h" +#include "i_watcher_callback.h" + +namespace OHOS::FileManagement::ModuleFileIO::ANI { + +class WatchEventListener final : public IWatcherCallback { +public: + WatchEventListener(ani_vm *vm, const ani_ref &callback) : vm(vm), callback(callback) {} + bool IsStrictEquals(const std::shared_ptr &other) const override; + void InvokeCallback(const std::string &fileName, uint32_t event, uint32_t cookie) const override; + + std::string GetClassName() const override + { + return className_; + } + +private: + inline static const std::string className_ = "WatchEventListener"; + void SendWatchEvent(const WatchEvent &watchEvent) const; + + ani_vm *vm; + ani_ref callback; +}; + +} // namespace OHOS::FileManagement::ModuleFileIO::ANI +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_WATCH_EVENT_LISTENER_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_wrapper.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..154ccd4cbf90cab562c23aa8bdc00f56f3c4c2da --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_wrapper.cpp @@ -0,0 +1,62 @@ +/* + * 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 "watch_event_wrapper.h" + +#include "filemgmt_libhilog.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace std; + +ani_object WatchEventWrapper::Wrap(ani_env *env, const WatchEvent &evt) +{ + static const char *className = "L@ohos/file/fs/WatchEventInner;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + HILOGE("Cannot find class %s", className); + return nullptr; + } + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", "Lstd/core/String;DD:V", &ctor)) { + HILOGE("Cannot find constructor method for class %s", className); + return nullptr; + } + + auto [succ, fileName] = TypeConverter::ToAniString(env, evt.fileName); + if (!succ) { + HILOGE("Convert fileName to ani string failed!"); + return nullptr; + } + + auto event = static_cast(evt.event); + auto cookie = static_cast(evt.cookie); + + ani_object obj; + if (ANI_OK != env->Object_New(cls, ctor, &obj, fileName, event, cookie)) { + HILOGE("Create %s obj failed!", className); + return nullptr; + } + return obj; +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_wrapper.h b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..8e2ca980c9302cccd122711aa7d9648f169904ae --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/ani/watch_event_wrapper.h @@ -0,0 +1,37 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_WATCH_EVENT_WRAPPER_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_WATCH_EVENT_WRAPPER_H + +#include +#include +#include "fs_watch_entity.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class WatchEventWrapper final { +public: + static ani_object Wrap(ani_env *env, const WatchEvent &evt); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_ANI_WATCH_EVENT_WRAPPER_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9796486f31f5110285e865452ad7ce09e93c22f --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp @@ -0,0 +1,309 @@ +/* + * 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 "fs_file_watcher.h" + +#include +#include +#include +#include +#include + +#include + +#include "filemgmt_libhilog.h" +#include "uv.h" + +namespace OHOS::FileManagement::ModuleFileIO { +using namespace std; + +FsFileWatcher::FsFileWatcher() {} + +FsFileWatcher::~FsFileWatcher() {} + +int32_t FsFileWatcher::GetNotifyId() +{ + return notifyFd_; +} + +bool FsFileWatcher::InitNotify() +{ + notifyFd_ = inotify_init(); + if (notifyFd_ < 0) { + HILOGE("Failed to init notify errCode:%{public}d", errno); + return false; + } + eventFd_ = eventfd(0, EFD_CLOEXEC); + if (eventFd_ < 0) { + HILOGE("Failed to init eventfd errCode:%{public}d", errno); + return false; + } + return true; +} + +tuple FsFileWatcher::CheckEventWatched(const string &fileName, const uint32_t &event) +{ + int32_t wd = -1; + auto iter = wdFileNameMap_.find(fileName); + if (iter == wdFileNameMap_.end()) { + return { false, wd }; + } + wd = iter->second.first; + if ((iter->second.second & event) == event) { + return { true, wd }; + } + return { false, wd }; +} + +int32_t FsFileWatcher::StartNotify(shared_ptr info) +{ + lock_guard lock(watchMutex_); + if (notifyFd_ < 0) { + HILOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_); + return EIO; + } + + auto [isWatched, wd] = CheckEventWatched(info->fileName, info->events); + if (isWatched && wd > 0) { + info->wd = wd; + return ERRNO_NOERR; + } + uint32_t watchEvents = 0; + if (wd != -1) { + watchEvents = wdFileNameMap_[info->fileName].second | info->events; + } else { + watchEvents = info->events; + } + int32_t newWd = inotify_add_watch(notifyFd_, info->fileName.c_str(), watchEvents); + if (newWd < 0) { + HILOGE("Failed to start notify errCode:%{public}d", errno); + return errno; + } + info->wd = newWd; + wdFileNameMap_[info->fileName].first = newWd; + wdFileNameMap_[info->fileName].second = watchEvents; + return ERRNO_NOERR; +} + +int32_t FsFileWatcher::NotifyToWatchNewEvents(const string &fileName, const int32_t &wd, const uint32_t &watchEvents) +{ + int32_t newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents); + if (newWd < 0) { + HILOGE("Failed to start new notify errCode:%{public}d", errno); + return errno; + } + + if (newWd != wd) { + HILOGE("New notify wd is error"); + return EIO; + } + wdFileNameMap_[fileName].second = watchEvents; + return ERRNO_NOERR; +} + +int32_t FsFileWatcher::CloseNotifyFd() +{ + int32_t closeRet = ERRNO_NOERR; + int32_t fd = notifyFd_; + + if (watcherInfoSet_.size() == 0) { + run_ = false; + notifyFd_ = -1; + closeRet = close(fd); + if (closeRet != 0) { + HILOGE("Failed to stop notify close fd errCode:%{public}d", errno); + } + notifyFd_ = -1; + closeRet = close(eventFd_); + if (closeRet != 0) { + HILOGE("Failed to close eventfd errCode:%{public}d", errno); + } + eventFd_ = -1; + DestroyTaskThead(); + } + return closeRet; +} + +int32_t FsFileWatcher::StopNotify(shared_ptr info) +{ + unique_lock lock(watchMutex_); + if (notifyFd_ < 0) { + HILOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_); + return EIO; + } + uint32_t newEvents = RemoveWatcherInfo(info); + if (newEvents > 0) { + if (access(info->fileName.c_str(), F_OK) == 0) { + return NotifyToWatchNewEvents(info->fileName, info->wd, newEvents); + } + HILOGE("The Watched file does not exist, and the remaining monitored events will be invalid."); + return ERRNO_NOERR; + } + if (inotify_rm_watch(notifyFd_, info->wd) == -1) { + int32_t rmErr = errno; + if (access(info->fileName.c_str(), F_OK) == 0) { + HILOGE("Failed to stop notify errCode:%{public}d", rmErr); + wdFileNameMap_.erase(info->fileName); + CloseNotifyFd(); + return rmErr; + } + } + wdFileNameMap_.erase(info->fileName); + return CloseNotifyFd(); +} + +void FsFileWatcher::ReadNotifyEvent() +{ + int32_t len = 0; + int32_t index = 0; + char buf[BUF_SIZE] = { 0 }; + struct inotify_event *event = nullptr; + do { + len = read(notifyFd_, &buf, sizeof(buf)); + if (len < 0 && errno != EINTR) { + HILOGE("Read notify event failed! ret: %d", errno); + break; + } + } while (len < 0); + while (index < len) { + event = reinterpret_cast(buf + index); + NotifyEvent(event); + index += sizeof(struct inotify_event) + static_cast(event->len); + } +} + +void FsFileWatcher::AsyncGetNotifyEvent() +{ + lock_guard lock(taskMutex_); + if (!taskRunning_) { + taskRunning_ = true; + taskThead_ = thread(&FsFileWatcher::GetNotifyEvent, this); + } +} + +void FsFileWatcher::GetNotifyEvent() +{ + if (run_) { + return; + } + run_ = true; + nfds_t nfds = 2; + struct pollfd fds[2]; + fds[0].fd = eventFd_; + fds[0].events = 0; + fds[1].fd = notifyFd_; + fds[1].events = POLLIN; + int32_t ret = 0; + while (run_) { + ret = poll(fds, nfds, -1); + if (ret > 0) { + if (static_cast(fds[0].revents) & POLLNVAL) { + run_ = false; + return; + } + if (static_cast(fds[1].revents) & POLLIN) { + ReadNotifyEvent(); + } + } else if (ret < 0 && errno == EINTR) { + continue; + } else { + HILOGE("Failed to poll NotifyFd, errno=%{public}d", errno); + return; + } + } +} + +bool FsFileWatcher::AddWatcherInfo(const string &fileName, shared_ptr info) +{ + for (auto &iter : watcherInfoSet_) { + if (iter->fileName == info->fileName && iter->events == info->events) { + bool isSame = iter->callback->IsStrictEquals(info->callback); + if (isSame) { + HILOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str()); + return false; + } + } + } + watcherInfoSet_.insert(info); + return true; +} + +uint32_t FsFileWatcher::RemoveWatcherInfo(shared_ptr info) +{ + watcherInfoSet_.erase(info); + uint32_t otherEvents = 0; + for (const auto &iter : watcherInfoSet_) { + if (iter->fileName == info->fileName && iter->wd > 0) { + otherEvents |= iter->events; + } + } + return otherEvents; +} + +static bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event) +{ + if ((mask & event) > 0) { + return true; + } + return false; +} + +void FsFileWatcher::NotifyEvent(const struct inotify_event *event) +{ + lock_guard lock(watchMutex_); + string tempFileName; + auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(), + [event](const pair> &iter) { return iter.second.first == event->wd; }); + if (found != wdFileNameMap_.end()) { + tempFileName = found->first; + } + + for (const auto &iter : watcherInfoSet_) { + string fileName = tempFileName; + uint32_t watchEvent = 0; + if ((iter->fileName == fileName) && (iter->wd > 0)) { + watchEvent = iter->events; + } + if (!CheckIncludeEvent(event->mask, watchEvent)) { + continue; + } + if (event->len > 0) { + fileName += "/" + string(event->name); + } + iter->TriggerCallback(fileName, event->mask & IN_ALL_EVENTS, event->cookie); + } +} + +bool FsFileWatcher::CheckEventValid(const uint32_t &event) +{ + if ((event & IN_ALL_EVENTS) == event) { + return true; + } else { + HILOGE("Param event:%{public}x is not valid", event); + return false; + } +} + +void FsFileWatcher::DestroyTaskThead() +{ + if (taskThead_.joinable()) { + taskThead_.join(); + } + + lock_guard lock(taskMutex_); + if (taskRunning_) { + taskRunning_ = false; + } +} +} // namespace OHOS::FileManagement::ModuleFileIO diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h new file mode 100644 index 0000000000000000000000000000000000000000..12a8ef7f88a8388ca9872e903306430b9483a748 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h @@ -0,0 +1,74 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_FILE_WATCHER_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_FILE_WATCHER_H + +#include +#include +#include +#include +#include + +#include + +#include "filemgmt_libfs.h" +#include "fs_watch_entity.h" +#include "singleton.h" + +namespace OHOS::FileManagement::ModuleFileIO { +using namespace std; + +constexpr int BUF_SIZE = 1024; + +class FsFileWatcher : public Singleton { +public: + FsFileWatcher(); + ~FsFileWatcher(); + int32_t GetNotifyId(); + bool InitNotify(); + int StartNotify(shared_ptr info); + int StopNotify(shared_ptr info); + void GetNotifyEvent(); + void AsyncGetNotifyEvent(); + bool AddWatcherInfo(const string &fileName, shared_ptr info); + bool CheckEventValid(const uint32_t &event); + +private: + uint32_t RemoveWatcherInfo(shared_ptr info); + tuple CheckEventWatched(const string &fileName, const uint32_t &event); + void NotifyEvent(const struct inotify_event *event); + int CloseNotifyFd(); + int NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents); + void ReadNotifyEvent(); + void DestroyTaskThead(); + +private: + mutex taskMutex_; + atomic taskRunning_ = false; + thread taskThead_; + + mutex watchMutex_; + bool run_ = false; + int32_t notifyFd_ = -1; + int32_t eventFd_ = -1; + unordered_set> watcherInfoSet_; + unordered_map> wdFileNameMap_; + + FsFileWatcher(const FsFileWatcher &) = delete; + FsFileWatcher &operator=(const FsFileWatcher &) = delete; +}; + +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_FILE_WATCHER_H diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_watch_entity.h b/interfaces/kits/js/src/mod_fs/class_watcher/fs_watch_entity.h new file mode 100644 index 0000000000000000000000000000000000000000..88fe92b1869b6288404a0365d0e2be108325bd37 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_watch_entity.h @@ -0,0 +1,50 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_WATCH_ENTITY_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_WATCH_ENTITY_H + +#include +#include +#include +#include "i_watcher_callback.h" + +namespace OHOS::FileManagement::ModuleFileIO { + +struct WatchEvent { + std::string fileName = ""; + uint32_t event = 0; + uint32_t cookie = 0; +}; + +struct WatcherInfo { + std::string fileName = ""; + uint32_t events = 0; + int32_t wd = -1; + std::shared_ptr callback; + + explicit WatcherInfo(std::shared_ptr callback) : callback(std::move(callback)) {} + + void TriggerCallback(const std::string &fileName, uint32_t event, uint32_t cookie) const + { + callback->InvokeCallback(fileName, event, cookie); + } +}; + +struct FsWatchEntity { + std::shared_ptr data_; +}; + +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_WATCH_ENTITY_H diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_watcher.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/fs_watcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6be59006599de21006b235eb93efcb7932ba47b9 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_watcher.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fs_watcher.h" + +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "fs_file_watcher.h" +#include "fs_watch_entity.h" +#include "securec.h" + +namespace OHOS::FileManagement::ModuleFileIO { +using namespace std; + +FsResult FsWatcher::Constructor() +{ + auto watchEntity = CreateUniquePtr(); + if (watchEntity == nullptr) { + HILOGE("Failed to request heap memory."); + return FsResult::Error(ENOMEM); + } + + FsWatcher *watcherPtr = new FsWatcher(move(watchEntity)); + + if (watcherPtr == nullptr) { + HILOGE("Failed to create FsWatcher object on heap."); + return FsResult::Error(ENOMEM); + } + + return FsResult::Success(move(watcherPtr)); +} + +FsResult FsWatcher::Stop() +{ + if (!watchEntity) { + HILOGE("Failed to get watchEntity when stop."); + return FsResult::Error(EINVAL); + } + int ret = FsFileWatcher::GetInstance().StopNotify(watchEntity->data_); + if (ret != ERRNO_NOERR) { + HILOGE("Failed to stopNotify errno:%{public}d", errno); + return FsResult::Error(ret); + } + return FsResult::Success(); +} + +FsResult FsWatcher::Start() +{ + if (!watchEntity) { + HILOGE("Failed to get watchEntity when start."); + return FsResult::Error(EINVAL); + } + + shared_ptr info = watchEntity->data_; + int ret = FsFileWatcher::GetInstance().StartNotify(info); + if (ret != ERRNO_NOERR) { + HILOGE("Failed to startNotify."); + return FsResult::Error(ret); + } + + FsFileWatcher::GetInstance().AsyncGetNotifyEvent(); + + return FsResult::Success(); +} + +} // namespace OHOS::FileManagement::ModuleFileIO diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_watcher.h b/interfaces/kits/js/src/mod_fs/class_watcher/fs_watcher.h new file mode 100644 index 0000000000000000000000000000000000000000..46a1c5340b5c249e7c62b54bc95196bca1188003 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_watcher.h @@ -0,0 +1,48 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_WATCHER_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_WATCHER_H + +#include "filemgmt_libfs.h" +#include "fs_watch_entity.h" + +namespace OHOS::FileManagement::ModuleFileIO { + +class FsWatcher { +public: + static FsResult Constructor(); + FsResult Start(); + FsResult Stop(); + + FsWatchEntity *GetWatchEntity() const + { + return watchEntity.get(); + } + + FsWatcher(const FsWatcher &) = delete; + FsWatcher &operator=(const FsWatcher &) = delete; + + FsWatcher(FsWatcher &&) noexcept = default; + FsWatcher &operator=(FsWatcher &&) noexcept = default; + + ~FsWatcher() = default; + +private: + unique_ptr watchEntity; + explicit FsWatcher(unique_ptr entity) : watchEntity(move(entity)) {} +}; +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_FS_WATCHER_H diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/i_watcher_callback.h b/interfaces/kits/js/src/mod_fs/class_watcher/i_watcher_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..a6baaa78226de612f9a3eb321b823887b02cd4eb --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/i_watcher_callback.h @@ -0,0 +1,32 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_I_WATCHER_CALLBACK_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_I_WATCHER_CALLBACK_H + +#include + +namespace OHOS::FileManagement::ModuleFileIO { + +class IWatcherCallback { +public: + virtual ~IWatcherCallback() = default; + virtual bool IsStrictEquals(const std::shared_ptr &other) const = 0; + virtual void InvokeCallback(const std::string &fileName, uint32_t event, uint32_t cookie) const = 0; + virtual std::string GetClassName() const = 0; +}; + +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_I_WATCHER_CALLBACK_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/watcher_ani.cpp b/interfaces/kits/js/src/mod_fs/properties/ani/watcher_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04e281ab0f3ab54333cb3b01773603ab58ea85cb --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/watcher_ani.cpp @@ -0,0 +1,77 @@ +/* + * 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 "watcher_ani.h" + +#include "ani_helper.h" +#include "error_handler.h" +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "fs_watcher_wrapper.h" +#include "watch_event_listener.h" +#include "watcher_core.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace OHOS::FileManagement::ModuleFileIO; + +ani_object WatcherAni::CreateWatcherSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path, ani_double events, ani_ref listener) +{ + auto [succPath, filePath] = TypeConverter::ToUTF8String(env, path); + if (!succPath) { + HILOGE("Invalid path"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + + ani_ref cbRef; + if (ANI_OK != env->GlobalReference_Create(move(listener), &cbRef)) { + HILOGE("Failed to get callback"); + ErrorHandler::Throw(env, EINVAL); + return nullptr; + } + ani_vm *vm = nullptr; + env->GetVM(&vm); + auto eventListener = CreateSharedPtr(vm, cbRef); + if (eventListener == nullptr) { + HILOGE("Failed to request heap memory."); + ErrorHandler::Throw(env, ENOMEM); + return nullptr; + } + + FsResult ret = + WatcherCore::DoCreateWatcher(filePath, static_cast(events), move(eventListener)); + if (!ret.IsSuccess()) { + HILOGE("Create watcher failed"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return nullptr; + } + const FsWatcher *watcher = ret.GetData().value(); + auto result = FsWatcherWrapper::Wrap(env, move(watcher)); + if (result == nullptr) { + ErrorHandler::Throw(env, UNKNOWN_ERR); + return nullptr; + } + return result; +} +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/ani/watcher_ani.h b/interfaces/kits/js/src/mod_fs/properties/ani/watcher_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..1175c2f6f67878a72c323ffbad99c9594b2e1682 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/ani/watcher_ani.h @@ -0,0 +1,36 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_WATCHER_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_WATCHER_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class WatcherAni final { +public: + static ani_object CreateWatcherSync( + ani_env *env, [[maybe_unused]] ani_class clazz, ani_string path, ani_double events, ani_ref listener); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS + +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_WATCHER_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp b/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp new file mode 100644 index 0000000000000000000000000000000000000000..382f494ec67385ce04a25f7bd2d66e0be80403c8 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp @@ -0,0 +1,108 @@ +/* + * 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 "watcher_core.h" + +#include +#include +#include + +#include "file_utils.h" +#include "filemgmt_libhilog.h" +#include "fs_file_watcher.h" + +namespace OHOS::FileManagement::ModuleFileIO { +using namespace std; + +static FsResult InstantiateWatcher() +{ + if (FsFileWatcher::GetInstance().GetNotifyId() < 0 && !FsFileWatcher::GetInstance().InitNotify()) { + HILOGE("Failed to get notifyId or initnotify fail"); + return FsResult::Error(errno); + } + FsResult result = FsWatcher::Constructor(); + if (!result.IsSuccess()) { + HILOGE("Failed to instantiate watcher"); + return FsResult::Error(EIO); + } + return result; +} + +shared_ptr ToWatcherInfo( + const string &path, const int32_t events, shared_ptr callback, int32_t &errCode) +{ + if (events <= 0 || !FsFileWatcher::GetInstance().CheckEventValid(events)) { + HILOGE("Failed to get watcher event."); + errCode = EINVAL; + return nullptr; + } + + if (!callback) { + HILOGE("Failed to get callback"); + errCode = EINVAL; + return nullptr; + } + + auto info = CreateSharedPtr(callback); + if (info == nullptr) { + HILOGE("Failed to request heap memory."); + errCode = ENOMEM; + return nullptr; + } + + info->events = static_cast(events); + info->fileName = move(path); + return info; +} + +FsResult WatcherCore::DoCreateWatcher( + const string &path, const int32_t events, shared_ptr callback) +{ + int errCode = 0; + auto info = ToWatcherInfo(path, events, callback, errCode); + if (errCode != 0) { + HILOGE("Failed to parse param"); + return FsResult::Error(errCode); + } + + auto result = InstantiateWatcher(); + if (!result.IsSuccess()) { + return result; + } + + const FsWatcher *objWatcher = result.GetData().value(); + if (!objWatcher) { + HILOGE("Failed to get fsWatcher"); + return FsResult::Error(EIO); + } + + auto *watchEntity = objWatcher->GetWatchEntity(); + if (!watchEntity) { + HILOGE("Failed to get watchEntity."); + delete objWatcher; + objWatcher = nullptr; + return FsResult::Error(EIO); + } + + watchEntity->data_ = info; + + bool ret = FsFileWatcher::GetInstance().AddWatcherInfo(info->fileName, info); + if (!ret) { + HILOGE("Failed to add watcher info."); + return FsResult::Error(EINVAL); + } + return result; +} +} // namespace OHOS::FileManagement::ModuleFileIO diff --git a/interfaces/kits/js/src/mod_fs/properties/watcher_core.h b/interfaces/kits/js/src/mod_fs/properties/watcher_core.h new file mode 100644 index 0000000000000000000000000000000000000000..4a303dbcb490026b810424621226cb82e6d11a31 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/properties/watcher_core.h @@ -0,0 +1,32 @@ +/* + * 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 INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_WATCHER_CORE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_WATCHER_CORE_H + +#include "filemgmt_libfs.h" +#include "fs_watch_entity.h" +#include "fs_watcher.h" + +namespace OHOS::FileManagement::ModuleFileIO { + +class WatcherCore final { +public: + static FsResult DoCreateWatcher( + const std::string &path, const int32_t events, std::shared_ptr callback); +}; + +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_PROPERTIES_WATCHER_CORE_H \ No newline at end of file