Add VmPolicy to monitor path access.

An upcoming StrictMode feature is going to help detect app bugs
where they accidentally interact with credential-protected
filesystem paths while a user is encrypted.

Since much of the existing BlockGuard and BlockGuard.Policy
interface is already on the API greylist, we carefully add a new
parallel BlockGuard.VmPolicy object and start dispatching path
access events to it.  A future StrictMode CL will inspect paths
reported through this interface to report any violations.

Bug: 110413274
Test: atest cts/tests/tests/os/src/android/os/cts/StrictModeTest.java
Change-Id: I98bb9cb428336710566b3a8c81b6332ef981ecfe
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
index caf0470..e3cf529 100644
--- a/dalvik/src/main/java/dalvik/system/BlockGuard.java
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -16,23 +16,20 @@
 
 package dalvik.system;
 
-import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.net.SocketException;
+import libcore.util.NonNull;
+
+import java.util.Objects;
 
 /**
- * Mechanism to let threads set restrictions on what code is allowed
- * to do in their thread.
- *
- * <p>This is meant for applications to prevent certain blocking
- * operations from running on their main event loop (or "UI") threads.
- *
- * <p>Note that this is all best-effort to catch most accidental mistakes
- * and isn't intended to be a perfect mechanism, nor provide any sort of
- * security.
+ * Interface that enables {@code StrictMode} to install callbacks to implement
+ * its policy detection and penalty behavior in {@code libcore} code.
+ * <p>
+ * The framework separately defines {@code StrictMode.ThreadPolicy} and
+ * {@code StrictMode.VmPolicy}, so we mirror that separation here; the former is
+ * designed for per-thread policies, and the latter for process-wide policies.
+ * <p>
+ * Note that this is all best-effort to catch most accidental mistakes and isn't
+ * intended to be a perfect mechanism, nor provide any sort of security.
  *
  * @hide
  */
@@ -41,14 +38,9 @@
     // TODO: refactor class name to something more generic, since its scope is
     // growing beyond just blocking/logging.
 
-    public static final int DISALLOW_DISK_WRITE = 0x01;
-    public static final int DISALLOW_DISK_READ = 0x02;
-    public static final int DISALLOW_NETWORK = 0x04;
-    public static final int PASS_RESTRICTIONS_VIA_RPC = 0x08;
-    public static final int PENALTY_LOG = 0x10;
-    public static final int PENALTY_DIALOG = 0x20;
-    public static final int PENALTY_DEATH = 0x40;
-
+    /**
+     * Per-thread interface used to implement {@code StrictMode.ThreadPolicy}.
+     */
     public interface Policy {
         /**
          * Called on disk writes.
@@ -84,6 +76,36 @@
         int getPolicyMask();
     }
 
+    /**
+     * Per-process interface used to implement {@code StrictMode.VmPolicy}.
+     */
+    public interface VmPolicy {
+        /**
+         * Called by core libraries code when the given path is accessed. This
+         * allows an implementation to alert developers to unexpected path
+         * access, such as trying to access encrypted files before the
+         * encryption key has been installed.
+         * <p>
+         * This only needs to be called once when a path is first accessed by
+         * the process; it doesn't need to be invoked for each subsequent
+         * read/write. (In contrast, we always need to call the per-thread
+         * policy for every read/write, since ownership of an open file can move
+         * between threads.)
+         * <p>
+         * Note that this is all best-effort to catch most accidental mistakes
+         * and isn't intended to be a perfect mechanism, nor provide any sort of
+         * security.
+         *
+         * @param path The path in the local file system that is being accessed
+         *            for reading or writing.
+         */
+        void onPathAccess(String path);
+    }
+
+    /**
+     * @deprecated no longer actively used, but kept intact for greylist.
+     */
+    @Deprecated
     public static class BlockGuardPolicyException extends RuntimeException {
         // bitmask of DISALLOW_*, PENALTY_*, etc flags
         private final int mPolicyState;
@@ -122,18 +144,27 @@
     }
 
     /**
-     * The default, permissive policy that doesn't prevent any operations.
+     * The default, permissive per-thread policy.
      */
     public static final Policy LAX_POLICY = new Policy() {
-            public void onWriteToDisk() {}
-            public void onReadFromDisk() {}
-            public void onNetwork() {}
-            public void onUnbufferedIO() {}
-            public void onExplicitGc() {}
-            public int getPolicyMask() {
-                return 0;
-            }
-        };
+        @Override public void onWriteToDisk() {}
+        @Override public void onReadFromDisk() {}
+        @Override public void onNetwork() {}
+        @Override public void onUnbufferedIO() {}
+        @Override public void onExplicitGc() {}
+
+        @Override
+        public int getPolicyMask() {
+            return 0;
+        }
+    };
+
+    /**
+     * The default, permissive per-process policy.
+     */
+    public static final VmPolicy LAX_VM_POLICY = new VmPolicy() {
+        @Override public void onPathAccess(String path) {}
+    };
 
     private static ThreadLocal<Policy> threadPolicy = new ThreadLocal<Policy>() {
         @Override protected Policy initialValue() {
@@ -141,27 +172,52 @@
         }
     };
 
