Reinstate blockguard checks for file reads / writes.

Also adds a simple unit-test for file based checks.

bug: 25861497

Change-Id: I886321eae657af6531b21eb90c4749de7aec20d7
diff --git a/luni/src/test/java/dalvik/system/BlockGuardTest.java b/luni/src/test/java/dalvik/system/BlockGuardTest.java
new file mode 100644
index 0000000..24313cd
--- /dev/null
+++ b/luni/src/test/java/dalvik/system/BlockGuardTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package dalvik.system;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by narayan on 1/7/16.
+ */
+public class BlockGuardTest extends TestCase {
+
+    private BlockGuard.Policy oldPolicy;
+    private RecordingPolicy recorder = new RecordingPolicy();
+
+    @Override
+    public void setUp() {
+        oldPolicy = BlockGuard.getThreadPolicy();
+        BlockGuard.setThreadPolicy(recorder);
+    }
+
+    @Override
+    public void tearDown() {
+        BlockGuard.setThreadPolicy(oldPolicy);
+        recorder.clear();
+    }
+
+    public void testFile() throws Exception {
+        File f = File.createTempFile("foo", "bar");
+        recorder.expectAndClear("onReadFromDisk", "onWriteToDisk");
+
+        f.getAbsolutePath();
+        f.getParentFile();
+        f.getName();
+        f.getParent();
+        f.getPath();
+        f.isAbsolute();
+        recorder.expectNoViolations();
+
+        f.mkdir();
+        recorder.expectAndClear("onWriteToDisk");
+
+        f.listFiles();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.list();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.length();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.lastModified();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.canExecute();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.canRead();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.canWrite();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.isFile();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.isDirectory();
+        recorder.expectAndClear("onReadFromDisk");
+
+        f.setExecutable(true, false);
+        recorder.expectAndClear("onWriteToDisk");
+
+        f.setReadable(true, false);
+        recorder.expectAndClear("onWriteToDisk");
+
+        f.setWritable(true, false);
+        recorder.expectAndClear("onWriteToDisk");
+
+        f.delete();
+        recorder.expectAndClear("onWriteToDisk");
+    }
+
+    public void testFileInputStream() throws Exception {
+        File f = new File("/proc/version");
+        recorder.clear();
+
+        FileInputStream fis = new FileInputStream(f);
+        recorder.expectAndClear("onReadFromDisk");
+
+        fis.read(new byte[4],0, 4);
+        recorder.expectAndClear("onReadFromDisk");
+
+        fis.read();
+        recorder.expectAndClear("onReadFromDisk");
+
+        fis.skip(1);
+        recorder.expectAndClear("onReadFromDisk");
+
+        fis.close();
+    }
+
+    public void testFileOutputStream() throws Exception {
+        File f = File.createTempFile("foo", "bar");
+        recorder.clear();
+
+        FileOutputStream fos = new FileOutputStream(f);
+        recorder.expectAndClear("onWriteToDisk");
+
+        fos.write(new byte[3]);
+        recorder.expectAndClear("onWriteToDisk");
+
+        fos.write(4);
+        recorder.expectAndClear("onWriteToDisk");
+
+        fos.flush();
+        recorder.expectNoViolations();
+
+        fos.close();
+        recorder.expectNoViolations();
+    }
+
+
+    public static class RecordingPolicy implements BlockGuard.Policy {
+        private final List<String> violations = new ArrayList<>();
+
+        @Override
+        public void onWriteToDisk() {
+            addViolation("onWriteToDisk");
+        }
+
+        @Override
+        public void onReadFromDisk() {
+            addViolation("onReadFromDisk");
+        }
+
+        @Override
+        public void onNetwork() {
+            addViolation("onNetwork");
+        }
+
+        private void addViolation(String type) {
+            StackTraceElement[] threadTrace = Thread.currentThread().getStackTrace();
+
+            final StackTraceElement violator = threadTrace[4];
+            violations.add(type + " [caller= " + violator.getMethodName() + "]");
+        }
+
+        public void clear() {
+            violations.clear();
+        }
+
+        public void expectNoViolations() {
+            if (violations.size() != 0) {
+                throw new AssertionError("Expected 0 violations but found " + violations.size());
+            }
+        }
+
+        public void expectAndClear(String... expected) {
+            if (expected.length != violations.size()) {
+                throw new AssertionError("Expected " + expected.length + " violations but found "
+                        + violations.size());
+            }
+
+            for (int i = 0; i < expected.length; ++i) {
+                if (!violations.get(i).startsWith(expected[i])) {
+                    throw new AssertionError("Expected: " + expected[i] + " but was "
+                            + violations.get(i));
+                }
+            }
+
+            clear();
+        }
+
+        @Override
+        public int getPolicyMask() {
+            return 0;
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/io/File.java b/ojluni/src/main/java/java/io/File.java
index ee51a05..4d1f9d1 100755
--- a/ojluni/src/main/java/java/io/File.java
+++ b/ojluni/src/main/java/java/io/File.java
@@ -153,7 +153,7 @@
     /**
      * The FileSystem object representing the platform's local file system.
      */
-    static private FileSystem fs = FileSystem.getFileSystem();
+    static private final FileSystem fs = FileSystem.getFileSystem();
 
     /**
      * This abstract pathname's normalized pathname string. A normalized
diff --git a/ojluni/src/main/java/java/io/FileInputStream.java b/ojluni/src/main/java/java/io/FileInputStream.java
index b6b3780..3f7531a 100755
--- a/ojluni/src/main/java/java/io/FileInputStream.java
+++ b/ojluni/src/main/java/java/io/FileInputStream.java
@@ -27,6 +27,8 @@
 package java.io;
 
 import java.nio.channels.FileChannel;
+
+import dalvik.system.BlockGuard;
 import sun.nio.ch.FileChannelImpl;
 import sun.misc.IoTrace;
 import libcore.io.IoBridge;
@@ -135,6 +137,8 @@
         fd = new FileDescriptor();
         fd.incrementAndGetUseCount();
         this.path = name;
+
+        BlockGuard.getThreadPolicy().onReadFromDisk();
         open(name);
     }
 
@@ -284,6 +288,7 @@
         }
 
         try {
+            BlockGuard.getThreadPolicy().onReadFromDisk();
             return skip0(n);
         } catch(UseManualSkipException e) {
             return super.skip(n);
diff --git a/ojluni/src/main/java/java/io/FileOutputStream.java b/ojluni/src/main/java/java/io/FileOutputStream.java
index d6cee54..4200e06 100755
--- a/ojluni/src/main/java/java/io/FileOutputStream.java
+++ b/ojluni/src/main/java/java/io/FileOutputStream.java
@@ -27,6 +27,8 @@
 package java.io;
 
 import java.nio.channels.FileChannel;
+
+import dalvik.system.BlockGuard;
 import sun.nio.ch.FileChannelImpl;
 import sun.misc.IoTrace;
 import libcore.io.IoBridge;
@@ -210,6 +212,8 @@
         this.append = append;
         this.path = name;
         fd.incrementAndGetUseCount();
+
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         open(name, append);
     }
 
diff --git a/ojluni/src/main/java/java/io/UnixFileSystem.java b/ojluni/src/main/java/java/io/UnixFileSystem.java
index c2b39b8..60777f6 100755
--- a/ojluni/src/main/java/java/io/UnixFileSystem.java
+++ b/ojluni/src/main/java/java/io/UnixFileSystem.java
@@ -26,6 +26,8 @@
 package java.io;
 
 import java.security.AccessController;
+
+import dalvik.system.BlockGuard;
 import sun.security.action.GetPropertyAction;
 
 
@@ -169,6 +171,7 @@
                     }
                 }
                 if (res == null) {
+                    BlockGuard.getThreadPolicy().onReadFromDisk();
                     res = canonicalize0(path);
                     cache.put(path, res);
                     if (useCanonPrefixCache &&
@@ -236,29 +239,54 @@
 
     /* -- Attribute accessors -- */
 
-    /* ----- BEGIN android -----
-    public native int getBooleanAttributes0(File f);*/
-    public native int getBooleanAttributes0(String abspath);
+    private native int getBooleanAttributes0(String abspath);
 
     public int getBooleanAttributes(File f) {
-        /* ----- BEGIN android -----
-        int rv = getBooleanAttributes0(f);*/
+        BlockGuard.getThreadPolicy().onReadFromDisk();
+
         int rv = getBooleanAttributes0(f.getPath());
-        // ----- END android -----
         String name = f.getName();
         boolean hidden = (name.length() > 0) && (name.charAt(0) == '.');
         return rv | (hidden ? BA_HIDDEN : 0);
     }
 
-    public native boolean checkAccess(File f, int access);
-    public native long getLastModifiedTime(File f);
-    public native long getLength(File f);
-    public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
+    public boolean checkAccess(File f, int access) {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
+        return checkAccess0(f, access);
+    }
+
+    private native boolean checkAccess0(File f, int access);
+
+    public long getLastModifiedTime(File f) {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
+        return getLastModifiedTime0(f);
+    }
+
+    private native long getLastModifiedTime0(File f);
+
+    public long getLength(File f) {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
+        return getLength0(f);
+    }
+
+    private native long getLength0(File f);
+
+    public boolean setPermission(File f, int access, boolean enable, boolean owneronly) {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
+        return setPermission0(f, access, enable, owneronly);
+    }
+
+    private native boolean setPermission0(File f, int access, boolean enable, boolean owneronly);
 
     /* -- File operations -- */
 
-    public native boolean createFileExclusively(String path)
-        throws IOException;
+    public boolean createFileExclusively(String path) throws IOException {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
+        return createFileExclusively0(path);
+    }
+
+    private native boolean createFileExclusively0(String path) throws IOException;
+
     public boolean delete(File f) {
         // Keep canonicalization caches in sync after file deletion
         // and renaming operations. Could be more clever than this
@@ -267,11 +295,26 @@
         // anyway.
         cache.clear();
         javaHomePrefixCache.clear();
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return delete0(f);
     }
+
     private native boolean delete0(File f);
-    public native String[] list(File f);
-    public native boolean createDirectory(File f);
+
+    public String[] list(File f) {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
+        return list0(f);
+    }
+
+    private native String[] list0(File f);
+
+    public boolean createDirectory(File f) {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
+        return createDirectory0(f);
+    }
+
+    private native boolean createDirectory0(File f);
+
     public boolean rename(File f1, File f2) {
         // Keep canonicalization caches in sync after file deletion
         // and renaming operations. Could be more clever than this
@@ -280,11 +323,25 @@
         // anyway.
         cache.clear();
         javaHomePrefixCache.clear();
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return rename0(f1, f2);
     }
+
     private native boolean rename0(File f1, File f2);
-    public native boolean setLastModifiedTime(File f, long time);
-    public native boolean setReadOnly(File f);
+
+    public boolean setLastModifiedTime(File f, long time) {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
+        return setLastModifiedTime0(f, time);
+    }
+
+    private native boolean setLastModifiedTime0(File f, long time);
+
+    public boolean setReadOnly(File f) {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
+        return setReadOnly0(f);
+    }
+
+    private native boolean setReadOnly0(File f);
 
 
     /* -- Filesystem interface -- */
@@ -302,7 +359,13 @@
     }
 
     /* -- Disk usage -- */
-    public native long getSpace(File f, int t);
+    public long getSpace(File f, int t) {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
+
+        return getSpace0(f, t);
+    }
+
+    private native long getSpace0(File f, int t);
 
     /* -- Basic infrastructure -- */
 
diff --git a/ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java b/ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java
index e4de807..3e0e630 100755
--- a/ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java
+++ b/ojluni/src/main/java/sun/nio/ch/FileChannelImpl.java
@@ -35,6 +35,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.security.AccessController;
+
+import dalvik.system.BlockGuard;
 import sun.misc.Cleaner;
 import sun.misc.IoTrace;
 import sun.security.action.GetPropertyAction;
@@ -264,6 +266,9 @@
                 ti = threads.add();
                 if (!isOpen())
                     return 0;
+                if (append) {
+                    BlockGuard.getThreadPolicy().onWriteToDisk();
+                }
                 do {
                     // in append-mode then position is advanced to end before writing
                     p = (append) ? nd.size(fd) : position0(fd, -1);
@@ -289,6 +294,7 @@
                 ti = threads.add();
                 if (!isOpen())
                     return null;
+                BlockGuard.getThreadPolicy().onReadFromDisk();
                 do {
                     p  = position0(fd, newPosition);
                 } while ((p == IOStatus.INTERRUPTED) && isOpen());
@@ -445,6 +451,7 @@
             ti = threads.add();
             if (!isOpen())
                 return -1;
+            BlockGuard.getThreadPolicy().onWriteToDisk();
             do {
                 n = transferTo0(thisFDVal, position, icount, targetFDVal);
             } while ((n == IOStatus.INTERRUPTED) && isOpen());
@@ -905,6 +912,7 @@
             long mapSize = size + pagePosition;
             try {
                 // If no exception was thrown from map0, the address is valid
+                BlockGuard.getThreadPolicy().onReadFromDisk();
                 addr = map0(imode, mapPosition, mapSize);
             } catch (OutOfMemoryError x) {
                 // An OutOfMemoryError may indicate that we've exhausted memory
diff --git a/ojluni/src/main/java/sun/nio/ch/FileDispatcherImpl.java b/ojluni/src/main/java/sun/nio/ch/FileDispatcherImpl.java
index 8fcfae6..e57ccc5 100755
--- a/ojluni/src/main/java/sun/nio/ch/FileDispatcherImpl.java
+++ b/ojluni/src/main/java/sun/nio/ch/FileDispatcherImpl.java
@@ -25,6 +25,8 @@
 
 package sun.nio.ch;
 
+import dalvik.system.BlockGuard;
+
 import java.io.*;
 
 class FileDispatcherImpl extends FileDispatcher
@@ -42,54 +44,65 @@
     }
 
     int read(FileDescriptor fd, long address, int len) throws IOException {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
         return read0(fd, address, len);
     }
 
     int pread(FileDescriptor fd, long address, int len, long position)
         throws IOException
     {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
         return pread0(fd, address, len, position);
     }
 
     long readv(FileDescriptor fd, long address, int len) throws IOException {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
         return readv0(fd, address, len);
     }
 
     int write(FileDescriptor fd, long address, int len) throws IOException {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return write0(fd, address, len);
     }
 
     int pwrite(FileDescriptor fd, long address, int len, long position)
         throws IOException
     {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return pwrite0(fd, address, len, position);
     }
 
     long writev(FileDescriptor fd, long address, int len)
         throws IOException
     {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return writev0(fd, address, len);
     }
 
     int force(FileDescriptor fd, boolean metaData) throws IOException {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return force0(fd, metaData);
     }
 
     int truncate(FileDescriptor fd, long size) throws IOException {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return truncate0(fd, size);
     }
 
     long size(FileDescriptor fd) throws IOException {
+        BlockGuard.getThreadPolicy().onReadFromDisk();
         return size0(fd);
     }
 
     int lock(FileDescriptor fd, boolean blocking, long pos, long size,
              boolean shared) throws IOException
     {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         return lock0(fd, blocking, pos, size, shared);
     }
 
     void release(FileDescriptor fd, long pos, long size) throws IOException {
+        BlockGuard.getThreadPolicy().onWriteToDisk();
         release0(fd, pos, size);
     }
 
diff --git a/ojluni/src/main/native/UnixFileSystem_md.c b/ojluni/src/main/native/UnixFileSystem_md.c
index 578fde0..7cbb753 100755
--- a/ojluni/src/main/native/UnixFileSystem_md.c
+++ b/ojluni/src/main/native/UnixFileSystem_md.c
@@ -129,8 +129,8 @@
 }
 
 JNIEXPORT jboolean JNICALL
-Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
-                                        jobject file, jint a)
+Java_java_io_UnixFileSystem_checkAccess0(JNIEnv *env, jobject this,
+                                         jobject file, jint a)
 {
     jboolean rv = JNI_FALSE;
     int mode = 0;
@@ -159,11 +159,11 @@
 
 
 JNIEXPORT jboolean JNICALL
-Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
-                                          jobject file,
-                                          jint access,
-                                          jboolean enable,
-                                          jboolean owneronly)
+Java_java_io_UnixFileSystem_setPermission0(JNIEnv *env, jobject this,
+                                           jobject file,
+                                           jint access,
+                                           jboolean enable,
+                                           jboolean owneronly)
 {
     jboolean rv = JNI_FALSE;
 
@@ -206,8 +206,8 @@
 }
 
 JNIEXPORT jlong JNICALL
-Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
-                                                jobject file)
+Java_java_io_UnixFileSystem_getLastModifiedTime0(JNIEnv *env, jobject this,
+                                                 jobject file)
 {
     jlong rv = 0;
 
@@ -222,8 +222,8 @@
 
 
 JNIEXPORT jlong JNICALL
-Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
-                                      jobject file)
+Java_java_io_UnixFileSystem_getLength0(JNIEnv *env, jobject this,
+                                       jobject file)
 {
     jlong rv = 0;
 
@@ -241,8 +241,8 @@
 
 
 JNIEXPORT jboolean JNICALL
-Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
-                                                  jstring pathname)
+Java_java_io_UnixFileSystem_createFileExclusively0(JNIEnv *env, jclass cls,
+                                                   jstring pathname)
 {
     jboolean rv = JNI_FALSE;
 
@@ -282,8 +282,8 @@
 
 
 JNIEXPORT jobjectArray JNICALL
-Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
-                                 jobject file)
+Java_java_io_UnixFileSystem_list0(JNIEnv *env, jobject this,
+                                  jobject file)
 {
     DIR *dir = NULL;
     struct dirent64 *ptr;
@@ -353,8 +353,8 @@
 
 
 JNIEXPORT jboolean JNICALL
-Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
-                                            jobject file)
+Java_java_io_UnixFileSystem_createDirectory0(JNIEnv *env, jobject this,
+                                             jobject file)
 {
     jboolean rv = JNI_FALSE;
 
@@ -384,8 +384,8 @@
 }
 
 JNIEXPORT jboolean JNICALL
-Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
-                                                jobject file, jlong time)
+Java_java_io_UnixFileSystem_setLastModifiedTime0(JNIEnv *env, jobject this,
+                                                 jobject file, jlong time)
 {
     jboolean rv = JNI_FALSE;
 
@@ -413,8 +413,8 @@
 
 
 JNIEXPORT jboolean JNICALL
-Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
-                                        jobject file)
+Java_java_io_UnixFileSystem_setReadOnly0(JNIEnv *env, jobject this,
+                                         jobject file)
 {
     jboolean rv = JNI_FALSE;
 
@@ -430,8 +430,8 @@
 }
 
 JNIEXPORT jlong JNICALL
-Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
-                                     jobject file, jint t)
+Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
+                                      jobject file, jint t)
 {
     jlong rv = 0L;