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