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