+    private static volatile VmPolicy vmPolicy = LAX_VM_POLICY;
+
     /**
-     * Get the current thread's policy.
+     * Get the per-thread policy for the current thread.
      *
-     * @return the current thread's policy.  Never returns null.
-     *     Will return the LAX_POLICY instance if nothing else is set.
+     * @return the current thread's policy. Will return the {@link #LAX_POLICY}
+     *         instance if nothing else is set.
      */
-    public static Policy getThreadPolicy() {
+    public static @NonNull Policy getThreadPolicy() {
         return threadPolicy.get();
     }
 
     /**
-     * Sets the current thread's block guard policy.
+     * Sets the per-thread policy for the current thread.
+     * <p>
+     * This should only be called by {@code StrictMode}, since there can only be
+     * one policy active at any given time.
      *
-     * @param policy policy to set.  May not be null.  Use the public LAX_POLICY
-     *   if you want to unset the active policy.
+     * @param policy policy to set. Use the public {@link #LAX_POLICY} if you
+     *            want to unset the active policy.
      */
-    public static void setThreadPolicy(Policy policy) {
-        if (policy == null) {
-            throw new NullPointerException("policy == null");
-        }
-        threadPolicy.set(policy);
+    public static void setThreadPolicy(@NonNull Policy policy) {
+        threadPolicy.set(Objects.requireNonNull(policy));
+    }
+
+    /**
+     * Get the per-process policy for the current process.
+     *
+     * @return the current process's policy. Will return the
+     *         {@link #LAX_VM_POLICY} instance if nothing else is set.
+     */
+    public static @NonNull VmPolicy getVmPolicy() {
+        return vmPolicy;
+    }
+
+    /**
+     * Set the per-process policy for the current process.
+     * <p>
+     * This should only be called by {@code StrictMode}, since there can only be
+     * one policy active at any given time.
+     *
+     * @param policy policy to set. Use the public {@link #LAX_VM_POLICY} if you
+     *            want to unset the active policy.
+     */
+    public static void setVmPolicy(@NonNull VmPolicy policy) {
+        vmPolicy = Objects.requireNonNull(policy);
     }
 
     private BlockGuard() {}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 9b1ebe9..ac10da6 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -65,16 +65,19 @@
 
     @Override public boolean access(String path, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         return os.access(path, mode);
     }
 
     @Override public void chmod(String path, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.chmod(path, mode);
     }
 
     @Override public void chown(String path, int uid, int gid) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.chown(path, uid, gid);
     }
 
@@ -174,11 +177,14 @@
 
     @Override public void lchown(String path, int uid, int gid) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.lchown(path, uid, gid);
     }
 
     @Override public void link(String oldPath, String newPath) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(oldPath);
+        BlockGuard.getVmPolicy().onPathAccess(newPath);
         os.link(oldPath, newPath);
     }
 
@@ -189,21 +195,25 @@
 
     @Override public StructStat lstat(String path) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         return os.lstat(path);
     }
 
     @Override public void mkdir(String path, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.mkdir(path, mode);
     }
 
     @Override public void mkfifo(String path, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.mkfifo(path, mode);
     }
 
     @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         if ((flags & O_ACCMODE) != O_RDONLY) {
             BlockGuard.getThreadPolicy().onWriteToDisk();
         }
@@ -256,11 +266,13 @@
 
     @Override public String readlink(String path) throws ErrnoException {
       BlockGuard.getThreadPolicy().onReadFromDisk();
+      BlockGuard.getVmPolicy().onPathAccess(path);
       return os.readlink(path);
     }
 
     @Override public String realpath(String path) throws ErrnoException {
       BlockGuard.getThreadPolicy().onReadFromDisk();
+      BlockGuard.getVmPolicy().onPathAccess(path);
       return os.realpath(path);
     }
 
@@ -281,11 +293,14 @@
 
     @Override public void remove(String path) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.remove(path);
     }
 
     @Override public void rename(String oldPath, String newPath) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(oldPath);
+        BlockGuard.getVmPolicy().onPathAccess(newPath);
         os.rename(oldPath, newPath);
     }
 
@@ -325,16 +340,20 @@
 
     @Override public StructStat stat(String path) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         return os.stat(path);
     }
 
     @Override public StructStatVfs statvfs(String path) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         return os.statvfs(path);
     }
 
     @Override public void symlink(String oldPath, String newPath) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(oldPath);
+        BlockGuard.getVmPolicy().onPathAccess(newPath);
         os.symlink(oldPath, newPath);
     }
 
@@ -355,17 +374,20 @@
 
     @Override public void execv(String filename, String[] argv) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(filename);
         os.execv(filename, argv);
     }
 
     @Override public void execve(String filename, String[] argv, String[] envp)
             throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(filename);
         os.execve(filename, argv, envp);
     }
 
     @Override public byte[] getxattr(String path, String name) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         return os.getxattr(path, name);
     }
 
