Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7c79b4f authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Update FileObserver to be able to watch multiple files.

Bug: 120879205
Test: atest cts/tests/tests/os/src/android/os/cts/FileObserverTest.java
Test: atest frameworks/base/core/tests/coretests/src/android/os/FileObserverTest.java
Change-Id: Ie1067c81dc502f2ad971e1f5d02be8baa319c3ad
parent 2bfd202e
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -34405,8 +34405,12 @@ package android.os {
  }
  public abstract class FileObserver {
    ctor public FileObserver(String);
    ctor public FileObserver(String, int);
    ctor @Deprecated public FileObserver(String);
    ctor public FileObserver(@NonNull java.io.File);
    ctor public FileObserver(@NonNull java.util.List<java.io.File>);
    ctor @Deprecated public FileObserver(String, int);
    ctor public FileObserver(@NonNull java.io.File, int);
    ctor public FileObserver(@NonNull java.util.List<java.io.File>, int);
    method protected void finalize();
    method public abstract void onEvent(int, @Nullable String);
    method public void startWatching();
+83 −24
Original line number Diff line number Diff line
@@ -16,11 +16,15 @@

package android.os;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
@@ -28,7 +32,7 @@ import java.util.HashMap;
 * the device (including this one).  FileObserver is an abstract class;
 * subclasses must implement the event handler {@link #onEvent(int, String)}.
 *
 * <p>Each FileObserver instance monitors a single file or directory.
 * <p>Each FileObserver instance can monitor multiple files or directories.
 * If a directory is monitored, events will be triggered for all files and
 * subdirectories inside the monitored directory.</p>
 *
@@ -86,21 +90,32 @@ public abstract class FileObserver {
            observe(m_fd);
        }

        public int startWatching(String path, int mask, FileObserver observer) {
            int wfd = startWatching(m_fd, path, mask);
        public int[] startWatching(List<File> files, int mask, FileObserver observer) {
            final int count = files.size();
            final String[] paths = new String[count];
            for (int i = 0; i < count; ++i) {
                paths[i] = files.get(i).getAbsolutePath();
            }
            final int[] wfds = new int[count];
            Arrays.fill(wfds, -1);

            Integer i = new Integer(wfd);
            if (wfd >= 0) {
            startWatching(m_fd, paths, mask, wfds);

            final WeakReference<FileObserver> fileObserverWeakReference =
                    new WeakReference<>(observer);
            synchronized (m_observers) {
                    m_observers.put(i, new WeakReference(observer));
                for (int wfd : wfds) {
                    if (wfd >= 0) {
                        m_observers.put(wfd, fileObserverWeakReference);
                    }
                }
            }

            return i;
            return wfds;
        }

        public void stopWatching(int descriptor) {
            stopWatching(m_fd, descriptor);
        public void stopWatching(int[] descriptors) {
            stopWatching(m_fd, descriptors);
        }

        public void onEvent(int wfd, int mask, String path) {
@@ -129,8 +144,8 @@ public abstract class FileObserver {

        private native int init();
        private native void observe(int fd);
        private native int startWatching(int fd, String path, int mask);
        private native void stopWatching(int fd, int wfd);
        private native void startWatching(int fd, String[] paths, int mask, int[] wfds);
        private native void stopWatching(int fd, int[] wfds);
    }

    private static ObserverThread s_observerThread;
@@ -141,15 +156,34 @@ public abstract class FileObserver {
    }

    // instance
    private String m_path;
    private Integer m_descriptor;
    private int m_mask;
    private final List<File> mFiles;
    private int[] mDescriptors;
    private final int mMask;

    /**
     * Equivalent to FileObserver(path, FileObserver.ALL_EVENTS).
     *
     * @deprecated use {@link #FileObserver(File)} instead.
     */
    @Deprecated
    public FileObserver(String path) {
        this(path, ALL_EVENTS);
        this(new File(path));
    }

    /**
     * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS).
     */
    public FileObserver(@NonNull File file) {
        this(Arrays.asList(file));
    }

    /**
     * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS).
     *
     * @param files The files or directories to monitor
     */
    public FileObserver(@NonNull List<File> files) {
        this(files, ALL_EVENTS);
    }

    /**
@@ -159,11 +193,36 @@ public abstract class FileObserver {
     *
     * @param path The file or directory to monitor
     * @param mask The event or events (added together) to watch for
     *
     * @deprecated use {@link #FileObserver(File, int)} instead.
     */
    @Deprecated
    public FileObserver(String path, int mask) {
        m_path = path;
        m_mask = mask;
        m_descriptor = -1;
        this(new File(path), mask);
    }

    /**
     * Create a new file observer for a certain file or directory.
     * Monitoring does not start on creation!  You must call
     * {@link #startWatching()} before you will receive events.
     *
     * @param file The file or directory to monitor
     * @param mask The event or events (added together) to watch for
     */
    public FileObserver(@NonNull File file, int mask) {
        this(Arrays.asList(file), mask);
    }

    /**
     * Version of {@link #FileObserver(File, int)} that allows callers to monitor
     * multiple files or directories.
     *
     * @param files The files or directories to monitor
     * @param mask The event or events (added together) to watch for
     */
    public FileObserver(@NonNull List<File> files, int mask) {
        mFiles = files;
        mMask = mask;
    }

    protected void finalize() {
@@ -176,8 +235,8 @@ public abstract class FileObserver {
     * If monitoring is already started, this call has no effect.
     */
    public void startWatching() {
        if (m_descriptor < 0) {
            m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
        if (mDescriptors == null) {
            mDescriptors = s_observerThread.startWatching(mFiles, mMask, this);
        }
    }

@@ -187,9 +246,9 @@ public abstract class FileObserver {
     * monitoring is already stopped, this call has no effect.
     */
    public void stopWatching() {
        if (m_descriptor >= 0) {
            s_observerThread.stopWatching(m_descriptor);
            m_descriptor = -1;
        if (mDescriptors != null) {
            s_observerThread.stopWatching(mDescriptors);
            mDescriptors = null;
        }
    }

+27 −11
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
*/

#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
@@ -98,31 +100,45 @@ static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd
#endif
}

static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask)
static void android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd,
                                                       jobjectArray pathStrings, jint mask,
                                                       jintArray wfdArray)
{
    int res = -1;
    ScopedIntArrayRW wfds(env, wfdArray);
    if (wfds.get() == nullptr) {
        jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRW");
    }

#if defined(__linux__)

    if (fd >= 0)
    {
        const char* path = env->GetStringUTFChars(pathString, NULL);
        size_t count = wfds.size();
        for (jsize i = 0; i < count; ++i) {
            jstring pathString = (jstring) env->GetObjectArrayElement(pathStrings, i);

        res = inotify_add_watch(fd, path, mask);
            ScopedUtfChars path(env, pathString);

        env->ReleaseStringUTFChars(pathString, path);
            wfds[i] = inotify_add_watch(fd, path.c_str(), mask);
        }
    }

#endif

    return res;
}

static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object, jint fd, jint wfd)
static void android_os_fileobserver_stopWatching(JNIEnv* env, jobject object,
                                                 jint fd, jintArray wfdArray)
{
#if defined(__linux__)

    inotify_rm_watch((int)fd, (uint32_t)wfd);
    ScopedIntArrayRO wfds(env, wfdArray);
    if (wfds.get() == nullptr) {
        jniThrowException(env, "java/lang/IllegalStateException", "Failed to get ScopedIntArrayRO");
    }
    size_t count = wfds.size();
    for (size_t i = 0; i < count; ++i) {
        inotify_rm_watch((int)fd, (uint32_t)wfds[i]);
    }

#endif
}
@@ -131,8 +147,8 @@ static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
    { "init", "()I", (void*)android_os_fileobserver_init },
    { "observe", "(I)V", (void*)android_os_fileobserver_observe },
    { "startWatching", "(ILjava/lang/String;I)I", (void*)android_os_fileobserver_startWatching },
    { "stopWatching", "(II)V", (void*)android_os_fileobserver_stopWatching }
    { "startWatching", "(I[Ljava/lang/String;I[I)V", (void*)android_os_fileobserver_startWatching },
    { "stopWatching", "(I[I)V", (void*)android_os_fileobserver_stopWatching }

};