Home | History | Annotate | Download | only in system
      1 /*
      2  * Copyright (C) 2016 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 
     17 package dalvik.system;
     18 
     19 import android.system.Os;
     20 import android.system.OsConstants;
     21 import junit.framework.TestCase;
     22 
     23 import java.io.File;
     24 import java.io.FileDescriptor;
     25 import java.io.FileInputStream;
     26 import java.io.FileOutputStream;
     27 import java.io.RandomAccessFile;
     28 import java.util.ArrayList;
     29 import java.util.EnumSet;
     30 import java.util.List;
     31 import java.util.Set;
     32 
     33 public class BlockGuardTest extends TestCase {
     34 
     35     private BlockGuard.Policy oldPolicy;
     36     private RecordingPolicy recorder = new RecordingPolicy();
     37 
     38     @Override
     39     public void setUp() {
     40         recorder.setChecks(EnumSet.allOf(RecordingPolicy.Check.class));
     41         oldPolicy = BlockGuard.getThreadPolicy();
     42         BlockGuard.setThreadPolicy(recorder);
     43     }
     44 
     45     @Override
     46     public void tearDown() {
     47         BlockGuard.setThreadPolicy(oldPolicy);
     48         recorder.clear();
     49     }
     50 
     51     public void testFile() throws Exception {
     52         File f = File.createTempFile("foo", "bar");
     53         recorder.expectAndClear("onReadFromDisk", "onWriteToDisk");
     54 
     55         f.getAbsolutePath();
     56         f.getParentFile();
     57         f.getName();
     58         f.getParent();
     59         f.getPath();
     60         f.isAbsolute();
     61         recorder.expectNoViolations();
     62 
     63         f.mkdir();
     64         recorder.expectAndClear("onWriteToDisk");
     65 
     66         f.listFiles();
     67         recorder.expectAndClear("onReadFromDisk");
     68 
     69         f.list();
     70         recorder.expectAndClear("onReadFromDisk");
     71 
     72         f.length();
     73         recorder.expectAndClear("onReadFromDisk");
     74 
     75         f.lastModified();
     76         recorder.expectAndClear("onReadFromDisk");
     77 
     78         f.canExecute();
     79         recorder.expectAndClear("onReadFromDisk");
     80 
     81         f.canRead();
     82         recorder.expectAndClear("onReadFromDisk");
     83 
     84         f.canWrite();
     85         recorder.expectAndClear("onReadFromDisk");
     86 
     87         f.isFile();
     88         recorder.expectAndClear("onReadFromDisk");
     89 
     90         f.isDirectory();
     91         recorder.expectAndClear("onReadFromDisk");
     92 
     93         f.setExecutable(true, false);
     94         recorder.expectAndClear("onWriteToDisk");
     95 
     96         f.setReadable(true, false);
     97         recorder.expectAndClear("onWriteToDisk");
     98 
     99         f.setWritable(true, false);
    100         recorder.expectAndClear("onWriteToDisk");
    101 
    102         f.delete();
    103         recorder.expectAndClear("onWriteToDisk");
    104     }
    105 
    106     public void testFileInputStream() throws Exception {
    107         // The file itself doesn't matter: it just has to exist and allow the creation of the
    108         // FileInputStream. The BlockGuard should have the same behavior towards a normal file and
    109         // system file.
    110         File tmpFile = File.createTempFile("inputFile", ".txt");
    111         try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
    112             fos.write("01234567890".getBytes());
    113         }
    114 
    115         try {
    116             recorder.clear();
    117 
    118             FileInputStream fis = new FileInputStream(tmpFile);
    119             recorder.expectAndClear("onReadFromDisk");
    120 
    121             fis.read(new byte[4], 0, 4);
    122             recorder.expectAndClear("onReadFromDisk");
    123 
    124             fis.read();
    125             recorder.expectAndClear("onReadFromDisk");
    126 
    127             fis.skip(1);
    128             recorder.expectAndClear("onReadFromDisk");
    129 
    130             fis.close();
    131         } finally {
    132             tmpFile.delete();
    133         }
    134     }
    135 
    136     public void testFileOutputStream() throws Exception {
    137         File f = File.createTempFile("foo", "bar");
    138         recorder.clear();
    139 
    140         FileOutputStream fos = new FileOutputStream(f);
    141         recorder.expectAndClear("onWriteToDisk");
    142 
    143         fos.write(new byte[3]);
    144         recorder.expectAndClear("onWriteToDisk");
    145 
    146         fos.write(4);
    147         recorder.expectAndClear("onWriteToDisk");
    148 
    149         fos.flush();
    150         recorder.expectNoViolations();
    151 
    152         fos.close();
    153         recorder.expectNoViolations();
    154     }
    155 
    156     public void testUnbufferedIO() throws Exception {
    157         File f = File.createTempFile("foo", "bar");
    158         recorder.setChecks(EnumSet.of(RecordingPolicy.Check.UNBUFFERED_IO));
    159         recorder.clear();
    160 
    161         try (FileOutputStream fos = new FileOutputStream(f)) {
    162             recorder.expectNoViolations();
    163             for (int i = 0; i < 11; i++) {
    164                 recorder.expectNoViolations();
    165                 fos.write("a".getBytes());
    166             }
    167             recorder.expectAndClear("onUnbufferedIO");
    168         }
    169 
    170         try (FileInputStream fis = new FileInputStream(new File("/dev/null"))) {
    171             recorder.expectNoViolations();
    172             byte[] b = new byte[1];
    173             for (int i = 0; i < 11; i++) {
    174                 recorder.expectNoViolations();
    175                 fis.read(b);
    176             }
    177             recorder.expectAndClear("onUnbufferedIO");
    178         }
    179 
    180         try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) {
    181             // seek should reset the IoTracker.
    182             ras.seek(0);
    183             recorder.expectNoViolations();
    184             for (int i = 0; i < 11; i++) {
    185                 recorder.expectNoViolations();
    186                 ras.read("a".getBytes());
    187             }
    188             recorder.expectAndClear("onUnbufferedIO");
    189         }
    190 
    191         try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) {
    192             // No violation is expected as a write is called while reading which should reset the
    193             // IoTracker counter.
    194             for (int i = 0; i < 11; i++) {
    195                 recorder.expectNoViolations();
    196                 if (i == 5) {
    197                     ras.write("a".getBytes());
    198                 }
    199                 ras.read("a".getBytes());
    200             }
    201             recorder.expectNoViolations();
    202         }
    203 
    204         try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) {
    205             // No violation is expected as a seek is called while reading which should reset the
    206             // IoTracker counter.
    207             for (int i = 0; i < 11; i++) {
    208                 recorder.expectNoViolations();
    209                 if (i == 5) {
    210                     ras.seek(0);
    211                 }
    212                 ras.read("a".getBytes());
    213             }
    214             recorder.expectNoViolations();
    215         }
    216 
    217         try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) {
    218             // seek should reset the IoTracker.
    219             for (int i = 0; i < 11; i++) {
    220                 recorder.expectNoViolations();
    221                 ras.write("a".getBytes());
    222             }
    223             recorder.expectAndClear("onUnbufferedIO");
    224         }
    225 
    226         try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) {
    227             // No violation is expected as a read is called while writing which should reset the
    228             // IoTracker counter.
    229             for (int i = 0; i < 11; i++) {
    230                 recorder.expectNoViolations();
    231                 if (i == 5) {
    232                     ras.read("a".getBytes());
    233                 }
    234                 ras.write("a".getBytes());
    235             }
    236             recorder.expectNoViolations();
    237         }
    238 
    239         try (RandomAccessFile ras = new RandomAccessFile(f, "rw")) {
    240             for (int i = 0; i < 11; i++) {
    241                 recorder.expectNoViolations();
    242                 if (i == 5) {
    243                     ras.seek(0);
    244                 }
    245                 ras.write("a".getBytes());
    246             }
    247             recorder.expectNoViolations();
    248         }
    249     }
    250 
    251     public void testOpen() throws Exception {
    252         File temp = File.createTempFile("foo", "bar");
    253         recorder.clear();
    254 
    255         // Open in read/write mode : should be recorded as a read and a write to disk.
    256         FileDescriptor fd = Os.open(temp.getPath(), OsConstants.O_RDWR, 0);
    257         recorder.expectAndClear("onReadFromDisk", "onWriteToDisk");
    258         Os.close(fd);
    259 
    260         // Open in read only mode : should be recorded as a read from disk.
    261         recorder.clear();
    262         fd = Os.open(temp.getPath(), OsConstants.O_RDONLY, 0);
    263         recorder.expectAndClear("onReadFromDisk");
    264         Os.close(fd);
    265     }
    266 
    267     public static class RecordingPolicy implements BlockGuard.Policy {
    268         private final List<String> violations = new ArrayList<>();
    269         private Set<Check> checksList;
    270 
    271         public enum Check {
    272             WRITE_TO_DISK,
    273             READ_FROM_DISK,
    274             NETWORK,
    275             UNBUFFERED_IO,
    276         }
    277 
    278         public void setChecks(EnumSet<Check> checksList) {
    279             this.checksList = checksList;
    280         }
    281 
    282         @Override
    283         public void onWriteToDisk() {
    284             if (checksList != null && checksList.contains(Check.WRITE_TO_DISK)) {
    285                 addViolation("onWriteToDisk");
    286             }
    287         }
    288 
    289         @Override
    290         public void onReadFromDisk() {
    291             if (checksList != null && checksList.contains(Check.READ_FROM_DISK)) {
    292                 addViolation("onReadFromDisk");
    293             }
    294         }
    295 
    296         @Override
    297         public void onNetwork() {
    298             if (checksList != null && checksList.contains(Check.NETWORK)) {
    299                 addViolation("onNetwork");
    300             }
    301         }
    302 
    303         @Override
    304         public void onUnbufferedIO() {
    305             if (checksList != null && checksList.contains(Check.UNBUFFERED_IO)) {
    306                 addViolation("onUnbufferedIO");
    307             }
    308         }
    309 
    310         private void addViolation(String type) {
    311             StackTraceElement[] threadTrace = Thread.currentThread().getStackTrace();
    312 
    313             final StackTraceElement violator = threadTrace[4];
    314             violations.add(type + " [caller= " + violator.getMethodName() + "]");
    315         }
    316 
    317         public void clear() {
    318             violations.clear();
    319         }
    320 
    321         public void expectNoViolations() {
    322             if (violations.size() != 0) {
    323                 throw new AssertionError("Expected 0 violations but found " + violations.size());
    324             }
    325         }
    326 
    327         public void expectAndClear(String... expected) {
    328             if (expected.length != violations.size()) {
    329                 throw new AssertionError("Expected " + expected.length + " violations but found "
    330                         + violations.size());
    331             }
    332 
    333             for (int i = 0; i < expected.length; ++i) {
    334                 if (!violations.get(i).startsWith(expected[i])) {
    335                     throw new AssertionError("Expected: " + expected[i] + " but was "
    336                             + violations.get(i));
    337                 }
    338             }
    339 
    340             clear();
    341         }
    342 
    343         @Override
    344         public int getPolicyMask() {
    345             return 0;
    346         }
    347     }
    348 }
    349