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