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