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
17package android.os;
18
19import android.util.Log;
20
21import com.android.internal.os.RuntimeInit;
22
23import java.lang.ref.WeakReference;
24import java.util.ArrayList;
25import java.util.HashMap;
26
27public abstract class FileObserver {
28    public static final int ACCESS = 0x00000001; /* File was accessed */
29    public static final int MODIFY = 0x00000002; /* File was modified */
30    public static final int ATTRIB = 0x00000004; /* Metadata changed */
31    public static final int CLOSE_WRITE = 0x00000008; /*  Writtable file was  closed */
32    public static final int CLOSE_NOWRITE = 0x00000010; /* Unwrittable file closed */
33    public static final int OPEN = 0x00000020; /* File was opened */
34    public static final int MOVED_FROM = 0x00000040; /* File was moved from X */
35    public static final int MOVED_TO = 0x00000080; /* File was moved to Y */
36    public static final int CREATE = 0x00000100; /* Subfile was created */
37    public static final int DELETE = 0x00000200; /* Subfile was deleted */
38    public static final int DELETE_SELF = 0x00000400; /* Self was deleted */
39    public static final int MOVE_SELF = 0x00000800; /* Self was moved */
40    public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
41            | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
42	    | DELETE_SELF | MOVE_SELF;
43
44    private static final String LOG_TAG = "FileObserver";
45
46    private static class ObserverThread extends Thread {
47	private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
48	private int m_fd;
49
50	public ObserverThread() {
51	    super("FileObserver");
52	    m_fd = init();
53	}
54
55	public void run() {
56	    observe(m_fd);
57	}
58
59	public int startWatching(String path, int mask, FileObserver observer) {
60	    int wfd = startWatching(m_fd, path, mask);
61
62	    Integer i = new Integer(wfd);
63	    if (wfd >= 0) {
64		synchronized (m_observers) {
65		    m_observers.put(i, new WeakReference(observer));
66		}
67	    }
68
69	    return i;
70	}
71
72	public void stopWatching(int descriptor) {
73	    stopWatching(m_fd, descriptor);
74	}
75
76    public void onEvent(int wfd, int mask, String path) {
77        // look up our observer, fixing up the map if necessary...
78        FileObserver observer;
79
80        synchronized (m_observers) {
81            WeakReference weak = m_observers.get(wfd);
82            observer = (FileObserver) weak.get();
83            if (observer == null) {
84                m_observers.remove(wfd);
85            }
86        }
87
88        // ...then call out to the observer without the sync lock held
89        if (observer != null) {
90            try {
91                observer.onEvent(mask, path);
92            } catch (Throwable throwable) {
93                Log.e(LOG_TAG, "Unhandled throwable " + throwable.toString() +
94                        " (returned by observer " + observer + ")", throwable);
95                RuntimeInit.crash("FileObserver", throwable);
96            }
97        }
98    }
99
100	private native int init();
101	private native void observe(int fd);
102	private native int startWatching(int fd, String path, int mask);
103	private native void stopWatching(int fd, int wfd);
104    }
105
106    private static ObserverThread s_observerThread;
107
108    static {
109	s_observerThread = new ObserverThread();
110	s_observerThread.start();
111    }
112
113    // instance
114    private String m_path;
115    private Integer m_descriptor;
116    private int m_mask;
117
118    public FileObserver(String path) {
119	this(path, ALL_EVENTS);
120    }
121
122    public FileObserver(String path, int mask) {
123	m_path = path;
124	m_mask = mask;
125	m_descriptor = -1;
126    }
127
128    protected void finalize() {
129	stopWatching();
130    }
131
132    public void startWatching() {
133	if (m_descriptor < 0) {
134	    m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
135	}
136    }
137
138    public void stopWatching() {
139	if (m_descriptor >= 0) {
140	    s_observerThread.stopWatching(m_descriptor);
141	    m_descriptor = -1;
142	}
143    }
144
145    public abstract void onEvent(int event, String path);
146}
147