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
setUp()39     public void setUp() {
40         recorder.setChecks(EnumSet.allOf(RecordingPolicy.Check.class));
41         oldPolicy = BlockGuard.getThreadPolicy();
42         BlockGuard.setThreadPolicy(recorder);
43     }
44 
45     @Override
tearDown()46     public void tearDown() {
47         BlockGuard.setThreadPolicy(oldPolicy);
48         recorder.clear();
49     }
50 
testFile()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 
testFileInputStream()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 
testFileOutputStream()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 
testUnbufferedIO()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 
testOpen()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 
setChecks(EnumSet<Check> checksList)278         public void setChecks(EnumSet<Check> checksList) {
279             this.checksList = checksList;
280         }
281 
282         @Override
onWriteToDisk()283         public void onWriteToDisk() {
284             if (checksList != null && checksList.contains(Check.WRITE_TO_DISK)) {
285                 addViolation("onWriteToDisk");
286             }
287         }
288 
289         @Override
onReadFromDisk()290         public void onReadFromDisk() {
291             if (checksList != null && checksList.contains(Check.READ_FROM_DISK)) {
292                 addViolation("onReadFromDisk");
293             }
294         }
295 
296         @Override
onNetwork()297         public void onNetwork() {
298             if (checksList != null && checksList.contains(Check.NETWORK)) {
299                 addViolation("onNetwork");
300             }
301         }
302 
303         @Override
onUnbufferedIO()304         public void onUnbufferedIO() {
305             if (checksList != null && checksList.contains(Check.UNBUFFERED_IO)) {
306                 addViolation("onUnbufferedIO");
307             }
308         }
309 
addViolation(String type)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 
clear()317         public void clear() {
318             violations.clear();
319         }
320 
expectNoViolations()321         public void expectNoViolations() {
322             if (violations.size() != 0) {
323                 throw new AssertionError("Expected 0 violations but found " + violations.size());
324             }
325         }
326 
expectAndClear(String... expected)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
getPolicyMask()344         public int getPolicyMask() {
345             return 0;
346         }
347     }
348 }
349