This blog will give a brief introduction to inotify and illustrate the usage of Android FileObserver API with an example.
inotify
inotify is a simple but powerful file change notification system. It’s a mechanism built into the Linux 2.6.13 kernel and above.
The mechanism is exposed to the userspace program through a set of 3 system calls, including inotify_init, inotify_add_watch and inotify_rm_watch.
These system calls are declared in /usr/include/sys/inotify.h. One needs to include the header file as below in order to use them.
#include <sys/inotify.h>
The inotify_init function has the following prototype,
int inotify_init (void);
This function will create an inotify instance and return a file descriptor which all events are read from. Each instance is associated with a unique, ordered queue for events.
The inotify_add_watch function is declared as,
int inotify_add_watch (int __fd, const char *__name, uint32_t __mask);
where __fd is the file descriptor returned from the inotify_init function, __name is the path to the object (either file or directory) to watch, and __mask indicates the types of event to watch. The return value can be used to refer to the watch added, called watch descriptor.
The inotify_rm_watch system call is declared as,
int inotify_rm_watch (int __fd, int __wd);
where __fd is the return value of inotify_init, and __wd is the return value of inotify_add_watch. This function will remove the watch from the queue.
With this set of system call and normal file IO operations, one can monitor the file changes in Linux system. Please refer reference 1 and 2 for further details of inotify.
Android FileObserver
Android FileObserver is based on FileObserver. It provides similar monitoring mechanism as inotify does. One thing worth-mentioning is that the API documentation says “If a directory is monitored, events will be triggered for all files and subdirectories (recursively) inside the monitored directory”, but the FileObserver API is actually not recursive.
In other words, a FileObserver is attached to a folder, then only the files and sub-folders inside it are monitored, but its sub sub-folders and files are not. For example, there is a folder B inside folder A, and a folder C inside folder B. If a FileObserver is created to monitor folder A, then any modifications done for folder B is detected, but not for folder C.
An Android Sample App Based on FileObserver
As FileObserver is an abstract class, one must create a subclass that extends FileObserver and implement the event handler onEvent(int, string).
Below is an example,
package roman10.tutorial.fileobserver;
import android.os.FileObserver;
public class MyFileObserver extends FileObserver {
public String absolutePath;
public MyFileObserver(String path) {
super(path, FileObserver.ALL_EVENTS);
absolutePath = path;
}
@Override
public void onEvent(int event, String path) {
if (path == null) {
return;
}
//a new file or subdirectory was created under the monitored directory
if ((FileObserver.CREATE & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is created\n";
}
//a file or directory was opened
if ((FileObserver.OPEN & event)!=0) {
FileAccessLogStatic.accessLogMsg += path + " is opened\n";
}
//data was read from a file
if ((FileObserver.ACCESS & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is accessed/read\n";
}
//data was written to a file
if ((FileObserver.MODIFY & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is modified\n";
}
//someone has a file or directory open read-only, and closed it
if ((FileObserver.CLOSE_NOWRITE & event)!=0) {
FileAccessLogStatic.accessLogMsg += path + " is closed\n";
}
//someone has a file or directory open for writing, and closed it
if ((FileObserver.CLOSE_WRITE & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is written and closed\n";
}
//[todo: consider combine this one with one below]
//a file was deleted from the monitored directory
if ((FileObserver.DELETE & event)!=0) {
//for testing copy file
// FileUtils.copyFile(absolutePath + "/" + path);
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is deleted\n";
}
//the monitored file or directory was deleted, monitoring effectively stops
if ((FileObserver.DELETE_SELF & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + " is deleted\n";
}
//a file or subdirectory was moved from the monitored directory
if ((FileObserver.MOVED_FROM & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is moved to somewhere " + "\n";
}
//a file or subdirectory was moved to the monitored directory
if ((FileObserver.MOVED_TO & event)!=0) {
FileAccessLogStatic.accessLogMsg += "File is moved to " + absolutePath + "/" + path + "\n";
}
//the monitored file or directory was moved; monitoring continues
if ((FileObserver.MOVE_SELF & event)!=0) {
FileAccessLogStatic.accessLogMsg += path + " is moved\n";
}
//Metadata (permissions, owner, timestamp) was changed explicitly
if ((FileObserver.ATTRIB & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is changed (permissions, owner, timestamp)\n";
}
}
}
Once the subclass is defined, one needs to create instances of this class in order to use it.
MyFileObserver fileOb = new MyFileObserver("/sdcard/testf/");
Similar to inotify_add_watch and inotify_remove_watch, one can call startWatching() and stopWatching() methods to start or stop monitoring.
fileOb.startWatching();
fileOb.stopWatching();
For a complete sample app which monitors the /sdcard directory of the Android OS, and output the results to screen, you can refer to my github repo here, or simply click here to download the source code zip file.
Below is a screenshot of the sample app.
Figure 1. Screenshot of File Modification Monitor
References
1. Kernel Korner – Intro to inotify: http://www.linuxjournal.com/article/8478
2. inotify – a powerful yet simple file change notification system:http://www.kernel.org/doc/Documentation/filesystems/inotify.txt
3. Android FileObserver API documentation:http://developer.android.com/reference/android/os/FileObserver.html