1 /* 2 * Copyright (C) 2011 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 android.os.storage.cts; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.content.res.Resources.NotFoundException; 22 import android.os.Environment; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.Parcel; 26 import android.os.ParcelFileDescriptor; 27 import android.os.ProxyFileDescriptorCallback; 28 import android.os.cts.R; 29 import android.os.storage.OnObbStateChangeListener; 30 import android.os.storage.StorageManager; 31 import android.os.storage.StorageVolume; 32 import android.platform.test.annotations.AppModeFull; 33 import android.system.ErrnoException; 34 import android.system.Os; 35 import android.system.OsConstants; 36 import android.test.AndroidTestCase; 37 import android.test.ComparisonFailure; 38 import android.util.Log; 39 40 import com.android.compatibility.common.util.FileUtils; 41 42 import junit.framework.AssertionFailedError; 43 44 import java.io.ByteArrayOutputStream; 45 import java.io.File; 46 import java.io.FileInputStream; 47 import java.io.IOException; 48 import java.io.InputStream; 49 import java.io.InterruptedIOException; 50 import java.io.FileDescriptor; 51 import java.io.SyncFailedException; 52 import java.lang.reflect.Field; 53 import java.lang.reflect.Modifier; 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.UUID; 57 import java.util.concurrent.CountDownLatch; 58 import java.util.concurrent.SynchronousQueue; 59 60 public class StorageManagerTest extends AndroidTestCase { 61 62 private static final String TAG = StorageManager.class.getSimpleName(); 63 64 private static final long MAX_WAIT_TIME = 25*1000; 65 private static final long WAIT_TIME_INCR = 5*1000; 66 67 private static final String OBB_MOUNT_PREFIX = "/mnt/obb/"; 68 private static final String TEST1_NEW_CONTENTS = "1\n"; 69 70 private StorageManager mStorageManager; 71 private final Handler mHandler = new Handler(Looper.getMainLooper()); 72 73 @Override setUp()74 protected void setUp() throws Exception { 75 super.setUp(); 76 mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); 77 } 78 79 @AppModeFull(reason = "Instant apps cannot access external storage") testMountAndUnmountObbNormal()80 public void testMountAndUnmountObbNormal() throws IOException { 81 for (File target : getTargetFiles()) { 82 target = new File(target, "test1_new.obb"); 83 Log.d(TAG, "Testing path " + target); 84 doMountAndUnmountObbNormal(target); 85 } 86 } 87 doMountAndUnmountObbNormal(File outFile)88 private void doMountAndUnmountObbNormal(File outFile) throws IOException { 89 final String canonPath = mountObb(R.raw.test1_new, outFile, OnObbStateChangeListener.MOUNTED); 90 91 mountObb(R.raw.test1_new, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 92 93 try { 94 final String mountPath = checkMountedPath(canonPath); 95 final File mountDir = new File(mountPath); 96 final File testFile = new File(mountDir, "test1.txt"); 97 98 assertTrue("OBB mounted path should be a directory", mountDir.isDirectory()); 99 assertTrue("test1.txt does not exist in OBB dir", testFile.exists()); 100 assertFileContains(testFile, TEST1_NEW_CONTENTS); 101 } finally { 102 unmountObb(outFile, OnObbStateChangeListener.UNMOUNTED); 103 } 104 } 105 106 @AppModeFull(reason = "Instant apps cannot access external storage") testAttemptMountNonObb()107 public void testAttemptMountNonObb() { 108 for (File target : getTargetFiles()) { 109 target = new File(target, "test1_nosig.obb"); 110 Log.d(TAG, "Testing path " + target); 111 doAttemptMountNonObb(target); 112 } 113 } 114 doAttemptMountNonObb(File outFile)115 private void doAttemptMountNonObb(File outFile) { 116 try { 117 mountObb(R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL); 118 fail("mountObb should've failed with an exception"); 119 } catch (IllegalArgumentException e) { 120 // Expected 121 } 122 123 assertFalse("OBB should not be mounted", 124 mStorageManager.isObbMounted(outFile.getPath())); 125 126 assertNull("OBB's mounted path should be null", 127 mStorageManager.getMountedObbPath(outFile.getPath())); 128 } 129 130 @AppModeFull(reason = "Instant apps cannot access external storage") testAttemptMountObbWrongPackage()131 public void testAttemptMountObbWrongPackage() { 132 for (File target : getTargetFiles()) { 133 target = new File(target, "test1_wrongpackage.obb"); 134 Log.d(TAG, "Testing path " + target); 135 doAttemptMountObbWrongPackage(target); 136 } 137 } 138 doAttemptMountObbWrongPackage(File outFile)139 private void doAttemptMountObbWrongPackage(File outFile) { 140 mountObb(R.raw.test1_wrongpackage, outFile, 141 OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 142 143 assertFalse("OBB should not be mounted", 144 mStorageManager.isObbMounted(outFile.getPath())); 145 146 assertNull("OBB's mounted path should be null", 147 mStorageManager.getMountedObbPath(outFile.getPath())); 148 } 149 150 @AppModeFull(reason = "Instant apps cannot access external storage") testMountAndUnmountTwoObbs()151 public void testMountAndUnmountTwoObbs() throws IOException { 152 for (File target : getTargetFiles()) { 153 Log.d(TAG, "Testing target " + target); 154 final File test1 = new File(target, "test1.obb"); 155 final File test2 = new File(target, "test2.obb"); 156 doMountAndUnmountTwoObbs(test1, test2); 157 } 158 } 159 doMountAndUnmountTwoObbs(File file1, File file2)160 private void doMountAndUnmountTwoObbs(File file1, File file2) throws IOException { 161 ObbObserver oo1 = mountObbWithoutWait(R.raw.test1_new, file1); 162 ObbObserver oo2 = mountObbWithoutWait(R.raw.test1_new, file2); 163 164 Log.d(TAG, "Waiting for OBB #1 to complete mount"); 165 waitForObbActionCompletion(file1, oo1, OnObbStateChangeListener.MOUNTED); 166 Log.d(TAG, "Waiting for OBB #2 to complete mount"); 167 waitForObbActionCompletion(file2, oo2, OnObbStateChangeListener.MOUNTED); 168 169 try { 170 final String mountPath1 = checkMountedPath(oo1.getPath()); 171 final File mountDir1 = new File(mountPath1); 172 final File testFile1 = new File(mountDir1, "test1.txt"); 173 assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory()); 174 assertTrue("test1.txt does not exist in OBB dir", testFile1.exists()); 175 assertFileContains(testFile1, TEST1_NEW_CONTENTS); 176 177 final String mountPath2 = checkMountedPath(oo2.getPath()); 178 final File mountDir2 = new File(mountPath2); 179 final File testFile2 = new File(mountDir2, "test1.txt"); 180 assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory()); 181 assertTrue("test1.txt does not exist in OBB dir", testFile2.exists()); 182 assertFileContains(testFile2, TEST1_NEW_CONTENTS); 183 } finally { 184 unmountObb(file1, OnObbStateChangeListener.UNMOUNTED); 185 unmountObb(file2, OnObbStateChangeListener.UNMOUNTED); 186 } 187 } 188 testGetPrimaryVolume()189 public void testGetPrimaryVolume() throws Exception { 190 final StorageVolume volume = mStorageManager.getPrimaryStorageVolume(); 191 assertNotNull("Did not get primary storage", volume); 192 193 // Tests some basic Scoped Directory Access requests. 194 assertNull("Should not grant access for root directory", volume.createAccessIntent(null)); 195 assertNull("Should not grant access for invalid directory", 196 volume.createAccessIntent("/system")); 197 assertNotNull("Should grant access for valid directory " + Environment.DIRECTORY_DOCUMENTS, 198 volume.createAccessIntent(Environment.DIRECTORY_DOCUMENTS)); 199 200 // Tests basic properties. 201 assertNotNull("Should have description", volume.getDescription(mContext)); 202 assertTrue("Should be primary", volume.isPrimary()); 203 assertEquals("Wrong state", Environment.MEDIA_MOUNTED, volume.getState()); 204 205 // Tests properties that depend on storage type (emulated or physical) 206 final String uuid = volume.getUuid(); 207 final boolean removable = volume.isRemovable(); 208 final boolean emulated = volume.isEmulated(); 209 if (emulated) { 210 assertFalse("Should not be removable", removable); 211 assertNull("Should not have uuid", uuid); 212 } else { 213 assertTrue("Should be removable", removable); 214 assertNotNull("Should have uuid", uuid); 215 } 216 217 // Tests path - although it's not a public API, sm.getPrimaryStorageVolume() 218 // explicitly states it should match Environment#getExternalStorageDirectory 219 final String path = volume.getPath(); 220 assertEquals("Path does not match Environment's", 221 Environment.getExternalStorageDirectory().getAbsolutePath(), path); 222 223 // Tests Parcelable contract. 224 assertEquals("Wrong describeContents", 0, volume.describeContents()); 225 final Parcel parcel = Parcel.obtain(); 226 try { 227 volume.writeToParcel(parcel, 0); 228 parcel.setDataPosition(0); 229 final StorageVolume clone = StorageVolume.CREATOR.createFromParcel(parcel); 230 assertStorageVolumesEquals(volume, clone); 231 } finally { 232 parcel.recycle(); 233 } 234 } 235 testGetStorageVolumes()236 public void testGetStorageVolumes() throws Exception { 237 final List<StorageVolume> volumes = mStorageManager.getStorageVolumes(); 238 assertFalse("No volume return", volumes.isEmpty()); 239 StorageVolume primary = null; 240 for (StorageVolume volume : volumes) { 241 if (volume.isPrimary()) { 242 assertNull("There can be only one primary volume: " + volumes, primary); 243 primary = volume; 244 } 245 } 246 assertNotNull("No primary volume on " + volumes, primary); 247 assertStorageVolumesEquals(primary, mStorageManager.getPrimaryStorageVolume()); 248 } 249 testGetStorageVolume()250 public void testGetStorageVolume() throws Exception { 251 assertNull("Should not get volume for null path", 252 mStorageManager.getStorageVolume((File) null)); 253 assertNull("Should not get volume for invalid path", 254 mStorageManager.getStorageVolume(new File("/system"))); 255 assertNull("Should not get volume for storage directory", 256 mStorageManager.getStorageVolume(Environment.getStorageDirectory())); 257 258 final File root = Environment.getExternalStorageDirectory(); 259 final StorageVolume primary = mStorageManager.getPrimaryStorageVolume(); 260 final StorageVolume rootVolume = mStorageManager.getStorageVolume(root); 261 assertNotNull("No volume for root (" + root + ")", rootVolume); 262 assertStorageVolumesEquals(primary, rootVolume); 263 264 final File child = new File(root, "child"); 265 StorageVolume childVolume = mStorageManager.getStorageVolume(child); 266 assertNotNull("No volume for child (" + child + ")", childVolume); 267 assertStorageVolumesEquals(primary, childVolume); 268 } 269 assertNoUuid(File file)270 private void assertNoUuid(File file) { 271 try { 272 final UUID uuid = mStorageManager.getUuidForPath(file); 273 fail("Unexpected UUID " + uuid + " for " + file); 274 } catch (IOException expected) { 275 } 276 } 277 testGetUuidForPath()278 public void testGetUuidForPath() throws Exception { 279 assertEquals(StorageManager.UUID_DEFAULT, 280 mStorageManager.getUuidForPath(Environment.getDataDirectory())); 281 assertEquals(StorageManager.UUID_DEFAULT, 282 mStorageManager.getUuidForPath(mContext.getDataDir())); 283 284 assertNoUuid(new File("/")); 285 assertNoUuid(new File("/proc/")); 286 } 287 288 @AppModeFull(reason = "Instant apps cannot access external storage") testGetExternalUuidForPath()289 public void testGetExternalUuidForPath() throws Exception { 290 final UUID extUuid = mStorageManager 291 .getUuidForPath(Environment.getExternalStorageDirectory()); 292 if (Environment.isExternalStorageEmulated()) { 293 assertEquals(StorageManager.UUID_DEFAULT, extUuid); 294 } 295 296 assertEquals(extUuid, mStorageManager.getUuidForPath(mContext.getExternalCacheDir())); 297 assertEquals(extUuid, mStorageManager.getUuidForPath(new File("/sdcard/"))); 298 } 299 300 private static class TestProxyFileDescriptorCallback extends ProxyFileDescriptorCallback { 301 final byte[] bytes; 302 int fsyncCount; 303 int releaseCount; 304 ErrnoException onGetSizeError = null; 305 ErrnoException onReadError = null; 306 ErrnoException onWriteError = null; 307 ErrnoException onFsyncError = null; 308 TestProxyFileDescriptorCallback(int size, String seed)309 TestProxyFileDescriptorCallback(int size, String seed) { 310 final byte[] seedBytes = seed.getBytes(); 311 bytes = new byte[size]; 312 for (int i = 0; i < size; i++) { 313 bytes[i] = seedBytes[i % seedBytes.length]; 314 } 315 } 316 317 @Override onWrite(long offset, int size, byte[] data)318 public int onWrite(long offset, int size, byte[] data) throws ErrnoException { 319 if (onWriteError != null) { 320 throw onWriteError; 321 } 322 for (int i = 0; i < size; i++) { 323 bytes[(int)(i + offset)] = data[i]; 324 } 325 return size; 326 } 327 328 @Override onRead(long offset, int size, byte[] data)329 public int onRead(long offset, int size, byte[] data) throws ErrnoException { 330 if (onReadError != null) { 331 throw onReadError; 332 } 333 final int len = (int)(Math.min(size, bytes.length - offset)); 334 for (int i = 0; i < len; i++) { 335 data[i] = bytes[(int)(i + offset)]; 336 } 337 return len; 338 } 339 340 @Override onGetSize()341 public long onGetSize() throws ErrnoException { 342 if (onGetSizeError != null) { 343 throw onGetSizeError; 344 } 345 return bytes.length; 346 } 347 348 @Override onFsync()349 public void onFsync() throws ErrnoException { 350 if (onFsyncError != null) { 351 throw onFsyncError; 352 } 353 fsyncCount++; 354 } 355 356 @Override onRelease()357 public void onRelease() { 358 releaseCount++; 359 } 360 } 361 testOpenProxyFileDescriptor()362 public void testOpenProxyFileDescriptor() throws Exception { 363 final TestProxyFileDescriptorCallback appleCallback = 364 new TestProxyFileDescriptorCallback(1024 * 1024, "Apple"); 365 final TestProxyFileDescriptorCallback orangeCallback = 366 new TestProxyFileDescriptorCallback(1024 * 128, "Orange"); 367 final TestProxyFileDescriptorCallback cherryCallback = 368 new TestProxyFileDescriptorCallback(1024 * 1024, "Cherry"); 369 try (final ParcelFileDescriptor appleFd = mStorageManager.openProxyFileDescriptor( 370 ParcelFileDescriptor.MODE_READ_ONLY, appleCallback, mHandler); 371 final ParcelFileDescriptor orangeFd = mStorageManager.openProxyFileDescriptor( 372 ParcelFileDescriptor.MODE_WRITE_ONLY, orangeCallback, mHandler); 373 final ParcelFileDescriptor cherryFd = mStorageManager.openProxyFileDescriptor( 374 ParcelFileDescriptor.MODE_READ_WRITE, cherryCallback, mHandler)) { 375 // Stat 376 assertEquals(appleCallback.onGetSize(), appleFd.getStatSize()); 377 assertEquals(orangeCallback.onGetSize(), orangeFd.getStatSize()); 378 assertEquals(cherryCallback.onGetSize(), cherryFd.getStatSize()); 379 380 final byte[] bytes = new byte[100]; 381 382 // Read 383 for (int i = 0; i < 2; i++) { 384 Os.read(appleFd.getFileDescriptor(), bytes, 0, 100); 385 for (int j = 0; j < 100; j++) { 386 assertEquals(appleCallback.bytes[i * 100 + j], bytes[j]); 387 } 388 } 389 try { 390 Os.read(orangeFd.getFileDescriptor(), bytes, 0, 100); 391 fail(); 392 } catch (ErrnoException exp) { 393 assertEquals(OsConstants.EBADF, exp.errno); 394 } 395 for (int i = 0; i < 2; i++) { 396 Os.read(cherryFd.getFileDescriptor(), bytes, 0, 100); 397 for (int j = 0; j < 100; j++) { 398 assertEquals(cherryCallback.bytes[i * 100 + j], bytes[j]); 399 } 400 } 401 402 // Pread 403 Os.pread(appleFd.getFileDescriptor(), bytes, 0, 100, 500); 404 for (int j = 0; j < 100; j++) { 405 assertEquals(appleCallback.bytes[500 + j], bytes[j]); 406 } 407 try { 408 Os.pread(orangeFd.getFileDescriptor(), bytes, 0, 100, 500); 409 fail(); 410 } catch (ErrnoException exp) { 411 assertEquals(OsConstants.EBADF, exp.errno); 412 } 413 Os.pread(cherryFd.getFileDescriptor(), bytes, 0, 100, 500); 414 for (int j = 0; j < 100; j++) { 415 assertEquals(cherryCallback.bytes[500 + j], bytes[j]); 416 } 417 418 // Write 419 final byte[] writeSeed = "Strawberry".getBytes(); 420 for (int i = 0; i < bytes.length; i++) { 421 bytes[i] = writeSeed[i % writeSeed.length]; 422 } 423 try { 424 Os.write(appleFd.getFileDescriptor(), bytes, 0, 100); 425 fail(); 426 } catch (ErrnoException exp) { 427 assertEquals(OsConstants.EBADF, exp.errno); 428 } 429 for (int i = 0; i < 2; i++) { 430 Os.write(orangeFd.getFileDescriptor(), bytes, 0, 100); 431 for (int j = 0; j < 100; j++) { 432 assertEquals(bytes[j], orangeCallback.bytes[i * 100 + j]); 433 } 434 } 435 Os.lseek(cherryFd.getFileDescriptor(), 0, OsConstants.SEEK_SET); 436 for (int i = 0; i < 2; i++) { 437 Os.write(cherryFd.getFileDescriptor(), bytes, 0, 100); 438 for (int j = 0; j < 100; j++) { 439 assertEquals(bytes[j], cherryCallback.bytes[i * 100 + j]); 440 } 441 } 442 443 // Pwrite 444 try { 445 Os.pwrite(appleFd.getFileDescriptor(), bytes, 0, 100, 500); 446 fail(); 447 } catch (ErrnoException exp) { 448 assertEquals(OsConstants.EBADF, exp.errno); 449 } 450 Os.pwrite(orangeFd.getFileDescriptor(), bytes, 0, 100, 500); 451 for (int j = 0; j < 100; j++) { 452 assertEquals(bytes[j], orangeCallback.bytes[500 + j]); 453 } 454 Os.pwrite(cherryFd.getFileDescriptor(), bytes, 0, 100, 500); 455 for (int j = 0; j < 100; j++) { 456 assertEquals(bytes[j], cherryCallback.bytes[500 + j]); 457 } 458 459 // Flush 460 assertEquals(0, appleCallback.fsyncCount); 461 assertEquals(0, orangeCallback.fsyncCount); 462 assertEquals(0, cherryCallback.fsyncCount); 463 appleFd.getFileDescriptor().sync(); 464 orangeFd.getFileDescriptor().sync(); 465 cherryFd.getFileDescriptor().sync(); 466 assertEquals(1, appleCallback.fsyncCount); 467 assertEquals(1, orangeCallback.fsyncCount); 468 assertEquals(1, cherryCallback.fsyncCount); 469 470 // Before release 471 assertEquals(0, appleCallback.releaseCount); 472 assertEquals(0, orangeCallback.releaseCount); 473 assertEquals(0, cherryCallback.releaseCount); 474 } 475 476 // Release 477 int retry = 3; 478 while (true) { 479 try { 480 assertEquals(1, appleCallback.releaseCount); 481 assertEquals(1, orangeCallback.releaseCount); 482 assertEquals(1, cherryCallback.releaseCount); 483 break; 484 } catch (AssertionFailedError error) { 485 if (retry-- > 0) { 486 Thread.sleep(500); 487 continue; 488 } else { 489 throw error; 490 } 491 } 492 } 493 } 494 testOpenProxyFileDescriptor_error()495 public void testOpenProxyFileDescriptor_error() throws Exception { 496 final TestProxyFileDescriptorCallback callback = 497 new TestProxyFileDescriptorCallback(1024 * 1024, "Error"); 498 final byte[] bytes = new byte[128]; 499 callback.onGetSizeError = new ErrnoException("onGetSize", OsConstants.ENOENT); 500 try { 501 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 502 ParcelFileDescriptor.MODE_READ_WRITE, callback, mHandler)) {} 503 fail(); 504 } catch (IOException exp) {} 505 callback.onGetSizeError = null; 506 507 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 508 ParcelFileDescriptor.MODE_READ_WRITE, callback, mHandler)) { 509 callback.onReadError = new ErrnoException("onRead", OsConstants.EIO); 510 try { 511 Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length); 512 fail(); 513 } catch (ErrnoException error) {} 514 515 callback.onReadError = new ErrnoException("onRead", OsConstants.ENOSYS); 516 try { 517 Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length); 518 fail(); 519 } catch (ErrnoException error) {} 520 521 callback.onReadError = null; 522 Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length); 523 524 callback.onWriteError = new ErrnoException("onWrite", OsConstants.EIO); 525 try { 526 Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length); 527 fail(); 528 } catch (ErrnoException error) {} 529 530 callback.onWriteError = new ErrnoException("onWrite", OsConstants.ENOSYS); 531 try { 532 Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length); 533 fail(); 534 } catch (ErrnoException error) {} 535 536 callback.onWriteError = null; 537 Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length); 538 539 callback.onFsyncError = new ErrnoException("onFsync", OsConstants.EIO); 540 try { 541 fd.getFileDescriptor().sync(); 542 fail(); 543 } catch (SyncFailedException error) {} 544 545 callback.onFsyncError = new ErrnoException("onFsync", OsConstants.ENOSYS); 546 try { 547 fd.getFileDescriptor().sync(); 548 fail(); 549 } catch (SyncFailedException error) {} 550 551 callback.onFsyncError = null; 552 fd.getFileDescriptor().sync(); 553 } 554 } 555 testOpenProxyFileDescriptor_async()556 public void testOpenProxyFileDescriptor_async() throws Exception { 557 final CountDownLatch blockReadLatch = new CountDownLatch(1); 558 final CountDownLatch readBlockedLatch = new CountDownLatch(1); 559 final CountDownLatch releaseLatch = new CountDownLatch(2); 560 final TestProxyFileDescriptorCallback blockCallback = 561 new TestProxyFileDescriptorCallback(1024 * 1024, "Block") { 562 @Override 563 public int onRead(long offset, int size, byte[] data) throws ErrnoException { 564 try { 565 readBlockedLatch.countDown(); 566 blockReadLatch.await(); 567 } catch (InterruptedException e) { 568 fail(e.getMessage()); 569 } 570 return super.onRead(offset, size, data); 571 } 572 573 @Override 574 public void onRelease() { 575 releaseLatch.countDown(); 576 } 577 }; 578 final TestProxyFileDescriptorCallback asyncCallback = 579 new TestProxyFileDescriptorCallback(1024 * 1024, "Async") { 580 @Override 581 public void onRelease() { 582 releaseLatch.countDown(); 583 } 584 }; 585 final SynchronousQueue<Handler> handlerChannel = new SynchronousQueue<>(); 586 final Thread looperThread = new Thread(() -> { 587 Looper.prepare(); 588 try { 589 handlerChannel.put(new Handler()); 590 } catch (InterruptedException e) { 591 fail(e.getMessage()); 592 } 593 Looper.loop(); 594 }); 595 looperThread.start(); 596 597 final Handler handler = handlerChannel.take(); 598 599 try (final ParcelFileDescriptor blockFd = 600 mStorageManager.openProxyFileDescriptor( 601 ParcelFileDescriptor.MODE_READ_ONLY, blockCallback, mHandler); 602 final ParcelFileDescriptor asyncFd = 603 mStorageManager.openProxyFileDescriptor( 604 ParcelFileDescriptor.MODE_READ_ONLY, asyncCallback, handler)) { 605 final Thread readingThread = new Thread(() -> { 606 final byte[] bytes = new byte[128]; 607 try { 608 609 Os.read(blockFd.getFileDescriptor(), bytes, 0, 128); 610 } catch (ErrnoException | InterruptedIOException e) { 611 fail(e.getMessage()); 612 } 613 }); 614 readingThread.start(); 615 616 readBlockedLatch.countDown(); 617 assertEquals(Thread.State.RUNNABLE, readingThread.getState()); 618 619 final byte[] bytes = new byte[128]; 620 Log.d("StorageManagerTest", "start read async"); 621 assertEquals(128, Os.read(asyncFd.getFileDescriptor(), bytes, 0, 128)); 622 Log.d("StorageManagerTest", "stop read async"); 623 624 blockReadLatch.countDown(); 625 readingThread.join(); 626 } 627 628 releaseLatch.await(); 629 handler.getLooper().quit(); 630 looperThread.join(); 631 } 632 testOpenProxyFileDescriptor_largeFile()633 public void testOpenProxyFileDescriptor_largeFile() throws Exception { 634 final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() { 635 @Override 636 public int onRead(long offset, int size, byte[] data) throws ErrnoException { 637 for (int i = 0; i < size; i++) { 638 data[i] = 'L'; 639 } 640 return size; 641 } 642 643 @Override 644 public long onGetSize() throws ErrnoException { 645 return 8L * 1024L * 1024L * 1024L; // 8GB 646 } 647 648 @Override 649 public void onRelease() {} 650 }; 651 final byte[] bytes = new byte[128]; 652 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 653 ParcelFileDescriptor.MODE_READ_ONLY, callback, mHandler)) { 654 assertEquals(8L * 1024L * 1024L * 1024L, fd.getStatSize()); 655 656 final int readBytes = Os.pread( 657 fd.getFileDescriptor(), bytes, 0, bytes.length, fd.getStatSize() - 64L); 658 assertEquals(64, readBytes); 659 for (int i = 0; i < 64; i++) { 660 assertEquals('L', bytes[i]); 661 } 662 } 663 } 664 testOpenProxyFileDescriptor_largeRead()665 public void testOpenProxyFileDescriptor_largeRead() throws Exception { 666 final int SIZE = 1024 * 1024; 667 final TestProxyFileDescriptorCallback callback = 668 new TestProxyFileDescriptorCallback(SIZE, "abcdefghijklmnopqrstuvwxyz"); 669 final byte[] bytes = new byte[SIZE]; 670 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 671 ParcelFileDescriptor.MODE_READ_ONLY, callback, mHandler)) { 672 final int readBytes = Os.read( 673 fd.getFileDescriptor(), bytes, 0, bytes.length); 674 assertEquals(bytes.length, readBytes); 675 for (int i = 0; i < bytes.length; i++) { 676 assertEquals(callback.bytes[i], bytes[i]); 677 } 678 } 679 } 680 testOpenProxyFileDescriptor_largeWrite()681 public void testOpenProxyFileDescriptor_largeWrite() throws Exception { 682 final int SIZE = 1024 * 1024; 683 final TestProxyFileDescriptorCallback callback = 684 new TestProxyFileDescriptorCallback(SIZE, "abcdefghijklmnopqrstuvwxyz"); 685 final byte[] bytes = new byte[SIZE]; 686 for (int i = 0; i < SIZE; i++) { 687 bytes[i] = (byte)(i % 123); 688 } 689 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 690 ParcelFileDescriptor.MODE_WRITE_ONLY, callback, mHandler)) { 691 final int writtenBytes = Os.write( 692 fd.getFileDescriptor(), bytes, 0, bytes.length); 693 assertEquals(bytes.length, writtenBytes); 694 for (int i = 0; i < bytes.length; i++) { 695 assertEquals(bytes[i], callback.bytes[i]); 696 } 697 } 698 } 699 testIsAllocationSupported()700 public void testIsAllocationSupported() throws Exception { 701 FileDescriptor good = Os.open( 702 File.createTempFile("StorageManagerTest", "").getAbsolutePath(), 703 OsConstants.O_RDONLY, 0); 704 FileDescriptor bad = Os.open("/proc/self/cmdline", OsConstants.O_RDONLY, 0); 705 try { 706 assertTrue(mStorageManager.isAllocationSupported(good)); 707 assertFalse(mStorageManager.isAllocationSupported(bad)); 708 } finally { 709 try { 710 Os.close(good); 711 } catch (ErrnoException ignored) {} 712 713 try { 714 Os.close(bad); 715 } catch (ErrnoException ignored) {} 716 } 717 } 718 assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone)719 private void assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone) 720 throws Exception { 721 // Asserts equals() method. 722 assertEquals("StorageVolume.equals() mismatch", volume, clone); 723 // Make sure all fields match. 724 for (Field field : StorageVolume.class.getDeclaredFields()) { 725 if (Modifier.isStatic(field.getModifiers())) continue; 726 field.setAccessible(true); 727 final Object originalValue = field.get(volume); 728 final Object clonedValue = field.get(clone); 729 assertEquals("Mismatch for field " + field.getName(), originalValue, clonedValue); 730 } 731 } 732 assertStartsWith(String message, String prefix, String actual)733 private static void assertStartsWith(String message, String prefix, String actual) { 734 if (!actual.startsWith(prefix)) { 735 throw new ComparisonFailure(message, prefix, actual); 736 } 737 } 738 assertFileContains(File file, String contents)739 private static void assertFileContains(File file, String contents) throws IOException { 740 byte[] actual = readFully(new FileInputStream(file)); 741 byte[] expected = contents.getBytes("UTF-8"); 742 assertEquals("unexpected size", expected.length, actual.length); 743 for (int i = 0; i < expected.length; i++) { 744 assertEquals("unexpected value at offset " + i, expected[i], actual[i]); 745 } 746 } 747 748 private static class ObbObserver extends OnObbStateChangeListener { 749 private String path; 750 751 public int state = -1; 752 boolean done = false; 753 754 @Override onObbStateChange(String path, int state)755 public void onObbStateChange(String path, int state) { 756 Log.d(TAG, "Received message. path=" + path + ", state=" + state); 757 synchronized (this) { 758 this.path = path; 759 this.state = state; 760 done = true; 761 notifyAll(); 762 } 763 } 764 getPath()765 public String getPath() { 766 assertTrue("Expected ObbObserver to have received a state change.", done); 767 return path; 768 } 769 getState()770 public int getState() { 771 assertTrue("Expected ObbObserver to have received a state change.", done); 772 return state; 773 } 774 isDone()775 public boolean isDone() { 776 return done; 777 } 778 waitForCompletion()779 public boolean waitForCompletion() { 780 long waitTime = 0; 781 synchronized (this) { 782 while (!isDone() && waitTime < MAX_WAIT_TIME) { 783 try { 784 wait(WAIT_TIME_INCR); 785 waitTime += WAIT_TIME_INCR; 786 } catch (InterruptedException e) { 787 Log.i(TAG, "Interrupted during sleep", e); 788 } 789 } 790 } 791 792 return isDone(); 793 } 794 } 795 getTargetFiles()796 private List<File> getTargetFiles() { 797 final List<File> targets = new ArrayList<File>(); 798 for (File dir : mContext.getObbDirs()) { 799 assertNotNull("Valid media must be inserted during CTS", dir); 800 assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED, 801 Environment.getStorageState(dir)); 802 targets.add(dir); 803 } 804 return targets; 805 } 806 copyRawToFile(int rawResId, File outFile)807 private void copyRawToFile(int rawResId, File outFile) { 808 Resources res = mContext.getResources(); 809 InputStream is = null; 810 try { 811 is = res.openRawResource(rawResId); 812 } catch (NotFoundException e) { 813 fail("Failed to load resource with id: " + rawResId); 814 } 815 assertTrue(FileUtils.copyToFile(is, outFile)); 816 exposeFile(outFile); 817 } 818 exposeFile(File file)819 private File exposeFile(File file) { 820 file.setReadable(true, false); 821 file.setReadable(true, true); 822 823 File dir = file.getParentFile(); 824 do { 825 dir.setExecutable(true, false); 826 dir.setExecutable(true, true); 827 dir = dir.getParentFile(); 828 } while (dir != null); 829 830 return file; 831 } 832 mountObb(final int resource, final File file, int expectedState)833 private String mountObb(final int resource, final File file, int expectedState) { 834 copyRawToFile(resource, file); 835 836 final ObbObserver observer = new ObbObserver(); 837 assertTrue("mountObb call on " + file.getPath() + " should succeed", 838 mStorageManager.mountObb(file.getPath(), null, observer)); 839 840 assertTrue("Mount should have completed", 841 observer.waitForCompletion()); 842 843 if (expectedState == OnObbStateChangeListener.MOUNTED) { 844 assertTrue("OBB should be mounted", mStorageManager.isObbMounted(observer.getPath())); 845 } 846 847 assertEquals(expectedState, observer.getState()); 848 849 return observer.getPath(); 850 } 851 mountObbWithoutWait(final int resource, final File file)852 private ObbObserver mountObbWithoutWait(final int resource, final File file) { 853 copyRawToFile(resource, file); 854 855 final ObbObserver observer = new ObbObserver(); 856 assertTrue("mountObb call on " + file.getPath() + " should succeed", 857 mStorageManager.mountObb(file.getPath(), null, observer)); 858 859 return observer; 860 } 861 waitForObbActionCompletion(final File file, final ObbObserver observer, int expectedState)862 private void waitForObbActionCompletion(final File file, final ObbObserver observer, 863 int expectedState) { 864 assertTrue("Mount should have completed", observer.waitForCompletion()); 865 866 assertTrue("OBB should be mounted", mStorageManager.isObbMounted(observer.getPath())); 867 868 assertEquals(expectedState, observer.getState()); 869 } 870 checkMountedPath(final String path)871 private String checkMountedPath(final String path) { 872 final String mountPath = mStorageManager.getMountedObbPath(path); 873 assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX, 874 OBB_MOUNT_PREFIX, 875 mountPath); 876 return mountPath; 877 } 878 unmountObb(final File file, int expectedState)879 private void unmountObb(final File file, int expectedState) { 880 final ObbObserver observer = new ObbObserver(); 881 882 assertTrue("unmountObb call on test1_new.obb should succeed", 883 mStorageManager.unmountObb(file.getPath(), false, observer)); 884 885 assertTrue("Unmount should have completed", 886 observer.waitForCompletion()); 887 888 assertEquals(expectedState, observer.getState()); 889 890 if (expectedState == OnObbStateChangeListener.UNMOUNTED) { 891 assertFalse("OBB should not be mounted", mStorageManager.isObbMounted(file.getPath())); 892 } 893 } 894 readFully(InputStream in)895 public static byte[] readFully(InputStream in) throws IOException { 896 // Shamelessly borrowed from libcore.io.Streams 897 try { 898 return readFullyNoClose(in); 899 } finally { 900 in.close(); 901 } 902 } 903 readFullyNoClose(InputStream in)904 public static byte[] readFullyNoClose(InputStream in) throws IOException { 905 // Shamelessly borrowed from libcore.io.Streams 906 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 907 byte[] buffer = new byte[1024]; 908 int count; 909 while ((count = in.read(buffer)) != -1) { 910 bytes.write(buffer, 0, count); 911 } 912 return bytes.toByteArray(); 913 } 914 } 915