@@ -378,12 +400,14 @@
 
     @Override public void removexattr(String path, String name) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.removexattr(path, name);
     }
 
     @Override public void setxattr(String path, String name, byte[] value, int flags)
             throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         os.setxattr(path, name, value, flags);
     }
 
@@ -395,6 +419,7 @@
 
     @Override public void unlink(String pathname) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(pathname);
         os.unlink(pathname);
     }
 
diff --git a/ojluni/src/main/java/java/io/FileInputStream.java b/ojluni/src/main/java/java/io/FileInputStream.java
index db7ba65..8df5f4d 100755
--- a/ojluni/src/main/java/java/io/FileInputStream.java
+++ b/ojluni/src/main/java/java/io/FileInputStream.java
@@ -159,8 +159,10 @@
 
         path = name;
 
-        // Android-added: BlockGuard support.
+        // BEGIN Android-added: BlockGuard support.
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
+        // END Android-added: BlockGuard support.
 
         open(name);
 
diff --git a/ojluni/src/main/java/java/io/UnixFileSystem.java b/ojluni/src/main/java/java/io/UnixFileSystem.java
index 1659b17..223c0fb 100644
--- a/ojluni/src/main/java/java/io/UnixFileSystem.java
+++ b/ojluni/src/main/java/java/io/UnixFileSystem.java
@@ -166,7 +166,10 @@
                     }
                 }
                 if (res == null) {
+                    // BEGIN Android-added: BlockGuard support.
                     BlockGuard.getThreadPolicy().onReadFromDisk();
+                    BlockGuard.getVmPolicy().onPathAccess(path);
+                    // END Android-added: BlockGuard support.
                     res = canonicalize0(path);
                     cache.put(path, res);
                     if (useCanonPrefixCache &&
@@ -236,9 +239,11 @@
 
     private native int getBooleanAttributes0(String abspath);
 
-    // Android-changed: Added thread policy check
     public int getBooleanAttributes(File f) {
+        // BEGIN Android-added: BlockGuard support.
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
+        // END Android-added: BlockGuard support.
 
         int rv = getBooleanAttributes0(f.getPath());
         String name = f.getName();
@@ -246,43 +251,47 @@
         return rv | (hidden ? BA_HIDDEN : 0);
     }
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public boolean checkAccess(File f, int access) {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return checkAccess0(f, access);
     }
     private native boolean checkAccess0(File f, int access);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public long getLastModifiedTime(File f) {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return getLastModifiedTime0(f);
     }
     private native long getLastModifiedTime0(File f);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public long getLength(File f) {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return getLength0(f);
     }
     private native long getLength0(File f);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public boolean setPermission(File f, int access, boolean enable, boolean owneronly) {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return setPermission0(f, access, enable, owneronly);
     }
     private native boolean setPermission0(File f, int access, boolean enable, boolean owneronly);
 
     /* -- File operations -- */
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public boolean createFileExclusively(String path) throws IOException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(path);
         return createFileExclusively0(path);
     }
     private native boolean createFileExclusively0(String path) throws IOException;
 
-    // Android-changed: Added thread policy check
     public boolean delete(File f) {
         // Keep canonicalization caches in sync after file deletion
         // and renaming operations. Could be more clever than this
@@ -291,27 +300,31 @@
         // anyway.
         cache.clear();
         javaHomePrefixCache.clear();
+        // BEGIN Android-added: BlockGuard support.
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
+        // END Android-added: BlockGuard support.
         return delete0(f);
     }
 
     private native boolean delete0(File f);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public String[] list(File f) {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return list0(f);
     }
     private native String[] list0(File f);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public boolean createDirectory(File f) {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return createDirectory0(f);
     }
     private native boolean createDirectory0(File f);
 
-    // Android-changed: Added thread policy check
     public boolean rename(File f1, File f2) {
         // Keep canonicalization caches in sync after file deletion
         // and renaming operations. Could be more clever than this
@@ -320,22 +333,28 @@
         // anyway.
         cache.clear();
         javaHomePrefixCache.clear();
+        // BEGIN Android-added: BlockGuard support.
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f1.getPath());
+        BlockGuard.getVmPolicy().onPathAccess(f2.getPath());
+        // END Android-added: BlockGuard support.
         return rename0(f1, f2);
     }
 
     private native boolean rename0(File f1, File f2);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public boolean setLastModifiedTime(File f, long time) {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return setLastModifiedTime0(f, time);
     }
     private native boolean setLastModifiedTime0(File f, long time);
 
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public boolean setReadOnly(File f) {
         BlockGuard.getThreadPolicy().onWriteToDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
         return setReadOnly0(f);
     }
     private native boolean setReadOnly0(File f);
@@ -356,9 +375,10 @@
     }
 
     /* -- Disk usage -- */
-    // Android-changed: Added thread policy check
+    // Android-changed: BlockGuard support.
     public long getSpace(File f, int t) {
         BlockGuard.getThreadPolicy().onReadFromDisk();
+        BlockGuard.getVmPolicy().onPathAccess(f.getPath());
 
         return getSpace0(f, t);
     }