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