blob: da0389578e15a6e7b38f619dc71f3c1164366c86 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.os;
18
Sudheer Shankaf7c79b42019-01-27 14:18:26 -080019import android.annotation.NonNull;
Jeff Sharkey902316d2016-08-23 10:24:56 -060020import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.util.Log;
22
Sudheer Shankaf7c79b42019-01-27 14:18:26 -080023import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import java.lang.ref.WeakReference;
Sudheer Shankaf7c79b42019-01-27 14:18:26 -080025import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import java.util.HashMap;
Sudheer Shankaf7c79b42019-01-27 14:18:26 -080027import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070029/**
30 * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
31 * to fire an event after files are accessed or changed by by any process on
32 * the device (including this one). FileObserver is an abstract class;
33 * subclasses must implement the event handler {@link #onEvent(int, String)}.
34 *
Sudheer Shankaf7c79b42019-01-27 14:18:26 -080035 * <p>Each FileObserver instance can monitor multiple files or directories.
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070036 * If a directory is monitored, events will be triggered for all files and
Scott Main6aad9952013-01-07 18:51:49 -080037 * subdirectories inside the monitored directory.</p>
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070038 *
39 * <p>An event mask is used to specify which changes or actions to report.
40 * Event type constants are used to describe the possible changes in the
41 * event mask as well as what actually happened in event callbacks.</p>
42 *
43 * <p class="caution"><b>Warning</b>: If a FileObserver is garbage collected, it
44 * will stop sending events. To ensure you keep receiving events, you must
45 * keep a reference to the FileObserver instance from some other live object.</p>
46 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047public abstract class FileObserver {
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070048 /** Event type: Data was read from a file */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070049 public static final int ACCESS = 0x00000001;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070050 /** Event type: Data was written to a file */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070051 public static final int MODIFY = 0x00000002;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070052 /** Event type: Metadata (permissions, owner, timestamp) was changed explicitly */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070053 public static final int ATTRIB = 0x00000004;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070054 /** Event type: Someone had a file or directory open for writing, and closed it */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070055 public static final int CLOSE_WRITE = 0x00000008;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070056 /** Event type: Someone had a file or directory open read-only, and closed it */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070057 public static final int CLOSE_NOWRITE = 0x00000010;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070058 /** Event type: A file or directory was opened */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070059 public static final int OPEN = 0x00000020;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070060 /** Event type: A file or subdirectory was moved from the monitored directory */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070061 public static final int MOVED_FROM = 0x00000040;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070062 /** Event type: A file or subdirectory was moved to the monitored directory */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070063 public static final int MOVED_TO = 0x00000080;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070064 /** Event type: A new file or subdirectory was created under the monitored directory */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070065 public static final int CREATE = 0x00000100;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070066 /** Event type: A file was deleted from the monitored directory */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070067 public static final int DELETE = 0x00000200;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070068 /** Event type: The monitored file or directory was deleted; monitoring effectively stops */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070069 public static final int DELETE_SELF = 0x00000400;
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070070 /** Event type: The monitored file or directory was moved; monitoring continues */
Joe Onorato9bb8fd72009-07-28 18:24:51 -070071 public static final int MOVE_SELF = 0x00000800;
72
Dan Egnor3dc1c7f2010-05-31 10:13:44 -070073 /** Event mask: All valid event types, combined */
74 public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
Dan Egnord95244f2010-02-05 13:52:35 -080076 | DELETE_SELF | MOVE_SELF;
Joe Onorato9bb8fd72009-07-28 18:24:51 -070077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private static final String LOG_TAG = "FileObserver";
79
80 private static class ObserverThread extends Thread {
Dan Egnord95244f2010-02-05 13:52:35 -080081 private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
82 private int m_fd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
Dan Egnord95244f2010-02-05 13:52:35 -080084 public ObserverThread() {
85 super("FileObserver");
86 m_fd = init();
87 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
Dan Egnord95244f2010-02-05 13:52:35 -080089 public void run() {
90 observe(m_fd);
91 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
Sudheer Shankaf7c79b42019-01-27 14:18:26 -080093 public int[] startWatching(List<File> files, int mask, FileObserver observer) {
94 final int count = files.size();
95 final String[] paths = new String[count];
96 for (int i = 0; i < count; ++i) {
97 paths[i] = files.get(i).getAbsolutePath();
98 }
99 final int[] wfds = new int[count];
100 Arrays.fill(wfds, -1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800102 startWatching(m_fd, paths, mask, wfds);
103
104 final WeakReference<FileObserver> fileObserverWeakReference =
105 new WeakReference<>(observer);
106 synchronized (m_observers) {
107 for (int wfd : wfds) {
108 if (wfd >= 0) {
109 m_observers.put(wfd, fileObserverWeakReference);
110 }
Dan Egnord95244f2010-02-05 13:52:35 -0800111 }
112 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800114 return wfds;
Dan Egnord95244f2010-02-05 13:52:35 -0800115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800117 public void stopWatching(int[] descriptors) {
118 stopWatching(m_fd, descriptors);
Dan Egnord95244f2010-02-05 13:52:35 -0800119 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
Dan Egnord95244f2010-02-05 13:52:35 -0800121 public void onEvent(int wfd, int mask, String path) {
122 // look up our observer, fixing up the map if necessary...
123 FileObserver observer = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
Dan Egnord95244f2010-02-05 13:52:35 -0800125 synchronized (m_observers) {
126 WeakReference weak = m_observers.get(wfd);
127 if (weak != null) { // can happen with lots of events from a dead wfd
128 observer = (FileObserver) weak.get();
129 if (observer == null) {
130 m_observers.remove(wfd);
131 }
132 }
133 }
134
135 // ...then call out to the observer without the sync lock held
136 if (observer != null) {
137 try {
138 observer.onEvent(mask, path);
139 } catch (Throwable throwable) {
140 Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
141 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 }
143 }
144
Dan Egnord95244f2010-02-05 13:52:35 -0800145 private native int init();
146 private native void observe(int fd);
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800147 private native void startWatching(int fd, String[] paths, int mask, int[] wfds);
148 private native void stopWatching(int fd, int[] wfds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
150
151 private static ObserverThread s_observerThread;
152
153 static {
Dan Egnord95244f2010-02-05 13:52:35 -0800154 s_observerThread = new ObserverThread();
155 s_observerThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 }
157
158 // instance
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800159 private final List<File> mFiles;
160 private int[] mDescriptors;
161 private final int mMask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700163 /**
164 * Equivalent to FileObserver(path, FileObserver.ALL_EVENTS).
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800165 *
166 * @deprecated use {@link #FileObserver(File)} instead.
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700167 */
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800168 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 public FileObserver(String path) {
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800170 this(new File(path));
171 }
172
173 /**
174 * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS).
175 */
176 public FileObserver(@NonNull File file) {
177 this(Arrays.asList(file));
178 }
179
180 /**
181 * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS).
182 *
183 * @param files The files or directories to monitor
184 */
185 public FileObserver(@NonNull List<File> files) {
186 this(files, ALL_EVENTS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 }
188
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700189 /**
190 * Create a new file observer for a certain file or directory.
191 * Monitoring does not start on creation! You must call
192 * {@link #startWatching()} before you will receive events.
193 *
194 * @param path The file or directory to monitor
195 * @param mask The event or events (added together) to watch for
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800196 *
197 * @deprecated use {@link #FileObserver(File, int)} instead.
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700198 */
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800199 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 public FileObserver(String path, int mask) {
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800201 this(new File(path), mask);
202 }
203
204 /**
205 * Create a new file observer for a certain file or directory.
206 * Monitoring does not start on creation! You must call
207 * {@link #startWatching()} before you will receive events.
208 *
209 * @param file The file or directory to monitor
210 * @param mask The event or events (added together) to watch for
211 */
212 public FileObserver(@NonNull File file, int mask) {
213 this(Arrays.asList(file), mask);
214 }
215
216 /**
217 * Version of {@link #FileObserver(File, int)} that allows callers to monitor
218 * multiple files or directories.
219 *
220 * @param files The files or directories to monitor
221 * @param mask The event or events (added together) to watch for
222 */
223 public FileObserver(@NonNull List<File> files, int mask) {
224 mFiles = files;
225 mMask = mask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 }
227
228 protected void finalize() {
Dan Egnord95244f2010-02-05 13:52:35 -0800229 stopWatching();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 }
231
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700232 /**
233 * Start watching for events. The monitored file or directory must exist at
234 * this time, or else no events will be reported (even if it appears later).
235 * If monitoring is already started, this call has no effect.
236 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 public void startWatching() {
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800238 if (mDescriptors == null) {
239 mDescriptors = s_observerThread.startWatching(mFiles, mMask, this);
Dan Egnord95244f2010-02-05 13:52:35 -0800240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 }
242
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700243 /**
244 * Stop watching for events. Some events may be in process, so events
245 * may continue to be reported even after this method completes. If
246 * monitoring is already stopped, this call has no effect.
247 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 public void stopWatching() {
Sudheer Shankaf7c79b42019-01-27 14:18:26 -0800249 if (mDescriptors != null) {
250 s_observerThread.stopWatching(mDescriptors);
251 mDescriptors = null;
Dan Egnord95244f2010-02-05 13:52:35 -0800252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 }
254
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700255 /**
256 * The event handler, which must be implemented by subclasses.
257 *
258 * <p class="note">This method is invoked on a special FileObserver thread.
259 * It runs independently of any threads, so take care to use appropriate
260 * synchronization! Consider using {@link Handler#post(Runnable)} to shift
261 * event handling work to the main thread to avoid concurrency problems.</p>
262 *
263 * <p>Event handlers must not throw exceptions.</p>
264 *
265 * @param event The type of event which happened
266 * @param path The path, relative to the main monitored file or directory,
Jeff Sharkey902316d2016-08-23 10:24:56 -0600267 * of the file or directory which triggered the event. This value can
268 * be {@code null} for certain events, such as {@link #MOVE_SELF}.
Dan Egnor3dc1c7f2010-05-31 10:13:44 -0700269 */
Jeff Sharkey902316d2016-08-23 10:24:56 -0600270 public abstract void onEvent(int event, @Nullable String path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271}