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 "JNIHelp.h"
19#include "jni.h"
20#include "utils/Log.h"
21#include "utils/misc.h"
22#include "core_jni_helpers.h"
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdint.h>
28#include <fcntl.h>
29#include <sys/ioctl.h>
30#include <errno.h>
31
32#if defined(__linux__)
33#include <sys/inotify.h>
34#endif
35
36namespace android {
37
38static jmethodID method_onEvent;
39
40static jint android_os_fileobserver_init(JNIEnv* env, jobject object)
41{
42#if defined(__linux__)
43    return (jint)inotify_init();
44#else
45    return -1;
46#endif
47}
48
49static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd)
50{
51#if defined(__linux__)
52
53    char event_buf[512];
54    struct inotify_event* event;
55
56    while (1)
57    {
58        int event_pos = 0;
59        int num_bytes = read(fd, event_buf, sizeof(event_buf));
60
61        if (num_bytes < (int)sizeof(*event))
62        {
63            if (errno == EINTR)
64                continue;
65
66            ALOGE("***** ERROR! android_os_fileobserver_observe() got a short event!");
67            return;
68        }
69
70        while (num_bytes >= (int)sizeof(*event))
71        {
72            int event_size;
73            event = (struct inotify_event *)(event_buf + event_pos);
74
75            jstring path = NULL;
76
77            if (event->len > 0)
78            {
79                path = env->NewStringUTF(event->name);
80            }
81
82            env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path);
83            if (env->ExceptionCheck()) {
84                env->ExceptionDescribe();
85                env->ExceptionClear();
86            }
87            if (path != NULL)
88            {
89                env->DeleteLocalRef(path);
90            }
91
92            event_size = sizeof(*event) + event->len;
93            num_bytes -= event_size;
94            event_pos += event_size;
95        }
96    }
97
98#endif
99}
100
101static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask)
102{
103    int res = -1;
104
105#if defined(__linux__)
106
107    if (fd >= 0)
108    {
109        const char* path = env->GetStringUTFChars(pathString, NULL);
110
111        res = inotify_add_watch(fd, path, mask);
112
113        env->ReleaseStringUTFChars(pathString, path);
114    }
115
116#endif
117
118    return res;
119}
120
121static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object, jint fd, jint wfd)
122{
123#if defined(__linux__)
124
125    inotify_rm_watch((int)fd, (uint32_t)wfd);
126
127#endif
128}
129
130static const JNINativeMethod sMethods[] = {
131     /* name, signature, funcPtr */
132    { "init", "()I", (void*)android_os_fileobserver_init },
133    { "observe", "(I)V", (void*)android_os_fileobserver_observe },
134    { "startWatching", "(ILjava/lang/String;I)I", (void*)android_os_fileobserver_startWatching },
135    { "stopWatching", "(II)V", (void*)android_os_fileobserver_stopWatching }
136
137};
138
139int register_android_os_FileObserver(JNIEnv* env)
140{
141    jclass clazz = FindClassOrDie(env, "android/os/FileObserver$ObserverThread");
142
143    method_onEvent = GetMethodIDOrDie(env, clazz, "onEvent", "(IILjava/lang/String;)V");
144
145    return RegisterMethodsOrDie(env, "android/os/FileObserver$ObserverThread", sMethods,
146                                NELEM(sMethods));
147}
148
149} /* namespace android */
150