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 static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assert.fail; 27 import static org.junit.Assume.assumeTrue; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.when; 30 import static org.testng.Assert.assertThrows; 31 32 import static java.util.stream.Collectors.joining; 33 34 import android.app.PendingIntent; 35 import android.content.Context; 36 import android.content.res.Resources; 37 import android.content.res.Resources.NotFoundException; 38 import android.os.Environment; 39 import android.os.Handler; 40 import android.os.Looper; 41 import android.os.Parcel; 42 import android.os.ParcelFileDescriptor; 43 import android.os.Process; 44 import android.os.ProxyFileDescriptorCallback; 45 import android.os.UserHandle; 46 import android.os.cts.R; 47 import android.os.storage.OnObbStateChangeListener; 48 import android.os.storage.StorageManager; 49 import android.os.storage.StorageManager.StorageVolumeCallback; 50 import android.os.storage.StorageVolume; 51 import android.platform.test.annotations.AppModeFull; 52 import android.provider.DeviceConfig; 53 import android.system.ErrnoException; 54 import android.system.Os; 55 import android.system.OsConstants; 56 import android.test.ComparisonFailure; 57 import android.util.Log; 58 59 import androidx.test.ext.junit.runners.AndroidJUnit4; 60 import androidx.test.platform.app.InstrumentationRegistry; 61 62 import com.android.compatibility.common.util.FileUtils; 63 import com.android.compatibility.common.util.SystemUtil; 64 65 import org.junit.Before; 66 import org.junit.FixMethodOrder; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 import org.junit.runners.MethodSorters; 70 71 import java.io.ByteArrayOutputStream; 72 import java.io.File; 73 import java.io.FileDescriptor; 74 import java.io.FileInputStream; 75 import java.io.IOException; 76 import java.io.InputStream; 77 import java.io.InterruptedIOException; 78 import java.io.SyncFailedException; 79 import java.lang.reflect.Field; 80 import java.lang.reflect.Modifier; 81 import java.util.ArrayList; 82 import java.util.List; 83 import java.util.Optional; 84 import java.util.Set; 85 import java.util.UUID; 86 import java.util.concurrent.CountDownLatch; 87 import java.util.concurrent.SynchronousQueue; 88 import java.util.concurrent.TimeUnit; 89 import java.util.stream.Collectors; 90 91 // The FixMethodOrder annotation is added to avoid flakiness caused by a change in test execution 92 // order (b/273874071). 93 // TODO(b/278069249): Investigate why the order matters, and possibly remove the annotation. 94 @RunWith(AndroidJUnit4.class) 95 @FixMethodOrder(MethodSorters.NAME_ASCENDING) 96 public class StorageManagerTest { 97 98 private static final String TAG = StorageManager.class.getSimpleName(); 99 100 private static final long MAX_WAIT_TIME = 25*1000; 101 private static final long WAIT_TIME_INCR = 5*1000; 102 103 private static final String OBB_MOUNT_PREFIX = "/mnt/obb/"; 104 private static final String TEST1_NEW_CONTENTS = "1\n"; 105 106 private Context mContext; 107 private StorageManager mStorageManager; 108 private final Handler mHandler = new Handler(Looper.getMainLooper()); 109 110 @Before setUp()111 public void setUp() throws Exception { 112 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 113 mStorageManager = mContext.getSystemService(StorageManager.class); 114 } 115 116 @Test 117 @AppModeFull(reason = "Instant apps cannot access external storage") testMountAndUnmountObbNormal()118 public void testMountAndUnmountObbNormal() throws IOException { 119 for (File target : getTargetFiles()) { 120 target = new File(target, "test1_new.obb"); 121 Log.d(TAG, "Testing path " + target); 122 doMountAndUnmountObbNormal(target); 123 } 124 } 125 doMountAndUnmountObbNormal(File outFile)126 private void doMountAndUnmountObbNormal(File outFile) throws IOException { 127 final String canonPath = mountObb(R.raw.test1_new, outFile, OnObbStateChangeListener.MOUNTED); 128 129 mountObb(R.raw.test1_new, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 130 131 try { 132 final String mountPath = checkMountedPath(canonPath); 133 final File mountDir = new File(mountPath); 134 final File testFile = new File(mountDir, "test1.txt"); 135 136 assertTrue("OBB mounted path should be a directory", mountDir.isDirectory()); 137 assertTrue("test1.txt does not exist in OBB dir", testFile.exists()); 138 assertFileContains(testFile, TEST1_NEW_CONTENTS); 139 } finally { 140 unmountObb(outFile, OnObbStateChangeListener.UNMOUNTED); 141 } 142 } 143 144 @Test 145 @AppModeFull(reason = "Instant apps cannot access external storage") testAttemptMountNonObb()146 public void testAttemptMountNonObb() { 147 for (File target : getTargetFiles()) { 148 target = new File(target, "test1_nosig.obb"); 149 Log.d(TAG, "Testing path " + target); 150 doAttemptMountNonObb(target); 151 } 152 } 153 doAttemptMountNonObb(File outFile)154 private void doAttemptMountNonObb(File outFile) { 155 try { 156 mountObb(R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL); 157 fail("mountObb should've failed with an exception"); 158 } catch (IllegalArgumentException e) { 159 // Expected 160 } 161 162 assertFalse("OBB should not be mounted", 163 mStorageManager.isObbMounted(outFile.getPath())); 164 165 assertNull("OBB's mounted path should be null", 166 mStorageManager.getMountedObbPath(outFile.getPath())); 167 } 168 169 @Test 170 @AppModeFull(reason = "Instant apps cannot access external storage") testAttemptMountObbWrongPackage()171 public void testAttemptMountObbWrongPackage() { 172 for (File target : getTargetFiles()) { 173 target = new File(target, "test1_wrongpackage.obb"); 174 Log.d(TAG, "Testing path " + target); 175 doAttemptMountObbWrongPackage(target); 176 } 177 } 178 doAttemptMountObbWrongPackage(File outFile)179 private void doAttemptMountObbWrongPackage(File outFile) { 180 mountObb(R.raw.test1_wrongpackage, outFile, 181 OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 182 183 assertFalse("OBB should not be mounted", 184 mStorageManager.isObbMounted(outFile.getPath())); 185 186 assertNull("OBB's mounted path should be null", 187 mStorageManager.getMountedObbPath(outFile.getPath())); 188 } 189 190 @Test 191 @AppModeFull(reason = "Instant apps cannot access external storage") testMountAndUnmountTwoObbs()192 public void testMountAndUnmountTwoObbs() throws IOException { 193 for (File target : getTargetFiles()) { 194 Log.d(TAG, "Testing target " + target); 195 final File test1 = new File(target, "test1.obb"); 196 final File test2 = new File(target, "test2.obb"); 197 doMountAndUnmountTwoObbs(test1, test2); 198 } 199 } 200 doMountAndUnmountTwoObbs(File file1, File file2)201 private void doMountAndUnmountTwoObbs(File file1, File file2) throws IOException { 202 ObbObserver oo1 = mountObbWithoutWait(R.raw.test1_new, file1); 203 ObbObserver oo2 = mountObbWithoutWait(R.raw.test1_new, file2); 204 205 Log.d(TAG, "Waiting for OBB #1 to complete mount"); 206 waitForObbActionCompletion(file1, oo1, OnObbStateChangeListener.MOUNTED); 207 Log.d(TAG, "Waiting for OBB #2 to complete mount"); 208 waitForObbActionCompletion(file2, oo2, OnObbStateChangeListener.MOUNTED); 209 210 try { 211 final String mountPath1 = checkMountedPath(oo1.getPath()); 212 final File mountDir1 = new File(mountPath1); 213 final File testFile1 = new File(mountDir1, "test1.txt"); 214 assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory()); 215 assertTrue("test1.txt does not exist in OBB dir", testFile1.exists()); 216 assertFileContains(testFile1, TEST1_NEW_CONTENTS); 217 218 final String mountPath2 = checkMountedPath(oo2.getPath()); 219 final File mountDir2 = new File(mountPath2); 220 final File testFile2 = new File(mountDir2, "test1.txt"); 221 assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory()); 222 assertTrue("test1.txt does not exist in OBB dir", testFile2.exists()); 223 assertFileContains(testFile2, TEST1_NEW_CONTENTS); 224 } finally { 225 unmountObb(file1, OnObbStateChangeListener.UNMOUNTED); 226 unmountObb(file2, OnObbStateChangeListener.UNMOUNTED); 227 } 228 } 229 230 @Test testGetPrimaryVolume()231 public void testGetPrimaryVolume() throws Exception { 232 final StorageVolume volume = mStorageManager.getPrimaryStorageVolume(); 233 assertNotNull("Did not get primary storage", volume); 234 235 // Tests some basic Scoped Directory Access requests. 236 assertNull("Should not grant access for root directory", volume.createAccessIntent(null)); 237 assertNull("Should not grant access for invalid directory", 238 volume.createAccessIntent("/system")); 239 assertNotNull("Should grant access for valid directory " + Environment.DIRECTORY_DOCUMENTS, 240 volume.createAccessIntent(Environment.DIRECTORY_DOCUMENTS)); 241 242 // Tests basic properties. 243 assertNotNull("Should have description", volume.getDescription(mContext)); 244 assertTrue("Should be primary", volume.isPrimary()); 245 assertEquals("Wrong state", Environment.MEDIA_MOUNTED, volume.getState()); 246 247 // Tests properties that depend on storage type (emulated or physical) 248 final String fsUuid = volume.getUuid(); 249 final UUID uuid = volume.getStorageUuid(); 250 final boolean removable = volume.isRemovable(); 251 final boolean emulated = volume.isEmulated(); 252 if (emulated) { 253 assertFalse("Should not be externally managed", volume.isExternallyManaged()); 254 assertFalse("Should not be removable", removable); 255 assertNull("Should not have fsUuid", fsUuid); 256 assertEquals("Should have uuid_default", StorageManager.UUID_DEFAULT, uuid); 257 } else { 258 assertTrue("Should be removable", removable); 259 assertNotNull("Should have fsUuid", fsUuid); 260 assertNull("Should not have uuid", uuid); 261 } 262 263 // Tests path - although it's not a public API, sm.getPrimaryStorageVolume() 264 // explicitly states it should match Environment#getExternalStorageDirectory 265 final String path = volume.getPath(); 266 assertEquals("Path does not match Environment's", 267 Environment.getExternalStorageDirectory().getAbsolutePath(), path); 268 269 // Tests Parcelable contract. 270 assertEquals("Wrong describeContents", 0, volume.describeContents()); 271 final Parcel parcel = Parcel.obtain(); 272 try { 273 volume.writeToParcel(parcel, 0); 274 parcel.setDataPosition(0); 275 final StorageVolume clone = StorageVolume.CREATOR.createFromParcel(parcel); 276 assertStorageVolumesEquals(volume, clone); 277 } finally { 278 parcel.recycle(); 279 } 280 } 281 282 @Test 283 @AppModeFull(reason = "Instant apps cannot access external storage") testGetStorageVolumes()284 public void testGetStorageVolumes() throws Exception { 285 final List<StorageVolume> volumes = mStorageManager.getStorageVolumes(); 286 assertFalse("No volume return", volumes.isEmpty()); 287 StorageVolume primary = null; 288 for (StorageVolume volume : volumes) { 289 if (volume.isPrimary()) { 290 assertNull("There can be only one primary volume: " + volumes, primary); 291 primary = volume; 292 } 293 } 294 assertNotNull("No primary volume on " + volumes, primary); 295 assertStorageVolumesEquals(primary, mStorageManager.getPrimaryStorageVolume()); 296 } 297 298 @Test 299 @AppModeFull(reason = "Instant apps cannot access external storage") testGetRecentStorageVolumes()300 public void testGetRecentStorageVolumes() throws Exception { 301 // At a minimum recent volumes should include current volumes 302 final Set<String> currentNames = mStorageManager.getStorageVolumes().stream() 303 .map((v) -> v.getMediaStoreVolumeName()).collect(Collectors.toSet()); 304 final Set<String> recentNames = mStorageManager.getRecentStorageVolumes().stream() 305 .map((v) -> v.getMediaStoreVolumeName()).collect(Collectors.toSet()); 306 assertTrue(recentNames.containsAll(currentNames)); 307 } 308 309 @Test 310 @AppModeFull(reason = "Instant apps cannot access external storage") testGetStorageVolume()311 public void testGetStorageVolume() throws Exception { 312 assertNull("Should not get volume for null path", 313 mStorageManager.getStorageVolume((File) null)); 314 assertNull("Should not get volume for invalid path", 315 mStorageManager.getStorageVolume(new File("/system"))); 316 assertNull("Should not get volume for storage directory", 317 mStorageManager.getStorageVolume(Environment.getStorageDirectory())); 318 319 final File root = Environment.getExternalStorageDirectory(); 320 Log.d("StorageManagerTest", "root: " + root); 321 final StorageVolume primary = mStorageManager.getPrimaryStorageVolume(); 322 final StorageVolume rootVolume = mStorageManager.getStorageVolume(root); 323 assertNotNull("No volume for root (" + root + ")", rootVolume); 324 assertStorageVolumesEquals(primary, rootVolume); 325 326 final File child = new File(root, "child"); 327 StorageVolume childVolume = mStorageManager.getStorageVolume(child); 328 assertNotNull("No volume for child (" + child + ")", childVolume); 329 Log.d("StorageManagerTest", "child: " + childVolume.getPath()); 330 assertStorageVolumesEquals(primary, childVolume); 331 } 332 333 @Test 334 @AppModeFull(reason = "Instant apps cannot access external storage") testGetStorageVolumeUSB()335 public void testGetStorageVolumeUSB() throws Exception { 336 assumeTrue(StorageManagerHelper.isAdoptableStorageSupported(mContext)); 337 338 String volumeName = StorageManagerHelper.createUSBVirtualDisk(); 339 Log.d(TAG, "testGetStorageVolumeUSB#volumeName: " + volumeName); 340 List<StorageVolume> storageVolumes = mStorageManager.getStorageVolumes(); 341 Optional<StorageVolume> usbStorageVolume = 342 storageVolumes.stream().filter(sv-> 343 sv != null && sv.getPath() != null && sv.getPath().contains(volumeName) 344 ).findFirst(); 345 assertTrue("The USB storage volume mounted on the main user is not present in " 346 + storageVolumes.stream().map(StorageVolume::getPath) 347 .collect(joining("\n")), usbStorageVolume.isPresent()); 348 } 349 350 @Test 351 @AppModeFull(reason = "Instant apps cannot access external storage") testGetStorageVolumeSDCard()352 public void testGetStorageVolumeSDCard() throws Exception { 353 assumeTrue(StorageManagerHelper.isAdoptableStorageSupported(mContext)); 354 355 String volumeName = StorageManagerHelper.createSDCardVirtualDisk(); 356 Log.d(TAG, "testGetStorageVolumeSDCard#volumeName: " + volumeName); 357 List<StorageVolume> storageVolumes = mStorageManager.getStorageVolumes(); 358 Optional<StorageVolume> sdCardStorageVolume = 359 storageVolumes.stream().filter(sv-> 360 sv != null && sv.getPath() != null && sv.getPath().contains(volumeName) 361 ).findFirst(); 362 assertTrue("The SdCard storage volume mounted on the main user is not present in " 363 + storageVolumes.stream().map(StorageVolume::getPath) 364 .collect(joining("\n")), sdCardStorageVolume.isPresent()); 365 } 366 assertNoUuid(File file)367 private void assertNoUuid(File file) { 368 try { 369 final UUID uuid = mStorageManager.getUuidForPath(file); 370 fail("Unexpected UUID " + uuid + " for " + file); 371 } catch (IOException expected) { 372 } 373 } 374 375 @Test testGetUuidForPath()376 public void testGetUuidForPath() throws Exception { 377 assertEquals(StorageManager.UUID_DEFAULT, 378 mStorageManager.getUuidForPath(Environment.getDataDirectory())); 379 assertEquals(StorageManager.UUID_DEFAULT, 380 mStorageManager.getUuidForPath(mContext.getDataDir())); 381 382 assertNoUuid(new File("/")); 383 assertNoUuid(new File("/proc/")); 384 } 385 386 @Test 387 @AppModeFull(reason = "Instant apps cannot access external storage") testGetExternalUuidForPath()388 public void testGetExternalUuidForPath() throws Exception { 389 final UUID extUuid = mStorageManager 390 .getUuidForPath(Environment.getExternalStorageDirectory()); 391 if (Environment.isExternalStorageEmulated()) { 392 assertEquals(StorageManager.UUID_DEFAULT, extUuid); 393 } 394 395 assertEquals(extUuid, mStorageManager.getUuidForPath(mContext.getExternalCacheDir())); 396 assertEquals(extUuid, mStorageManager.getUuidForPath(new File("/sdcard/"))); 397 } 398 399 @Test 400 @AppModeFull(reason = "Instant apps cannot access external storage") testCallback()401 public void testCallback() throws Exception { 402 final CountDownLatch mounted = new CountDownLatch(1); 403 final CountDownLatch unmounted = new CountDownLatch(1); 404 final StorageVolumeCallback callback = new StorageVolumeCallback() { 405 @Override 406 public void onStateChanged(StorageVolume volume) { 407 switch (volume.getState()) { 408 case Environment.MEDIA_MOUNTED: mounted.countDown(); break; 409 case Environment.MEDIA_UNMOUNTED: unmounted.countDown(); break; 410 } 411 } 412 }; 413 414 // Unmount storage data and obb dirs before test so test process won't be killed. 415 InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand( 416 "sm unmount-app-data-dirs " + mContext.getPackageName() + " " 417 + Process.myPid() + " " + UserHandle.myUserId()); 418 419 // Unmount primary storage, verify we can see it take effect 420 mStorageManager.registerStorageVolumeCallback(mContext.getMainExecutor(), callback); 421 InstrumentationRegistry.getInstrumentation().getUiAutomation() 422 .executeShellCommand("sm unmount emulated;" + UserHandle.myUserId()); 423 assertTrue(unmounted.await(30, TimeUnit.SECONDS)); 424 425 // Now unregister and verify we don't hear future events 426 mStorageManager.unregisterStorageVolumeCallback(callback); 427 InstrumentationRegistry.getInstrumentation().getUiAutomation() 428 .executeShellCommand("sm mount emulated;" + UserHandle.myUserId()); 429 assertFalse(mounted.await(15, TimeUnit.SECONDS)); 430 } 431 432 private static class TestProxyFileDescriptorCallback extends ProxyFileDescriptorCallback { 433 final byte[] bytes; 434 int fsyncCount; 435 int releaseCount; 436 ErrnoException onGetSizeError = null; 437 ErrnoException onReadError = null; 438 ErrnoException onWriteError = null; 439 ErrnoException onFsyncError = null; 440 TestProxyFileDescriptorCallback(int size, String seed)441 TestProxyFileDescriptorCallback(int size, String seed) { 442 final byte[] seedBytes = seed.getBytes(); 443 bytes = new byte[size]; 444 for (int i = 0; i < size; i++) { 445 bytes[i] = seedBytes[i % seedBytes.length]; 446 } 447 } 448 449 @Override onWrite(long offset, int size, byte[] data)450 public int onWrite(long offset, int size, byte[] data) throws ErrnoException { 451 if (onWriteError != null) { 452 throw onWriteError; 453 } 454 for (int i = 0; i < size; i++) { 455 bytes[(int)(i + offset)] = data[i]; 456 } 457 return size; 458 } 459 460 @Override onRead(long offset, int size, byte[] data)461 public int onRead(long offset, int size, byte[] data) throws ErrnoException { 462 if (onReadError != null) { 463 throw onReadError; 464 } 465 final int len = (int)(Math.min(size, bytes.length - offset)); 466 for (int i = 0; i < len; i++) { 467 data[i] = bytes[(int)(i + offset)]; 468 } 469 return len; 470 } 471 472 @Override onGetSize()473 public long onGetSize() throws ErrnoException { 474 if (onGetSizeError != null) { 475 throw onGetSizeError; 476 } 477 return bytes.length; 478 } 479 480 @Override onFsync()481 public void onFsync() throws ErrnoException { 482 if (onFsyncError != null) { 483 throw onFsyncError; 484 } 485 fsyncCount++; 486 } 487 488 @Override onRelease()489 public void onRelease() { 490 releaseCount++; 491 } 492 } 493 494 @Test testOpenProxyFileDescriptor()495 public void testOpenProxyFileDescriptor() throws Exception { 496 final TestProxyFileDescriptorCallback appleCallback = 497 new TestProxyFileDescriptorCallback(1024 * 1024, "Apple"); 498 final TestProxyFileDescriptorCallback orangeCallback = 499 new TestProxyFileDescriptorCallback(1024 * 128, "Orange"); 500 final TestProxyFileDescriptorCallback cherryCallback = 501 new TestProxyFileDescriptorCallback(1024 * 1024, "Cherry"); 502 try (final ParcelFileDescriptor appleFd = mStorageManager.openProxyFileDescriptor( 503 ParcelFileDescriptor.MODE_READ_ONLY, appleCallback, mHandler); 504 final ParcelFileDescriptor orangeFd = mStorageManager.openProxyFileDescriptor( 505 ParcelFileDescriptor.MODE_WRITE_ONLY, orangeCallback, mHandler); 506 final ParcelFileDescriptor cherryFd = mStorageManager.openProxyFileDescriptor( 507 ParcelFileDescriptor.MODE_READ_WRITE, cherryCallback, mHandler)) { 508 // Stat 509 assertEquals(appleCallback.onGetSize(), appleFd.getStatSize()); 510 assertEquals(orangeCallback.onGetSize(), orangeFd.getStatSize()); 511 assertEquals(cherryCallback.onGetSize(), cherryFd.getStatSize()); 512 513 final byte[] bytes = new byte[100]; 514 515 // Read 516 for (int i = 0; i < 2; i++) { 517 Os.read(appleFd.getFileDescriptor(), bytes, 0, 100); 518 for (int j = 0; j < 100; j++) { 519 assertEquals(appleCallback.bytes[i * 100 + j], bytes[j]); 520 } 521 } 522 try { 523 Os.read(orangeFd.getFileDescriptor(), bytes, 0, 100); 524 fail(); 525 } catch (ErrnoException exp) { 526 assertEquals(OsConstants.EBADF, exp.errno); 527 } 528 for (int i = 0; i < 2; i++) { 529 Os.read(cherryFd.getFileDescriptor(), bytes, 0, 100); 530 for (int j = 0; j < 100; j++) { 531 assertEquals(cherryCallback.bytes[i * 100 + j], bytes[j]); 532 } 533 } 534 535 // Pread 536 Os.pread(appleFd.getFileDescriptor(), bytes, 0, 100, 500); 537 for (int j = 0; j < 100; j++) { 538 assertEquals(appleCallback.bytes[500 + j], bytes[j]); 539 } 540 try { 541 Os.pread(orangeFd.getFileDescriptor(), bytes, 0, 100, 500); 542 fail(); 543 } catch (ErrnoException exp) { 544 assertEquals(OsConstants.EBADF, exp.errno); 545 } 546 Os.pread(cherryFd.getFileDescriptor(), bytes, 0, 100, 500); 547 for (int j = 0; j < 100; j++) { 548 assertEquals(cherryCallback.bytes[500 + j], bytes[j]); 549 } 550 551 // Write 552 final byte[] writeSeed = "Strawberry".getBytes(); 553 for (int i = 0; i < bytes.length; i++) { 554 bytes[i] = writeSeed[i % writeSeed.length]; 555 } 556 try { 557 Os.write(appleFd.getFileDescriptor(), bytes, 0, 100); 558 fail(); 559 } catch (ErrnoException exp) { 560 assertEquals(OsConstants.EBADF, exp.errno); 561 } 562 for (int i = 0; i < 2; i++) { 563 Os.write(orangeFd.getFileDescriptor(), bytes, 0, 100); 564 for (int j = 0; j < 100; j++) { 565 assertEquals(bytes[j], orangeCallback.bytes[i * 100 + j]); 566 } 567 } 568 Os.lseek(cherryFd.getFileDescriptor(), 0, OsConstants.SEEK_SET); 569 for (int i = 0; i < 2; i++) { 570 Os.write(cherryFd.getFileDescriptor(), bytes, 0, 100); 571 for (int j = 0; j < 100; j++) { 572 assertEquals(bytes[j], cherryCallback.bytes[i * 100 + j]); 573 } 574 } 575 576 // Pwrite 577 try { 578 Os.pwrite(appleFd.getFileDescriptor(), bytes, 0, 100, 500); 579 fail(); 580 } catch (ErrnoException exp) { 581 assertEquals(OsConstants.EBADF, exp.errno); 582 } 583 Os.pwrite(orangeFd.getFileDescriptor(), bytes, 0, 100, 500); 584 for (int j = 0; j < 100; j++) { 585 assertEquals(bytes[j], orangeCallback.bytes[500 + j]); 586 } 587 Os.pwrite(cherryFd.getFileDescriptor(), bytes, 0, 100, 500); 588 for (int j = 0; j < 100; j++) { 589 assertEquals(bytes[j], cherryCallback.bytes[500 + j]); 590 } 591 592 // Flush 593 assertEquals(0, appleCallback.fsyncCount); 594 assertEquals(0, orangeCallback.fsyncCount); 595 assertEquals(0, cherryCallback.fsyncCount); 596 appleFd.getFileDescriptor().sync(); 597 orangeFd.getFileDescriptor().sync(); 598 cherryFd.getFileDescriptor().sync(); 599 assertEquals(1, appleCallback.fsyncCount); 600 assertEquals(1, orangeCallback.fsyncCount); 601 assertEquals(1, cherryCallback.fsyncCount); 602 603 // Before release 604 assertEquals(0, appleCallback.releaseCount); 605 assertEquals(0, orangeCallback.releaseCount); 606 assertEquals(0, cherryCallback.releaseCount); 607 } 608 609 // Release 610 int retry = 3; 611 while (true) { 612 try { 613 assertEquals(1, appleCallback.releaseCount); 614 assertEquals(1, orangeCallback.releaseCount); 615 assertEquals(1, cherryCallback.releaseCount); 616 break; 617 } catch (AssertionError error) { 618 if (retry-- > 0) { 619 Thread.sleep(500); 620 continue; 621 } else { 622 throw error; 623 } 624 } 625 } 626 } 627 628 @Test testOpenProxyFileDescriptor_error()629 public void testOpenProxyFileDescriptor_error() throws Exception { 630 final TestProxyFileDescriptorCallback callback = 631 new TestProxyFileDescriptorCallback(1024 * 1024, "Error"); 632 final byte[] bytes = new byte[128]; 633 callback.onGetSizeError = new ErrnoException("onGetSize", OsConstants.ENOENT); 634 try { 635 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 636 ParcelFileDescriptor.MODE_READ_WRITE, callback, mHandler)) {} 637 fail(); 638 } catch (IOException exp) {} 639 callback.onGetSizeError = null; 640 641 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 642 ParcelFileDescriptor.MODE_READ_WRITE, callback, mHandler)) { 643 callback.onReadError = new ErrnoException("onRead", OsConstants.EIO); 644 try { 645 Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length); 646 fail(); 647 } catch (ErrnoException error) {} 648 649 callback.onReadError = new ErrnoException("onRead", OsConstants.ENOSYS); 650 try { 651 Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length); 652 fail(); 653 } catch (ErrnoException error) {} 654 655 callback.onReadError = null; 656 Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length); 657 658 callback.onWriteError = new ErrnoException("onWrite", OsConstants.EIO); 659 try { 660 Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length); 661 fail(); 662 } catch (ErrnoException error) {} 663 664 callback.onWriteError = new ErrnoException("onWrite", OsConstants.ENOSYS); 665 try { 666 Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length); 667 fail(); 668 } catch (ErrnoException error) {} 669 670 callback.onWriteError = null; 671 Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length); 672 673 callback.onFsyncError = new ErrnoException("onFsync", OsConstants.EIO); 674 try { 675 fd.getFileDescriptor().sync(); 676 fail(); 677 } catch (SyncFailedException error) {} 678 679 callback.onFsyncError = new ErrnoException("onFsync", OsConstants.ENOSYS); 680 try { 681 fd.getFileDescriptor().sync(); 682 fail(); 683 } catch (SyncFailedException error) {} 684 685 callback.onFsyncError = null; 686 fd.getFileDescriptor().sync(); 687 } 688 } 689 690 @Test testOpenProxyFileDescriptor_async()691 public void testOpenProxyFileDescriptor_async() throws Exception { 692 final CountDownLatch blockReadLatch = new CountDownLatch(1); 693 final CountDownLatch readBlockedLatch = new CountDownLatch(1); 694 final CountDownLatch releaseLatch = new CountDownLatch(2); 695 final TestProxyFileDescriptorCallback blockCallback = 696 new TestProxyFileDescriptorCallback(1024 * 1024, "Block") { 697 @Override 698 public int onRead(long offset, int size, byte[] data) throws ErrnoException { 699 try { 700 readBlockedLatch.countDown(); 701 blockReadLatch.await(); 702 } catch (InterruptedException e) { 703 fail(e.getMessage()); 704 } 705 return super.onRead(offset, size, data); 706 } 707 708 @Override 709 public void onRelease() { 710 releaseLatch.countDown(); 711 } 712 }; 713 final TestProxyFileDescriptorCallback asyncCallback = 714 new TestProxyFileDescriptorCallback(1024 * 1024, "Async") { 715 @Override 716 public void onRelease() { 717 releaseLatch.countDown(); 718 } 719 }; 720 final SynchronousQueue<Handler> handlerChannel = new SynchronousQueue<>(); 721 final Thread looperThread = new Thread(() -> { 722 Looper.prepare(); 723 try { 724 handlerChannel.put(new Handler()); 725 } catch (InterruptedException e) { 726 fail(e.getMessage()); 727 } 728 Looper.loop(); 729 }); 730 looperThread.start(); 731 732 final Handler handler = handlerChannel.take(); 733 734 try (final ParcelFileDescriptor blockFd = 735 mStorageManager.openProxyFileDescriptor( 736 ParcelFileDescriptor.MODE_READ_ONLY, blockCallback, mHandler); 737 final ParcelFileDescriptor asyncFd = 738 mStorageManager.openProxyFileDescriptor( 739 ParcelFileDescriptor.MODE_READ_ONLY, asyncCallback, handler)) { 740 final Thread readingThread = new Thread(() -> { 741 final byte[] bytes = new byte[128]; 742 try { 743 744 Os.read(blockFd.getFileDescriptor(), bytes, 0, 128); 745 } catch (ErrnoException | InterruptedIOException e) { 746 fail(e.getMessage()); 747 } 748 }); 749 readingThread.start(); 750 751 readBlockedLatch.countDown(); 752 assertEquals(Thread.State.RUNNABLE, readingThread.getState()); 753 754 final byte[] bytes = new byte[128]; 755 Log.d("StorageManagerTest", "start read async"); 756 assertEquals(128, Os.read(asyncFd.getFileDescriptor(), bytes, 0, 128)); 757 Log.d("StorageManagerTest", "stop read async"); 758 759 blockReadLatch.countDown(); 760 readingThread.join(); 761 } 762 763 releaseLatch.await(); 764 handler.getLooper().quit(); 765 looperThread.join(); 766 } 767 768 @Test testOpenProxyFileDescriptor_largeFile()769 public void testOpenProxyFileDescriptor_largeFile() throws Exception { 770 final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() { 771 @Override 772 public int onRead(long offset, int size, byte[] data) throws ErrnoException { 773 for (int i = 0; i < size; i++) { 774 data[i] = 'L'; 775 } 776 return size; 777 } 778 779 @Override 780 public long onGetSize() throws ErrnoException { 781 return 8L * 1024L * 1024L * 1024L; // 8GB 782 } 783 784 @Override 785 public void onRelease() {} 786 }; 787 final byte[] bytes = new byte[128]; 788 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 789 ParcelFileDescriptor.MODE_READ_ONLY, callback, mHandler)) { 790 assertEquals(8L * 1024L * 1024L * 1024L, fd.getStatSize()); 791 792 final int readBytes = Os.pread( 793 fd.getFileDescriptor(), bytes, 0, bytes.length, fd.getStatSize() - 64L); 794 assertEquals(64, readBytes); 795 for (int i = 0; i < 64; i++) { 796 assertEquals('L', bytes[i]); 797 } 798 } 799 } 800 801 @Test testOpenProxyFileDescriptor_largeRead()802 public void testOpenProxyFileDescriptor_largeRead() throws Exception { 803 final int SIZE = 1024 * 1024; 804 final TestProxyFileDescriptorCallback callback = 805 new TestProxyFileDescriptorCallback(SIZE, "abcdefghijklmnopqrstuvwxyz"); 806 final byte[] bytes = new byte[SIZE]; 807 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 808 ParcelFileDescriptor.MODE_READ_ONLY, callback, mHandler)) { 809 final int readBytes = Os.read( 810 fd.getFileDescriptor(), bytes, 0, bytes.length); 811 assertEquals(bytes.length, readBytes); 812 for (int i = 0; i < bytes.length; i++) { 813 assertEquals(callback.bytes[i], bytes[i]); 814 } 815 } 816 } 817 818 @Test testOpenProxyFileDescriptor_largeWrite()819 public void testOpenProxyFileDescriptor_largeWrite() throws Exception { 820 final int SIZE = 1024 * 1024; 821 final TestProxyFileDescriptorCallback callback = 822 new TestProxyFileDescriptorCallback(SIZE, "abcdefghijklmnopqrstuvwxyz"); 823 final byte[] bytes = new byte[SIZE]; 824 for (int i = 0; i < SIZE; i++) { 825 bytes[i] = (byte)(i % 123); 826 } 827 try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor( 828 ParcelFileDescriptor.MODE_WRITE_ONLY, callback, mHandler)) { 829 final int writtenBytes = Os.write( 830 fd.getFileDescriptor(), bytes, 0, bytes.length); 831 assertEquals(bytes.length, writtenBytes); 832 for (int i = 0; i < bytes.length; i++) { 833 assertEquals(bytes[i], callback.bytes[i]); 834 } 835 } 836 } 837 838 @Test testIsAllocationSupported()839 public void testIsAllocationSupported() throws Exception { 840 FileDescriptor good = Os.open( 841 File.createTempFile("StorageManagerTest", "").getAbsolutePath(), 842 OsConstants.O_RDONLY, 0); 843 FileDescriptor bad = Os.open("/proc/self/cmdline", OsConstants.O_RDONLY, 0); 844 try { 845 assertTrue(mStorageManager.isAllocationSupported(good)); 846 assertFalse(mStorageManager.isAllocationSupported(bad)); 847 } finally { 848 try { 849 Os.close(good); 850 } catch (ErrnoException ignored) {} 851 852 try { 853 Os.close(bad); 854 } catch (ErrnoException ignored) {} 855 } 856 } 857 858 @Test testFatUuidHandling()859 public void testFatUuidHandling() throws Exception { 860 assertEquals(UUID.fromString("fafafafa-fafa-5afa-8afa-fafa01234567"), 861 StorageManager.convert("0123-4567")); 862 assertEquals(UUID.fromString("fafafafa-fafa-5afa-8afa-fafadeadbeef"), 863 StorageManager.convert("DEAD-BEEF")); 864 assertEquals(UUID.fromString("fafafafa-fafa-5afa-8afa-fafadeadbeef"), 865 StorageManager.convert("dead-BEEF")); 866 867 try { 868 StorageManager.convert("DEADBEEF"); 869 fail(); 870 } catch (IllegalArgumentException expected) {} 871 872 try { 873 StorageManager.convert("DEAD-BEEF0"); 874 fail(); 875 } catch (IllegalArgumentException expected) {} 876 877 assertEquals("0123-4567", 878 StorageManager.convert(UUID.fromString("fafafafa-fafa-5afa-8afa-fafa01234567"))); 879 assertEquals("DEAD-BEEF", 880 StorageManager.convert(UUID.fromString("fafafafa-fafa-5afa-8afa-fafadeadbeef"))); 881 } 882 883 @Test 884 @AppModeFull(reason = "Instant apps cannot hold MANAGE_EXTERNAL_STORAGE permission") testGetManageSpaceActivityIntent()885 public void testGetManageSpaceActivityIntent() throws Exception { 886 String packageName = "android.os.cts"; 887 int REQUEST_CODE = 1; 888 PendingIntent piActual = null; 889 890 // Test should only pass with MANAGE_EXTERNAL_STORAGE permission 891 assertThat(Environment.isExternalStorageManager()).isTrue(); 892 893 // Invalid packageName should throw an IllegalArgumentException 894 String invalidPackageName = "this.is.invalid"; 895 assertThrows( 896 IllegalArgumentException.class, 897 () -> mStorageManager.getManageSpaceActivityIntent(invalidPackageName, 898 REQUEST_CODE)); 899 900 piActual = mStorageManager.getManageSpaceActivityIntent(packageName, 901 REQUEST_CODE); 902 assertThat(piActual.isActivity()).isTrue(); 903 904 // Nothing to assert, but call send to make sure it does not throw an exception 905 piActual.send(); 906 907 // Drop MANAGE_EXTERNAL_STORAGE permission 908 InstrumentationRegistry.getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); 909 } 910 assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone)911 private void assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone) 912 throws Exception { 913 // Asserts equals() method. 914 assertEquals("StorageVolume.equals() mismatch", volume, clone); 915 // Make sure all fields match. 916 for (Field field : StorageVolume.class.getDeclaredFields()) { 917 if (Modifier.isStatic(field.getModifiers())) continue; 918 field.setAccessible(true); 919 final Object originalValue = field.get(volume); 920 final Object clonedValue = field.get(clone); 921 assertEquals("Mismatch for field " + field.getName(), originalValue, clonedValue); 922 } 923 } 924 assertStartsWith(String message, String prefix, String actual)925 private static void assertStartsWith(String message, String prefix, String actual) { 926 if (!actual.startsWith(prefix)) { 927 throw new ComparisonFailure(message, prefix, actual); 928 } 929 } 930 assertFileContains(File file, String contents)931 private static void assertFileContains(File file, String contents) throws IOException { 932 byte[] actual = readFully(new FileInputStream(file)); 933 byte[] expected = contents.getBytes("UTF-8"); 934 assertEquals("unexpected size", expected.length, actual.length); 935 for (int i = 0; i < expected.length; i++) { 936 assertEquals("unexpected value at offset " + i, expected[i], actual[i]); 937 } 938 } 939 940 private static class ObbObserver extends OnObbStateChangeListener { 941 private String path; 942 943 public int state = -1; 944 boolean done = false; 945 946 @Override onObbStateChange(String path, int state)947 public void onObbStateChange(String path, int state) { 948 Log.d(TAG, "Received message. path=" + path + ", state=" + state); 949 synchronized (this) { 950 this.path = path; 951 this.state = state; 952 done = true; 953 notifyAll(); 954 } 955 } 956 getPath()957 public String getPath() { 958 assertTrue("Expected ObbObserver to have received a state change.", done); 959 return path; 960 } 961 getState()962 public int getState() { 963 assertTrue("Expected ObbObserver to have received a state change.", done); 964 return state; 965 } 966 isDone()967 public boolean isDone() { 968 return done; 969 } 970 waitForCompletion()971 public boolean waitForCompletion() { 972 long waitTime = 0; 973 synchronized (this) { 974 while (!isDone() && waitTime < MAX_WAIT_TIME) { 975 try { 976 wait(WAIT_TIME_INCR); 977 waitTime += WAIT_TIME_INCR; 978 } catch (InterruptedException e) { 979 Log.i(TAG, "Interrupted during sleep", e); 980 } 981 } 982 } 983 984 return isDone(); 985 } 986 } 987 getTargetFiles()988 private List<File> getTargetFiles() { 989 final List<File> targets = new ArrayList<File>(); 990 for (File dir : mContext.getObbDirs()) { 991 assertNotNull("Valid media must be inserted during CTS", dir); 992 assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED, 993 Environment.getStorageState(dir)); 994 targets.add(dir); 995 } 996 return targets; 997 } 998 copyRawToFile(int rawResId, File outFile)999 private void copyRawToFile(int rawResId, File outFile) { 1000 Resources res = mContext.getResources(); 1001 InputStream is = null; 1002 try { 1003 is = res.openRawResource(rawResId); 1004 } catch (NotFoundException e) { 1005 fail("Failed to load resource with id: " + rawResId); 1006 } 1007 assertTrue(FileUtils.copyToFile(is, outFile)); 1008 exposeFile(outFile); 1009 } 1010 exposeFile(File file)1011 private File exposeFile(File file) { 1012 file.setReadable(true, false); 1013 file.setReadable(true, true); 1014 1015 File dir = file.getParentFile(); 1016 do { 1017 dir.setExecutable(true, false); 1018 dir.setExecutable(true, true); 1019 dir = dir.getParentFile(); 1020 } while (dir != null); 1021 1022 return file; 1023 } 1024 mountObb(final int resource, final File file, int expectedState)1025 private String mountObb(final int resource, final File file, int expectedState) { 1026 copyRawToFile(resource, file); 1027 1028 final ObbObserver observer = new ObbObserver(); 1029 assertTrue("mountObb call on " + file.getPath() + " should succeed", 1030 mStorageManager.mountObb(file.getPath(), null, observer)); 1031 1032 assertTrue("Mount should have completed", 1033 observer.waitForCompletion()); 1034 1035 if (expectedState == OnObbStateChangeListener.MOUNTED) { 1036 assertTrue("OBB should be mounted", mStorageManager.isObbMounted(observer.getPath())); 1037 } 1038 1039 assertEquals(expectedState, observer.getState()); 1040 1041 return observer.getPath(); 1042 } 1043 mountObbWithoutWait(final int resource, final File file)1044 private ObbObserver mountObbWithoutWait(final int resource, final File file) { 1045 copyRawToFile(resource, file); 1046 1047 final ObbObserver observer = new ObbObserver(); 1048 assertTrue("mountObb call on " + file.getPath() + " should succeed", 1049 mStorageManager.mountObb(file.getPath(), null, observer)); 1050 1051 return observer; 1052 } 1053 waitForObbActionCompletion(final File file, final ObbObserver observer, int expectedState)1054 private void waitForObbActionCompletion(final File file, final ObbObserver observer, 1055 int expectedState) { 1056 assertTrue("Mount should have completed", observer.waitForCompletion()); 1057 1058 assertTrue("OBB should be mounted", mStorageManager.isObbMounted(observer.getPath())); 1059 1060 assertEquals(expectedState, observer.getState()); 1061 } 1062 checkMountedPath(final String path)1063 private String checkMountedPath(final String path) { 1064 final String mountPath = mStorageManager.getMountedObbPath(path); 1065 assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX, 1066 OBB_MOUNT_PREFIX, 1067 mountPath); 1068 return mountPath; 1069 } 1070 unmountObb(final File file, int expectedState)1071 private void unmountObb(final File file, int expectedState) { 1072 final ObbObserver observer = new ObbObserver(); 1073 1074 assertTrue("unmountObb call on test1_new.obb should succeed", 1075 mStorageManager.unmountObb(file.getPath(), false, observer)); 1076 1077 assertTrue("Unmount should have completed", 1078 observer.waitForCompletion()); 1079 1080 assertEquals(expectedState, observer.getState()); 1081 1082 if (expectedState == OnObbStateChangeListener.UNMOUNTED) { 1083 assertFalse("OBB should not be mounted", mStorageManager.isObbMounted(file.getPath())); 1084 } 1085 } 1086 1087 @Test testComputeStorageCacheBytes()1088 public void testComputeStorageCacheBytes() throws Exception { 1089 File mockFile = mock(File.class); 1090 1091 final int[] storageThresholdPercentHigh = new int[1]; 1092 SystemUtil.runWithShellPermissionIdentity(() -> { 1093 storageThresholdPercentHigh[0] = DeviceConfig.getInt( 1094 DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, 1095 StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY, 0); 1096 }); 1097 assertTrue("storageThresholdPercentHigh [" + storageThresholdPercentHigh[0] 1098 + "] expected to be greater than equal to 0", storageThresholdPercentHigh[0] >= 0); 1099 assertTrue("storageThresholdPercentHigh [" + storageThresholdPercentHigh[0] 1100 + "] expected to be lesser than equal to 100", 1101 storageThresholdPercentHigh[0] <= 100); 1102 1103 when(mockFile.getUsableSpace()).thenReturn(10000L); 1104 when(mockFile.getTotalSpace()).thenReturn(15000L); 1105 final long[] resultHigh = new long[1]; 1106 SystemUtil.runWithShellPermissionIdentity(() -> { 1107 resultHigh[0] = mStorageManager.computeStorageCacheBytes(mockFile); 1108 }); 1109 assertTrue("" + resultHigh[0] + " expected to be greater than equal to 0", 1110 resultHigh[0] >= 0L); 1111 assertTrue("" + resultHigh[0] + " expected to be less than equal to total space", 1112 resultHigh[0] <= mockFile.getTotalSpace()); 1113 1114 when(mockFile.getUsableSpace()).thenReturn(10000L); 1115 when(mockFile.getTotalSpace()).thenReturn(250000L); 1116 final long[] resultLow = new long[1]; 1117 SystemUtil.runWithShellPermissionIdentity(() -> { 1118 resultLow[0] = mStorageManager.computeStorageCacheBytes(mockFile); 1119 }); 1120 assertTrue("" + resultLow[0] + " expected to be greater than equal to 0", 1121 resultLow[0] >= 0L); 1122 assertTrue("" + resultLow[0] + " expected to be less than equal to total space", 1123 resultLow[0] <= mockFile.getTotalSpace()); 1124 1125 when(mockFile.getUsableSpace()).thenReturn(10000L); 1126 when(mockFile.getTotalSpace()).thenReturn(100000L); 1127 final long[] resultModerate = new long[1]; 1128 SystemUtil.runWithShellPermissionIdentity(() -> { 1129 resultModerate[0] = mStorageManager.computeStorageCacheBytes(mockFile); 1130 }); 1131 assertTrue("" + resultModerate[0] + " expected to be greater than equal to 0", 1132 resultModerate[0] >= 0L); 1133 assertTrue("" + resultModerate[0] + " expected to be less than equal to total space", 1134 resultModerate[0] <= mockFile.getTotalSpace()); 1135 } 1136 readFully(InputStream in)1137 public static byte[] readFully(InputStream in) throws IOException { 1138 // Shamelessly borrowed from libcore.io.Streams 1139 try { 1140 return readFullyNoClose(in); 1141 } finally { 1142 in.close(); 1143 } 1144 } 1145 readFullyNoClose(InputStream in)1146 public static byte[] readFullyNoClose(InputStream in) throws IOException { 1147 // Shamelessly borrowed from libcore.io.Streams 1148 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 1149 byte[] buffer = new byte[1024]; 1150 int count; 1151 while ((count = in.read(buffer)) != -1) { 1152 bytes.write(buffer, 0, count); 1153 } 1154 return bytes.toByteArray(); 1155 } 1156 } 1157