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