The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 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 | |
Jeff Sharkey | 902316d | 2016-08-23 10:24:56 -0600 | [diff] [blame] | 19 | import android.annotation.Nullable; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 20 | import android.util.Log; |
| 21 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 22 | import java.lang.ref.WeakReference; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 23 | import java.util.HashMap; |
| 24 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 25 | /** |
| 26 | * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>) |
| 27 | * to fire an event after files are accessed or changed by by any process on |
| 28 | * the device (including this one). FileObserver is an abstract class; |
| 29 | * subclasses must implement the event handler {@link #onEvent(int, String)}. |
| 30 | * |
| 31 | * <p>Each FileObserver instance monitors a single file or directory. |
| 32 | * If a directory is monitored, events will be triggered for all files and |
Scott Main | 6aad995 | 2013-01-07 18:51:49 -0800 | [diff] [blame] | 33 | * subdirectories inside the monitored directory.</p> |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 34 | * |
| 35 | * <p>An event mask is used to specify which changes or actions to report. |
| 36 | * Event type constants are used to describe the possible changes in the |
| 37 | * event mask as well as what actually happened in event callbacks.</p> |
| 38 | * |
| 39 | * <p class="caution"><b>Warning</b>: If a FileObserver is garbage collected, it |
| 40 | * will stop sending events. To ensure you keep receiving events, you must |
| 41 | * keep a reference to the FileObserver instance from some other live object.</p> |
| 42 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 43 | public abstract class FileObserver { |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 44 | /** Event type: Data was read from a file */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 45 | public static final int ACCESS = 0x00000001; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 46 | /** Event type: Data was written to a file */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 47 | public static final int MODIFY = 0x00000002; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 48 | /** Event type: Metadata (permissions, owner, timestamp) was changed explicitly */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 49 | public static final int ATTRIB = 0x00000004; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 50 | /** Event type: Someone had a file or directory open for writing, and closed it */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 51 | public static final int CLOSE_WRITE = 0x00000008; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 52 | /** Event type: Someone had a file or directory open read-only, and closed it */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 53 | public static final int CLOSE_NOWRITE = 0x00000010; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 54 | /** Event type: A file or directory was opened */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 55 | public static final int OPEN = 0x00000020; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 56 | /** Event type: A file or subdirectory was moved from the monitored directory */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 57 | public static final int MOVED_FROM = 0x00000040; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 58 | /** Event type: A file or subdirectory was moved to the monitored directory */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 59 | public static final int MOVED_TO = 0x00000080; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 60 | /** Event type: A new file or subdirectory was created under the monitored directory */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 61 | public static final int CREATE = 0x00000100; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 62 | /** Event type: A file was deleted from the monitored directory */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 63 | public static final int DELETE = 0x00000200; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 64 | /** Event type: The monitored file or directory was deleted; monitoring effectively stops */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 65 | public static final int DELETE_SELF = 0x00000400; |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 66 | /** Event type: The monitored file or directory was moved; monitoring continues */ |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 67 | public static final int MOVE_SELF = 0x00000800; |
| 68 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 69 | /** Event mask: All valid event types, combined */ |
| 70 | public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 71 | | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 72 | | DELETE_SELF | MOVE_SELF; |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 73 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 74 | private static final String LOG_TAG = "FileObserver"; |
| 75 | |
| 76 | private static class ObserverThread extends Thread { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 77 | private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>(); |
| 78 | private int m_fd; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 79 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 80 | public ObserverThread() { |
| 81 | super("FileObserver"); |
| 82 | m_fd = init(); |
| 83 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 84 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 85 | public void run() { |
| 86 | observe(m_fd); |
| 87 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 88 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 89 | public int startWatching(String path, int mask, FileObserver observer) { |
| 90 | int wfd = startWatching(m_fd, path, mask); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 91 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 92 | Integer i = new Integer(wfd); |
| 93 | if (wfd >= 0) { |
| 94 | synchronized (m_observers) { |
| 95 | m_observers.put(i, new WeakReference(observer)); |
| 96 | } |
| 97 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 98 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 99 | return i; |
| 100 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 101 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 102 | public void stopWatching(int descriptor) { |
| 103 | stopWatching(m_fd, descriptor); |
| 104 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 105 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 106 | public void onEvent(int wfd, int mask, String path) { |
| 107 | // look up our observer, fixing up the map if necessary... |
| 108 | FileObserver observer = null; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 109 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 110 | synchronized (m_observers) { |
| 111 | WeakReference weak = m_observers.get(wfd); |
| 112 | if (weak != null) { // can happen with lots of events from a dead wfd |
| 113 | observer = (FileObserver) weak.get(); |
| 114 | if (observer == null) { |
| 115 | m_observers.remove(wfd); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // ...then call out to the observer without the sync lock held |
| 121 | if (observer != null) { |
| 122 | try { |
| 123 | observer.onEvent(mask, path); |
| 124 | } catch (Throwable throwable) { |
| 125 | Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable); |
| 126 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 130 | private native int init(); |
| 131 | private native void observe(int fd); |
| 132 | private native int startWatching(int fd, String path, int mask); |
| 133 | private native void stopWatching(int fd, int wfd); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | private static ObserverThread s_observerThread; |
| 137 | |
| 138 | static { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 139 | s_observerThread = new ObserverThread(); |
| 140 | s_observerThread.start(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | // instance |
| 144 | private String m_path; |
| 145 | private Integer m_descriptor; |
| 146 | private int m_mask; |
| 147 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 148 | /** |
| 149 | * Equivalent to FileObserver(path, FileObserver.ALL_EVENTS). |
| 150 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 151 | public FileObserver(String path) { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 152 | this(path, ALL_EVENTS); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 153 | } |
| 154 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 155 | /** |
| 156 | * Create a new file observer for a certain file or directory. |
| 157 | * Monitoring does not start on creation! You must call |
| 158 | * {@link #startWatching()} before you will receive events. |
| 159 | * |
| 160 | * @param path The file or directory to monitor |
| 161 | * @param mask The event or events (added together) to watch for |
| 162 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 163 | public FileObserver(String path, int mask) { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 164 | m_path = path; |
| 165 | m_mask = mask; |
| 166 | m_descriptor = -1; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | protected void finalize() { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 170 | stopWatching(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 171 | } |
| 172 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 173 | /** |
| 174 | * Start watching for events. The monitored file or directory must exist at |
| 175 | * this time, or else no events will be reported (even if it appears later). |
| 176 | * If monitoring is already started, this call has no effect. |
| 177 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 178 | public void startWatching() { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 179 | if (m_descriptor < 0) { |
| 180 | m_descriptor = s_observerThread.startWatching(m_path, m_mask, this); |
| 181 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 182 | } |
| 183 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 184 | /** |
| 185 | * Stop watching for events. Some events may be in process, so events |
| 186 | * may continue to be reported even after this method completes. If |
| 187 | * monitoring is already stopped, this call has no effect. |
| 188 | */ |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 189 | public void stopWatching() { |
Dan Egnor | d95244f | 2010-02-05 13:52:35 -0800 | [diff] [blame] | 190 | if (m_descriptor >= 0) { |
| 191 | s_observerThread.stopWatching(m_descriptor); |
| 192 | m_descriptor = -1; |
| 193 | } |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 194 | } |
| 195 | |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 196 | /** |
| 197 | * The event handler, which must be implemented by subclasses. |
| 198 | * |
| 199 | * <p class="note">This method is invoked on a special FileObserver thread. |
| 200 | * It runs independently of any threads, so take care to use appropriate |
| 201 | * synchronization! Consider using {@link Handler#post(Runnable)} to shift |
| 202 | * event handling work to the main thread to avoid concurrency problems.</p> |
| 203 | * |
| 204 | * <p>Event handlers must not throw exceptions.</p> |
| 205 | * |
| 206 | * @param event The type of event which happened |
| 207 | * @param path The path, relative to the main monitored file or directory, |
Jeff Sharkey | 902316d | 2016-08-23 10:24:56 -0600 | [diff] [blame] | 208 | * of the file or directory which triggered the event. This value can |
| 209 | * be {@code null} for certain events, such as {@link #MOVE_SELF}. |
Dan Egnor | 3dc1c7f | 2010-05-31 10:13:44 -0700 | [diff] [blame] | 210 | */ |
Jeff Sharkey | 902316d | 2016-08-23 10:24:56 -0600 | [diff] [blame] | 211 | public abstract void onEvent(int event, @Nullable String path); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 212 | } |