1 /* 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 17 package android.os; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.util.Log; 24 import android.util.SparseArray; 25 26 import java.io.File; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.lang.ref.WeakReference; 30 import java.util.Arrays; 31 import java.util.HashMap; 32 import java.util.List; 33 34 /** 35 * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>) 36 * to fire an event after files are accessed or changed by any process on 37 * the device (including this one). FileObserver is an abstract class; 38 * subclasses must implement the event handler {@link #onEvent(int, String)}. 39 * 40 * <p>Each FileObserver instance can monitor multiple files or directories. 41 * If a directory is monitored, events will be triggered for all files and 42 * subdirectories inside the monitored directory.</p> 43 * 44 * <p>An event mask is used to specify which changes or actions to report. 45 * Event type constants are used to describe the possible changes in the 46 * event mask as well as what actually happened in event callbacks.</p> 47 * 48 * <p class="caution"><b>Warning</b>: If a FileObserver is garbage collected, it 49 * will stop sending events. To ensure you keep receiving events, you must 50 * keep a reference to the FileObserver instance from some other live object.</p> 51 */ 52 public abstract class FileObserver { 53 /** @hide */ 54 @IntDef(flag = true, value = { 55 ACCESS, 56 MODIFY, 57 ATTRIB, 58 CLOSE_WRITE, 59 CLOSE_NOWRITE, 60 OPEN, 61 MOVED_FROM, 62 MOVED_TO, 63 CREATE, 64 DELETE, 65 DELETE_SELF, 66 MOVE_SELF 67 }) 68 @Retention(RetentionPolicy.SOURCE) 69 public @interface NotifyEventType {} 70 71 /** Event type: Data was read from a file */ 72 public static final int ACCESS = 0x00000001; 73 /** Event type: Data was written to a file */ 74 public static final int MODIFY = 0x00000002; 75 /** Event type: Metadata (permissions, owner, timestamp) was changed explicitly */ 76 public static final int ATTRIB = 0x00000004; 77 /** Event type: Someone had a file or directory open for writing, and closed it */ 78 public static final int CLOSE_WRITE = 0x00000008; 79 /** Event type: Someone had a file or directory open read-only, and closed it */ 80 public static final int CLOSE_NOWRITE = 0x00000010; 81 /** Event type: A file or directory was opened */ 82 public static final int OPEN = 0x00000020; 83 /** Event type: A file or subdirectory was moved from the monitored directory */ 84 public static final int MOVED_FROM = 0x00000040; 85 /** Event type: A file or subdirectory was moved to the monitored directory */ 86 public static final int MOVED_TO = 0x00000080; 87 /** Event type: A new file or subdirectory was created under the monitored directory */ 88 public static final int CREATE = 0x00000100; 89 /** Event type: A file was deleted from the monitored directory */ 90 public static final int DELETE = 0x00000200; 91 /** Event type: The monitored file or directory was deleted; monitoring effectively stops */ 92 public static final int DELETE_SELF = 0x00000400; 93 /** Event type: The monitored file or directory was moved; monitoring continues */ 94 public static final int MOVE_SELF = 0x00000800; 95 96 /** Event mask: All valid event types, combined */ 97 @NotifyEventType 98 public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE 99 | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE 100 | DELETE_SELF | MOVE_SELF; 101 102 private static final String LOG_TAG = "FileObserver"; 103 104 private static class ObserverThread extends Thread { 105 /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */ 106 private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>(); 107 private SparseArray<WeakReference> mRealObservers = new SparseArray<>(); 108 private int m_fd; 109 ObserverThread()110 public ObserverThread() { 111 super("FileObserver"); 112 m_fd = init(); 113 } 114 run()115 public void run() { 116 observe(m_fd); 117 } 118 startWatching(List<File> files, @NotifyEventType int mask, FileObserver observer)119 public int[] startWatching(List<File> files, 120 @NotifyEventType int mask, FileObserver observer) { 121 final int count = files.size(); 122 final String[] paths = new String[count]; 123 for (int i = 0; i < count; ++i) { 124 paths[i] = files.get(i).getAbsolutePath(); 125 } 126 final int[] wfds = new int[count]; 127 Arrays.fill(wfds, -1); 128 129 startWatching(m_fd, paths, mask, wfds); 130 131 final WeakReference<FileObserver> fileObserverWeakReference = 132 new WeakReference<>(observer); 133 synchronized (mRealObservers) { 134 for (int wfd : wfds) { 135 if (wfd >= 0) { 136 mRealObservers.put(wfd, fileObserverWeakReference); 137 } 138 } 139 } 140 141 return wfds; 142 } 143 stopWatching(int[] descriptors)144 public void stopWatching(int[] descriptors) { 145 stopWatching(m_fd, descriptors); 146 } 147 148 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) onEvent(int wfd, @NotifyEventType int mask, String path)149 public void onEvent(int wfd, @NotifyEventType int mask, String path) { 150 // look up our observer, fixing up the map if necessary... 151 FileObserver observer = null; 152 153 synchronized (mRealObservers) { 154 WeakReference weak = mRealObservers.get(wfd); 155 if (weak != null) { // can happen with lots of events from a dead wfd 156 observer = (FileObserver) weak.get(); 157 if (observer == null) { 158 mRealObservers.remove(wfd); 159 } 160 } 161 } 162 163 // ...then call out to the observer without the sync lock held 164 if (observer != null) { 165 try { 166 observer.onEvent(mask, path); 167 } catch (Throwable throwable) { 168 Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable); 169 } 170 } 171 } 172 init()173 private native int init(); observe(int fd)174 private native void observe(int fd); startWatching(int fd, String[] paths, @NotifyEventType int mask, int[] wfds)175 private native void startWatching(int fd, String[] paths, 176 @NotifyEventType int mask, int[] wfds); stopWatching(int fd, int[] wfds)177 private native void stopWatching(int fd, int[] wfds); 178 } 179 180 @UnsupportedAppUsage 181 private static ObserverThread s_observerThread; 182 183 static { 184 s_observerThread = new ObserverThread(); s_observerThread.start()185 s_observerThread.start(); 186 } 187 188 // instance 189 private final List<File> mFiles; 190 private int[] mDescriptors; 191 private final int mMask; 192 193 /** 194 * Equivalent to FileObserver(path, FileObserver.ALL_EVENTS). 195 * 196 * @deprecated use {@link #FileObserver(File)} instead. 197 */ 198 @Deprecated FileObserver(String path)199 public FileObserver(String path) { 200 this(new File(path)); 201 } 202 203 /** 204 * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS). 205 */ FileObserver(@onNull File file)206 public FileObserver(@NonNull File file) { 207 this(Arrays.asList(file)); 208 } 209 210 /** 211 * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS). 212 * 213 * @param files The files or directories to monitor 214 */ FileObserver(@onNull List<File> files)215 public FileObserver(@NonNull List<File> files) { 216 this(files, ALL_EVENTS); 217 } 218 219 /** 220 * Create a new file observer for a certain file or directory. 221 * Monitoring does not start on creation! You must call 222 * {@link #startWatching()} before you will receive events. 223 * 224 * @param path The file or directory to monitor 225 * @param mask The event or events (added together) to watch for 226 * 227 * @deprecated use {@link #FileObserver(File, int)} instead. 228 */ 229 @Deprecated FileObserver(String path, @NotifyEventType int mask)230 public FileObserver(String path, @NotifyEventType int mask) { 231 this(new File(path), mask); 232 } 233 234 /** 235 * Create a new file observer for a certain file or directory. 236 * Monitoring does not start on creation! You must call 237 * {@link #startWatching()} before you will receive events. 238 * 239 * @param file The file or directory to monitor 240 * @param mask The event or events (added together) to watch for 241 */ FileObserver(@onNull File file, @NotifyEventType int mask)242 public FileObserver(@NonNull File file, @NotifyEventType int mask) { 243 this(Arrays.asList(file), mask); 244 } 245 246 /** 247 * Version of {@link #FileObserver(File, int)} that allows callers to monitor 248 * multiple files or directories. 249 * 250 * @param files The files or directories to monitor 251 * @param mask The event or events (added together) to watch for 252 */ FileObserver(@onNull List<File> files, @NotifyEventType int mask)253 public FileObserver(@NonNull List<File> files, @NotifyEventType int mask) { 254 mFiles = files; 255 mMask = mask; 256 } 257 finalize()258 protected void finalize() { 259 stopWatching(); 260 } 261 262 /** 263 * Start watching for events. The monitored file or directory must exist at 264 * this time, or else no events will be reported (even if it appears later). 265 * If monitoring is already started, this call has no effect. 266 */ startWatching()267 public void startWatching() { 268 if (mDescriptors == null) { 269 mDescriptors = s_observerThread.startWatching(mFiles, mMask, this); 270 } 271 } 272 273 /** 274 * Stop watching for events. Some events may be in process, so events 275 * may continue to be reported even after this method completes. If 276 * monitoring is already stopped, this call has no effect. 277 */ stopWatching()278 public void stopWatching() { 279 if (mDescriptors != null) { 280 s_observerThread.stopWatching(mDescriptors); 281 mDescriptors = null; 282 } 283 } 284 285 /** 286 * The event handler, which must be implemented by subclasses. 287 * 288 * <p class="note">This method is invoked on a special FileObserver thread. 289 * It runs independently of any threads, so take care to use appropriate 290 * synchronization! Consider using {@link Handler#post(Runnable)} to shift 291 * event handling work to the main thread to avoid concurrency problems.</p> 292 * 293 * <p>Event handlers must not throw exceptions.</p> 294 * 295 * @param event The type of event which happened 296 * @param path The path, relative to the main monitored file or directory, 297 * of the file or directory which triggered the event. This value can 298 * be {@code null} for certain events, such as {@link #MOVE_SELF}. 299 */ onEvent(int event, @Nullable String path)300 public abstract void onEvent(int event, @Nullable String path); 301 } 302