1 /* //device/libs/android_runtime/android_util_FileObserver.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include <nativehelper/JNIHelp.h>
19 #include <nativehelper/ScopedPrimitiveArray.h>
20 #include <nativehelper/ScopedUtfChars.h>
21 #include "jni.h"
22 #include "utils/Log.h"
23 #include "utils/misc.h"
24 #include "core_jni_helpers.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdint.h>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #include <errno.h>
33
34 #if defined(__linux__)
35 #include <sys/inotify.h>
36 #endif
37
38 namespace android {
39
40 static jmethodID method_onEvent;
41
android_os_fileobserver_init(JNIEnv * env,jobject object)42 static jint android_os_fileobserver_init(JNIEnv* env, jobject object)
43 {
44 #if defined(__linux__)
45 return (jint)inotify_init1(IN_CLOEXEC);
46 #else
47 return -1;
48 #endif
49 }
50
android_os_fileobserver_observe(JNIEnv * env,jobject object,jint fd)51 static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd)
52 {
53 #if defined(__linux__)
54
55 char event_buf[512];
56 struct inotify_event* event;
57
58 while (1)
59 {
60 int event_pos = 0;
61 int num_bytes = read(fd, event_buf, sizeof(event_buf));
62
63 if (num_bytes < (int)sizeof(*event))
64 {
65 if (errno == EINTR)
66 continue;
67
68 ALOGE("***** ERROR! android_os_fileobserver_observe() got a short event!");
69 return;
70 }
71
72 while (num_bytes >= (int)sizeof(*event))
73 {
74 int event_size;
75 event = (struct inotify_event *)(event_buf + event_pos);
76
77 jstring path = NULL;
78
79 if (event->len > 0)
80 {
81 path = env->NewStringUTF(event->name);
82 }
83
84 env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path);
85 if (env->ExceptionCheck()) {
86 env->ExceptionDescribe();
87 env->ExceptionClear();
88 }
89 if (path != NULL)
90 {
91 env->DeleteLocalRef(path);
92 }
93
94 event_size = sizeof(*event) + event->len;
95 num_bytes -= event_size;
96 event_pos += event_size;
97 }
98 }
99
100 #endif
101 }
102
android_os_fileobserver_startWatching(JNIEnv * env,jobject object,jint fd,jobjectArray pathStrings,jint mask,jintArray wfdArray)103 static void android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd,
104 jobjectArray pathStrings, jint mask,
105 jintArray wfdArray)
106 {
107 ScopedIntArrayRW wfds(env, wfdArray);
108 if (wfds.get() == nullptr) {
109 jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRW");
110 }
111
112 #if defined(__linux__)
113
114 if (fd >= 0)
115 {
116 size_t count = wfds.size();
117 for (jsize i = 0; i < count; ++i) {
118 jstring pathString = (jstring) env->GetObjectArrayElement(pathStrings, i);
119
120 ScopedUtfChars path(env, pathString);
121
122 wfds[i] = inotify_add_watch(fd, path.c_str(), mask);
123 }
124 }
125
126 #endif
127 }
128
android_os_fileobserver_stopWatching(JNIEnv * env,jobject object,jint fd,jintArray wfdArray)129 static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object,
130 jint fd, jintArray wfdArray)
131 {
132 #if defined(__linux__)
133
134 ScopedIntArrayRO wfds(env, wfdArray);
135 if (wfds.get() == nullptr) {
136 jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRO");
137 }
138 size_t count = wfds.size();
139 for (size_t i = 0; i < count; ++i) {
140 inotify_rm_watch((int)fd, (uint32_t)wfds[i]);
141 }
142
143 #endif
144 }
145
146 static const JNINativeMethod sMethods[] = {
147 /* name, signature, funcPtr */
148 { "init", "()I", (void*)android_os_fileobserver_init },
149 { "observe", "(I)V", (void*)android_os_fileobserver_observe },
150 { "startWatching", "(I[Ljava/lang/String;I[I)V", (void*)android_os_fileobserver_startWatching },
151 { "stopWatching", "(I[I)V", (void*)android_os_fileobserver_stopWatching }
152
153 };
154
register_android_os_FileObserver(JNIEnv * env)155 int register_android_os_FileObserver(JNIEnv* env)
156 {
157 jclass clazz = FindClassOrDie(env, "android/os/FileObserver$ObserverThread");
158
159 method_onEvent = GetMethodIDOrDie(env, clazz, "onEvent", "(IILjava/lang/String;)V");
160
161 return RegisterMethodsOrDie(env, "android/os/FileObserver$ObserverThread", sMethods,
162 NELEM(sMethods));
163 }
164
165 } /* namespace android */
166