| /* //device/libs/android_runtime/android_util_FileObserver.cpp |
| ** |
| ** Copyright 2006, The Android Open Source Project |
| ** |
| ** 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 <nativehelper/JNIHelp.h> |
| #include <nativehelper/ScopedPrimitiveArray.h> |
| #include <nativehelper/ScopedUtfChars.h> |
| #include "jni.h" |
| #include "utils/Log.h" |
| #include "utils/misc.h" |
| #include "core_jni_helpers.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <errno.h> |
| |
| #if defined(__linux__) |
| #include <sys/inotify.h> |
| #endif |
| |
| namespace android { |
| |
| static jmethodID method_onEvent; |
| |
| static jint android_os_fileobserver_init(JNIEnv* env, jobject object) |
| { |
| #if defined(__linux__) |
| return (jint)inotify_init(); |
| #else |
| return -1; |
| #endif |
| } |
| |
| static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd) |
| { |
| #if defined(__linux__) |
| |
| char event_buf[512]; |
| struct inotify_event* event; |
| |
| while (1) |
| { |
| int event_pos = 0; |
| int num_bytes = read(fd, event_buf, sizeof(event_buf)); |
| |
| if (num_bytes < (int)sizeof(*event)) |
| { |
| if (errno == EINTR) |
| continue; |
| |
| ALOGE("***** ERROR! android_os_fileobserver_observe() got a short event!"); |
| return; |
| } |
| |
| while (num_bytes >= (int)sizeof(*event)) |
| { |
| int event_size; |
| event = (struct inotify_event *)(event_buf + event_pos); |
| |
| jstring path = NULL; |
| |
| if (event->len > 0) |
| { |
| path = env->NewStringUTF(event->name); |
| } |
| |
| env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path); |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| } |
| if (path != NULL) |
| { |
| env->DeleteLocalRef(path); |
| } |
| |
| event_size = sizeof(*event) + event->len; |
| num_bytes -= event_size; |
| event_pos += event_size; |
| } |
| } |
| |
| #endif |
| } |
| |
| static void android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, |
| jobjectArray pathStrings, jint mask, |
| jintArray wfdArray) |
| { |
| ScopedIntArrayRW wfds(env, wfdArray); |
| if (wfds.get() == nullptr) { |
| jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRW"); |
| } |
| |
| #if defined(__linux__) |
| |
| if (fd >= 0) |
| { |
| size_t count = wfds.size(); |
| for (jsize i = 0; i < count; ++i) { |
| jstring pathString = (jstring) env->GetObjectArrayElement(pathStrings, i); |
| |
| ScopedUtfChars path(env, pathString); |
| |
| wfds[i] = inotify_add_watch(fd, path.c_str(), mask); |
| } |
| } |
| |
| #endif |
| } |
| |
| static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object, |
| jint fd, jintArray wfdArray) |
| { |
| #if defined(__linux__) |
| |
| ScopedIntArrayRO wfds(env, wfdArray); |
| if (wfds.get() == nullptr) { |
| jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRO"); |
| } |
| size_t count = wfds.size(); |
| for (size_t i = 0; i < count; ++i) { |
| inotify_rm_watch((int)fd, (uint32_t)wfds[i]); |
| } |
| |
| #endif |
| } |
| |
| static const JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| { "init", "()I", (void*)android_os_fileobserver_init }, |
| { "observe", "(I)V", (void*)android_os_fileobserver_observe }, |
| { "startWatching", "(I[Ljava/lang/String;I[I)V", (void*)android_os_fileobserver_startWatching }, |
| { "stopWatching", "(I[I)V", (void*)android_os_fileobserver_stopWatching } |
| |
| }; |
| |
| int register_android_os_FileObserver(JNIEnv* env) |
| { |
| jclass clazz = FindClassOrDie(env, "android/os/FileObserver$ObserverThread"); |
| |
| method_onEvent = GetMethodIDOrDie(env, clazz, "onEvent", "(IILjava/lang/String;)V"); |
| |
| return RegisterMethodsOrDie(env, "android/os/FileObserver$ObserverThread", sMethods, |
| NELEM(sMethods)); |
| } |
| |
| } /* namespace android */ |