1 /*
2  * Copyright (C) 2015 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.appsecurity.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
26 import com.android.ddmlib.Log;
27 import com.android.role.RoleProto;
28 import com.android.role.RoleServiceDumpProto;
29 import com.android.role.RoleUserStateProto;
30 import com.android.tradefed.device.CollectingByteOutputReceiver;
31 import com.android.tradefed.device.DeviceNotAvailableException;
32 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
33 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
34 import com.android.tradefed.util.AbiUtils;
35 
36 import com.google.protobuf.MessageLite;
37 import com.google.protobuf.Parser;
38 
39 import org.junit.After;
40 import org.junit.Assume;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 
45 import java.io.File;
46 import java.io.FileNotFoundException;
47 import java.util.ArrayList;
48 import java.util.List;
49 
50 /**
51  * Set of tests that verify behavior of external storage devices.
52  */
53 @RunWith(DeviceJUnit4ClassRunner.class)
54 public class ExternalStorageHostTest extends BaseHostJUnit4Test {
55     private static final String TAG = "ExternalStorageHostTest";
56 
57     private static class Config {
58         public final String apk;
59         public final String pkg;
60         public final String clazz;
61 
Config(String apk, String pkg, String clazz)62         public Config(String apk, String pkg, String clazz) {
63             this.apk = apk;
64             this.pkg = pkg;
65             this.clazz = clazz;
66         }
67     }
68 
69     private static final String COMMON_CLASS =
70             "com.android.cts.externalstorageapp.CommonExternalStorageTest";
71 
72     private static final String NONE_APK = "CtsExternalStorageApp.apk";
73     private static final String NONE_PKG = "com.android.cts.externalstorageapp";
74     private static final String NONE_CLASS = NONE_PKG + ".ExternalStorageTest";
75     private static final String READ_APK = "CtsReadExternalStorageApp.apk";
76     private static final String READ_PKG = "com.android.cts.readexternalstorageapp";
77     private static final String READ_CLASS = READ_PKG + ".ReadExternalStorageTest";
78     private static final String WRITE_APK = "CtsWriteExternalStorageApp.apk";
79     private static final String WRITE_PKG = "com.android.cts.writeexternalstorageapp";
80     private static final String WRITE_CLASS = WRITE_PKG + ".WriteExternalStorageTest";
81     private static final String WRITE_APK_2 = "CtsWriteExternalStorageApp2.apk";
82     private static final String WRITE_PKG_2 = "com.android.cts.writeexternalstorageapp2";
83     private static final String MULTIUSER_APK = "CtsMultiUserStorageApp.apk";
84     private static final String MULTIUSER_PKG = "com.android.cts.multiuserstorageapp";
85     private static final String MULTIUSER_CLASS = MULTIUSER_PKG + ".MultiUserStorageTest";
86 
87     private static final String MEDIA_CLAZZ = "com.android.cts.mediastorageapp.MediaStorageTest";
88 
89     private static final Config MEDIA = new Config("CtsMediaStorageApp.apk",
90             "com.android.cts.mediastorageapp", MEDIA_CLAZZ);
91     private static final Config MEDIA_28 = new Config("CtsMediaStorageApp28.apk",
92             "com.android.cts.mediastorageapp28", MEDIA_CLAZZ);
93     private static final Config MEDIA_29 = new Config("CtsMediaStorageApp29.apk",
94             "com.android.cts.mediastorageapp29", MEDIA_CLAZZ);
95 
96     private static final String PERM_ACCESS_MEDIA_LOCATION =
97             "android.permission.ACCESS_MEDIA_LOCATION";
98     private static final String PERM_READ_EXTERNAL_STORAGE =
99             "android.permission.READ_EXTERNAL_STORAGE";
100     private static final String PERM_WRITE_EXTERNAL_STORAGE =
101             "android.permission.WRITE_EXTERNAL_STORAGE";
102 
103     private static final String APP_OPS_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
104     private static final String APP_OPS_MANAGE_MEDIA = "android:manage_media";
105 
106     /** Copied from PackageManager*/
107     private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
108     private static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
109     private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
110     private static final String FEATURE_WATCH = "android.hardware.type.watch";
111 
112     private int[] mUsers;
113 
getTestAppFile(String fileName)114     private File getTestAppFile(String fileName) throws FileNotFoundException {
115         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
116         return buildHelper.getTestFile(fileName);
117     }
118 
119     @Before
setUp()120     public void setUp() throws Exception {
121         mUsers = Utils.prepareMultipleUsers(getDevice());
122         assertNotNull(getAbi());
123         assertNotNull(getBuild());
124     }
125 
126     @Before
127     @After
cleanUp()128     public void cleanUp() throws DeviceNotAvailableException {
129         getDevice().uninstallPackage(NONE_PKG);
130         getDevice().uninstallPackage(READ_PKG);
131         getDevice().uninstallPackage(WRITE_PKG);
132         getDevice().uninstallPackage(MULTIUSER_PKG);
133 
134         wipePrimaryExternalStorage();
135     }
136 
137     @Test
testExternalStorageRename()138     public void testExternalStorageRename() throws Exception {
139         try {
140             wipePrimaryExternalStorage();
141 
142             getDevice().uninstallPackage(WRITE_PKG);
143             installPackage(WRITE_APK);
144 
145             // Make sure user initialization is complete before testing
146             waitForBroadcastIdle();
147 
148             for (int user : mUsers) {
149                 runDeviceTests(WRITE_PKG, WRITE_CLASS, "testExternalStorageRename", user);
150             }
151         } finally {
152             getDevice().uninstallPackage(WRITE_PKG);
153         }
154     }
155 
156     /**
157      * Verify that app with no external storage permissions works correctly.
158      */
159     @Test
testExternalStorageNone29()160     public void testExternalStorageNone29() throws Exception {
161         try {
162             wipePrimaryExternalStorage();
163 
164             getDevice().uninstallPackage(NONE_PKG);
165             String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
166             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
167 
168             for (int user : mUsers) {
169                 runDeviceTests(NONE_PKG, COMMON_CLASS, user);
170                 runDeviceTests(NONE_PKG, NONE_CLASS, user);
171             }
172         } finally {
173             getDevice().uninstallPackage(NONE_PKG);
174         }
175     }
176 
177     /**
178      * Verify that app with
179      * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} works
180      * correctly.
181      */
182     @Test
testExternalStorageRead29()183     public void testExternalStorageRead29() throws Exception {
184         try {
185             wipePrimaryExternalStorage();
186 
187             getDevice().uninstallPackage(READ_PKG);
188             String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
189             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
190 
191             for (int user : mUsers) {
192                 runDeviceTests(READ_PKG, COMMON_CLASS, user);
193                 runDeviceTests(READ_PKG, READ_CLASS, user);
194             }
195         } finally {
196             getDevice().uninstallPackage(READ_PKG);
197         }
198     }
199 
200     /**
201      * Verify that app with
202      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} works
203      * correctly.
204      */
205     @Test
testExternalStorageWrite()206     public void testExternalStorageWrite() throws Exception {
207         try {
208             wipePrimaryExternalStorage();
209 
210             getDevice().uninstallPackage(WRITE_PKG);
211             String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
212             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
213 
214             for (int user : mUsers) {
215                 runDeviceTests(WRITE_PKG, COMMON_CLASS, user);
216                 runDeviceTests(WRITE_PKG, WRITE_CLASS, user);
217             }
218         } finally {
219             getDevice().uninstallPackage(WRITE_PKG);
220         }
221     }
222 
223     /**
224      * Verify that apps can't leave gifts in package specific external storage
225      * directories belonging to other apps. Apps can only create files in their
226      * external storage directories.
227      */
228     @Test
testExternalStorageNoGifts()229     public void testExternalStorageNoGifts() throws Exception {
230         try {
231             wipePrimaryExternalStorage();
232 
233             getDevice().uninstallPackage(NONE_PKG);
234             getDevice().uninstallPackage(READ_PKG);
235             getDevice().uninstallPackage(WRITE_PKG);
236             final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
237 
238             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
239             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
240             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
241             for (int user : mUsers) {
242                 runDeviceTests(NONE_PKG, NONE_PKG + ".GiftTest", "testStageNonGifts", user);
243                 runDeviceTests(READ_PKG, READ_PKG + ".ReadGiftTest", "testStageNonGifts", user);
244                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest", "testStageNonGifts", user);
245 
246                 runDeviceTests(NONE_PKG, NONE_PKG + ".GiftTest", "testNoGifts", user);
247                 runDeviceTests(READ_PKG, READ_PKG + ".ReadGiftTest", "testNoGifts", user);
248                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest", "testNoGifts", user);
249             }
250         } finally {
251             getDevice().uninstallPackage(NONE_PKG);
252             getDevice().uninstallPackage(READ_PKG);
253             getDevice().uninstallPackage(WRITE_PKG);
254         }
255     }
256 
257     /**
258      * Verify that app with REQUEST_INSTALL_PACKAGES can leave gifts in obb
259      * directories belonging to other apps, and those apps can read.
260      */
261     @Test
testCanAccessOtherObbDirs()262     public void testCanAccessOtherObbDirs() throws Exception {
263         try {
264             wipePrimaryExternalStorage();
265 
266             getDevice().uninstallPackage(WRITE_PKG_2);
267             getDevice().uninstallPackage(NONE_PKG);
268             final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
269 
270             // We purposefully delay the installation of the reading apps to
271             // verify that the daemon correctly invalidates any caches.
272             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK_2), false, options));
273             for (int user : mUsers) {
274                 updateAppOp(WRITE_PKG_2, user, "android:request_install_packages", true);
275                 updatePermissions(WRITE_PKG_2, user, new String[] {
276                         PERM_READ_EXTERNAL_STORAGE,
277                         PERM_WRITE_EXTERNAL_STORAGE,
278                 }, true);
279             }
280 
281             for (int user : mUsers) {
282                 runDeviceTests(WRITE_PKG_2, WRITE_PKG + ".WriteGiftTest", "testObbGifts", user);
283             }
284 
285             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
286             for (int user : mUsers) {
287                 runDeviceTests(NONE_PKG, NONE_PKG + ".GiftTest", "testObbGifts", user);
288             }
289 
290             for (int user : mUsers) {
291                 runDeviceTests(WRITE_PKG_2, WRITE_PKG + ".WriteGiftTest",
292                         "testAccessObbGifts", user);
293                 updateAppOp(WRITE_PKG_2, user, "android:request_install_packages", false);
294                 runDeviceTests(WRITE_PKG_2, WRITE_PKG + ".WriteGiftTest",
295                         "testCantAccessObbGifts", user);
296             }
297         } finally {
298             getDevice().uninstallPackage(WRITE_PKG_2);
299             getDevice().uninstallPackage(NONE_PKG);
300         }
301     }
302 
303     @Test
testExternalStorageUnsharedObb()304     public void testExternalStorageUnsharedObb() throws Exception {
305         final int numUsers = mUsers.length;
306         Assume.assumeTrue(numUsers > 1);
307 
308         try {
309             wipePrimaryExternalStorage();
310 
311             getDevice().uninstallPackage(NONE_PKG);
312             getDevice().uninstallPackage(WRITE_PKG);
313             final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
314 
315             // We purposefully delay the installation of the reading apps to
316             // verify that the daemon correctly invalidates any caches.
317             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
318             updateAppOp(WRITE_PKG, mUsers[0], "android:request_install_packages", true);
319             runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest", "testObbGifts", mUsers[0]);
320 
321             // Create a file in one user and verify that file is not accessible to other users.
322             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
323             for (int i = 1; i < numUsers; ++i) {
324                 runDeviceTests(NONE_PKG, NONE_PKG + ".GiftTest", "testNoObbGifts", mUsers[i]);
325                 updateAppOp(WRITE_PKG, mUsers[i], "android:request_install_packages", true);
326                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest", "testObbGifts", mUsers[i]);
327             }
328 
329             // Delete a file in one user and verify that it doesn't affect files accessible to
330             // other users.
331             runDeviceTests(NONE_PKG, NONE_PKG + ".GiftTest", "testRemoveObbGifts", mUsers[0]);
332             for (int i = 1; i < numUsers; ++i) {
333                 runDeviceTests(NONE_PKG, NONE_PKG + ".GiftTest", "testObbGifts", mUsers[i]);
334             }
335 
336         } finally {
337             getDevice().uninstallPackage(NONE_PKG);
338             getDevice().uninstallPackage(WRITE_PKG);
339         }
340     }
341 
342     /**
343      * Test multi-user emulated storage environment, ensuring that each user has
344      * isolated storage.
345      */
346     @Test
testMultiUserStorageIsolated()347     public void testMultiUserStorageIsolated() throws Exception {
348         try {
349             if (mUsers.length == 1) {
350                 Log.d(TAG, "Single user device; skipping isolated storage tests");
351                 return;
352             }
353 
354             final int owner = mUsers[0];
355             final int secondary = mUsers[1];
356 
357             // Install our test app
358             getDevice().uninstallPackage(MULTIUSER_PKG);
359             String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
360             final String installResult = getDevice()
361                     .installPackage(getTestAppFile(MULTIUSER_APK), false, options);
362             assertNull("Failed to install: " + installResult, installResult);
363 
364             // Clear data from previous tests
365             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testCleanIsolatedStorage", owner);
366             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testCleanIsolatedStorage", secondary);
367 
368             // Have both users try writing into isolated storage
369             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testWriteIsolatedStorage", owner);
370             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testWriteIsolatedStorage", secondary);
371 
372             // Verify they both have isolated view of storage
373             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testReadIsolatedStorage", owner);
374             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testReadIsolatedStorage", secondary);
375 
376             // Verify they can't poke at each other
377             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testUserIsolation", owner);
378             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testUserIsolation", secondary);
379 
380             // Verify they can't access other users' content using media provider
381             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", owner);
382             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", secondary);
383         } finally {
384             getDevice().uninstallPackage(MULTIUSER_PKG);
385         }
386     }
387 
388     /**
389      * Test that apps with read permissions see the appropriate permissions.
390      */
391     @Test
testMultiViewMoveConsistency()392     public void testMultiViewMoveConsistency() throws Exception {
393         try {
394             wipePrimaryExternalStorage();
395 
396             getDevice().uninstallPackage(NONE_PKG);
397             getDevice().uninstallPackage(READ_PKG);
398             final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
399 
400             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
401 
402             for (int user : mUsers) {
403                 runDeviceTests(READ_PKG, READ_PKG + ".ReadMultiViewTest", "testFolderSetup", user);
404             }
405             for (int user : mUsers) {
406                 runDeviceTests(READ_PKG, READ_PKG + ".ReadMultiViewTest", "testRWAccess", user);
407             }
408 
409             // for fuse file system
410             Thread.sleep(10000);
411             for (int user : mUsers) {
412                 runDeviceTests(READ_PKG, READ_PKG + ".ReadMultiViewTest", "testRWAccess", user);
413             }
414         } finally {
415             getDevice().uninstallPackage(NONE_PKG);
416             getDevice().uninstallPackage(READ_PKG);
417         }
418     }
419 
420     /** Verify that app without READ_EXTERNAL can play default URIs in external storage. */
421     @Test
testExternalStorageReadDefaultUris()422     public void testExternalStorageReadDefaultUris() throws Exception {
423         try {
424             wipePrimaryExternalStorage();
425 
426             getDevice().uninstallPackage(NONE_PKG);
427             getDevice().uninstallPackage(WRITE_PKG);
428             final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
429 
430             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
431             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
432 
433             for (int user : mUsers) {
434                 updateAppOp(WRITE_PKG, user, "android:write_settings", true);
435                 runDeviceTests(
436                         WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", user);
437 
438                 runDeviceTests(
439                         NONE_PKG, NONE_PKG + ".ReadDefaultUris", "testPlayDefaultUris", user);
440             }
441         } finally {
442             // Make sure the provider and uris are reset on failure.
443             for (int user : mUsers) {
444                 runDeviceTests(
445                         WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", user);
446             }
447             getDevice().uninstallPackage(NONE_PKG);
448             getDevice().uninstallPackage(WRITE_PKG);
449         }
450     }
451 
452     /**
453      * For security reasons, the shell user cannot access the shared storage of
454      * secondary users. Instead, developers should use the {@code content} shell
455      * tool to read/write files in those locations.
456      */
457     @Test
testSecondaryUsersInaccessible()458     public void testSecondaryUsersInaccessible() throws Exception {
459         List<String> mounts = new ArrayList<>();
460         for (String line : getDevice().executeShellCommand("cat /proc/mounts").split("\n")) {
461             String[] split = line.split(" ");
462             if (split[1].startsWith("/storage/") || split[1].startsWith("/mnt/")) {
463                 mounts.add(split[1]);
464             }
465         }
466 
467         for (int user : mUsers) {
468             String probe = "/sdcard/../" + user;
469             if (user == Utils.USER_SYSTEM) {
470                 // Primary user should always be visible. Skip checking raw
471                 // mount points, since we'd get false-positives for physical
472                 // devices that aren't multi-user aware.
473                 assertTrue(probe, access(probe));
474             } else {
475                 // Secondary user should never be visible.
476                 assertFalse(probe, access(probe));
477                 for (String mount : mounts) {
478                     probe = mount + "/" + user;
479                     assertFalse(probe, access(probe));
480                 }
481             }
482         }
483     }
484 
485     @Test
testMediaLegacy28()486     public void testMediaLegacy28() throws Exception {
487         doMediaLegacy(MEDIA_28);
488     }
489     @Test
testMediaLegacy29()490     public void testMediaLegacy29() throws Exception {
491         doMediaLegacy(MEDIA_29);
492     }
493 
doMediaLegacy(Config config)494     private void doMediaLegacy(Config config) throws Exception {
495         installPackage(config.apk);
496         installPackage(MEDIA_29.apk);
497         // Make sure user initialization is complete before updating permission
498         waitForBroadcastIdle();
499         for (int user : mUsers) {
500             updatePermissions(config.pkg, user, new String[] {
501                     PERM_READ_EXTERNAL_STORAGE,
502                     PERM_WRITE_EXTERNAL_STORAGE,
503             }, true);
504             updatePermissions(MEDIA_29.pkg, user, new String[] {
505                     PERM_READ_EXTERNAL_STORAGE,
506                     PERM_WRITE_EXTERNAL_STORAGE,
507             }, true);
508 
509             // Create the files needed for the test from MEDIA_29 pkg since shell
510             // can't access secondary user's storage.
511             runDeviceTests(MEDIA_29.pkg, MEDIA_29.clazz, "testStageFiles", user);
512             runDeviceTests(config.pkg, config.clazz, "testLegacy", user);
513             runDeviceTests(MEDIA_29.pkg, MEDIA_29.clazz, "testClearFiles", user);
514         }
515     }
516 
517 
518     @Test
testGrantUriPermission()519     public void testGrantUriPermission() throws Exception {
520         doGrantUriPermission(MEDIA, "testGrantUriPermission", new String[]{});
521         doGrantUriPermission(MEDIA, "testGrantUriPermission",
522                 new String[]{PERM_READ_EXTERNAL_STORAGE});
523         doGrantUriPermission(MEDIA, "testGrantUriPermission",
524                 new String[]{PERM_READ_EXTERNAL_STORAGE, PERM_WRITE_EXTERNAL_STORAGE});
525     }
526 
527     @Test
testGrantUriPermission29()528     public void testGrantUriPermission29() throws Exception {
529         doGrantUriPermission(MEDIA_29, "testGrantUriPermission", new String[]{});
530         doGrantUriPermission(MEDIA_29, "testGrantUriPermission",
531                 new String[]{PERM_READ_EXTERNAL_STORAGE});
532         doGrantUriPermission(MEDIA_29, "testGrantUriPermission",
533                 new String[]{PERM_READ_EXTERNAL_STORAGE, PERM_WRITE_EXTERNAL_STORAGE});
534     }
535 
doGrantUriPermission(Config config, String method, String[] grantPermissions)536     private void doGrantUriPermission(Config config, String method, String[] grantPermissions)
537             throws Exception {
538         uninstallPackage(config.apk);
539         installPackage(config.apk);
540         for (int user : mUsers) {
541             // Over revoke all permissions and grant necessary permissions later.
542             updatePermissions(config.pkg, user, new String[] {
543                     PERM_READ_EXTERNAL_STORAGE,
544                     PERM_WRITE_EXTERNAL_STORAGE,
545             }, false);
546             updatePermissions(config.pkg, user, grantPermissions, true);
547             runDeviceTests(config.pkg, config.clazz, method, user);
548         }
549     }
550 
551     @Test
testMediaNone()552     public void testMediaNone() throws Exception {
553         doMediaNone(MEDIA);
554     }
555     @Test
testMediaNone28()556     public void testMediaNone28() throws Exception {
557         doMediaNone(MEDIA_28);
558     }
559     @Test
testMediaNone29()560     public void testMediaNone29() throws Exception {
561         doMediaNone(MEDIA_29);
562     }
563 
doMediaNone(Config config)564     private void doMediaNone(Config config) throws Exception {
565         installPackage(config.apk);
566         for (int user : mUsers) {
567             updatePermissions(config.pkg, user, new String[] {
568                     PERM_READ_EXTERNAL_STORAGE,
569                     PERM_WRITE_EXTERNAL_STORAGE,
570             }, false);
571 
572             runDeviceTests(config.pkg, config.clazz, "testMediaNone", user);
573         }
574     }
575 
576     @Test
testMediaRead()577     public void testMediaRead() throws Exception {
578         doMediaRead(MEDIA);
579     }
580     @Test
testMediaRead28()581     public void testMediaRead28() throws Exception {
582         doMediaRead(MEDIA_28);
583     }
584     @Test
testMediaRead29()585     public void testMediaRead29() throws Exception {
586         doMediaRead(MEDIA_29);
587     }
588 
doMediaRead(Config config)589     private void doMediaRead(Config config) throws Exception {
590         installPackage(config.apk);
591         for (int user : mUsers) {
592             updatePermissions(config.pkg, user, new String[] {
593                     PERM_READ_EXTERNAL_STORAGE,
594             }, true);
595             updatePermissions(config.pkg, user, new String[] {
596                     PERM_WRITE_EXTERNAL_STORAGE,
597             }, false);
598 
599             runDeviceTests(config.pkg, config.clazz, "testMediaRead", user);
600         }
601     }
602 
603     @Test
testMediaWrite()604     public void testMediaWrite() throws Exception {
605         doMediaWrite(MEDIA);
606     }
607     @Test
testMediaWrite28()608     public void testMediaWrite28() throws Exception {
609         doMediaWrite(MEDIA_28);
610     }
611     @Test
testMediaWrite29()612     public void testMediaWrite29() throws Exception {
613         doMediaWrite(MEDIA_29);
614     }
615 
doMediaWrite(Config config)616     private void doMediaWrite(Config config) throws Exception {
617         installPackage(config.apk);
618         for (int user : mUsers) {
619             updatePermissions(config.pkg, user, new String[] {
620                     PERM_READ_EXTERNAL_STORAGE,
621                     PERM_WRITE_EXTERNAL_STORAGE,
622             }, true);
623 
624             runDeviceTests(config.pkg, config.clazz, "testMediaWrite", user);
625         }
626     }
627 
628     @Test
testMediaEscalation_RequestWriteFilePathSupport()629     public void testMediaEscalation_RequestWriteFilePathSupport() throws Exception {
630         // Not adding tests for MEDIA_28 and MEDIA_29 as they need W_E_S for write access via file
631         // path for shared files, and will always have access as they have W_E_S.
632         installPackage(MEDIA.apk);
633 
634         int user = getDevice().getCurrentUser();
635         updatePermissions(MEDIA.pkg, user, new String[] {
636                 PERM_READ_EXTERNAL_STORAGE,
637         }, true);
638         updatePermissions(MEDIA.pkg, user, new String[] {
639                 PERM_WRITE_EXTERNAL_STORAGE,
640         }, false);
641 
642         runDeviceTests(MEDIA.pkg, MEDIA.clazz, "testMediaEscalation_RequestWriteFilePathSupport",
643                 user);
644     }
645 
646     @Test
testMediaEscalation()647     public void testMediaEscalation() throws Exception {
648         doMediaEscalation(MEDIA);
649     }
650     @Test
testMediaEscalation28()651     public void testMediaEscalation28() throws Exception {
652         doMediaEscalation(MEDIA_28);
653     }
654     @Test
testMediaEscalation29()655     public void testMediaEscalation29() throws Exception {
656         doMediaEscalation(MEDIA_29);
657     }
658 
doMediaEscalation(Config config)659     private void doMediaEscalation(Config config) throws Exception {
660         installPackage(config.apk);
661 
662         // TODO: extend test to exercise secondary users
663         int user = getDevice().getCurrentUser();
664         updatePermissions(config.pkg, user, new String[] {
665                 PERM_READ_EXTERNAL_STORAGE,
666         }, true);
667         updatePermissions(config.pkg, user, new String[] {
668                 PERM_WRITE_EXTERNAL_STORAGE,
669         }, false);
670 
671         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_Open", user);
672         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_Update", user);
673         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_Delete", user);
674 
675         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_RequestWrite", user);
676         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_RequestTrash", user);
677         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_RequestFavorite", user);
678         runDeviceTests(config.pkg, config.clazz, "testMediaEscalation_RequestDelete", user);
679     }
680 
681     @Test
testExternalStorageClearing()682     public void testExternalStorageClearing() throws Exception {
683         String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
684 
685         try {
686             getDevice().uninstallPackage(WRITE_PKG);
687             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
688             for (int user : mUsers) {
689                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest", "testClearingWrite", user);
690             }
691 
692             // Uninstall and reinstall means all storage should be cleared
693             getDevice().uninstallPackage(WRITE_PKG);
694             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
695             for (int user : mUsers) {
696                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest", "testClearingRead", user);
697             }
698         } finally {
699             getDevice().uninstallPackage(WRITE_PKG);
700         }
701     }
702 
703     @Test
testIsExternalStorageLegacy()704     public void testIsExternalStorageLegacy() throws Exception {
705         String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
706 
707         try {
708             getDevice().uninstallPackage(WRITE_PKG);
709             getDevice().uninstallPackage(WRITE_PKG_2);
710             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
711             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK_2), false, options));
712             for (int user : mUsers) {
713                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest",
714                         "testIsExternalStorageLegacy", user);
715                 updatePermissions(WRITE_PKG, user, new String[] {
716                         PERM_READ_EXTERNAL_STORAGE,
717                         PERM_WRITE_EXTERNAL_STORAGE,
718                 }, false);
719                 runDeviceTests(WRITE_PKG, WRITE_PKG + ".WriteGiftTest",
720                         "testIsExternalStorageLegacy", user);
721 
722                 runDeviceTests(WRITE_PKG_2, WRITE_PKG + ".WriteGiftTest",
723                         "testNotIsExternalStorageLegacy", user);
724             }
725         } finally {
726             getDevice().uninstallPackage(WRITE_PKG);
727             getDevice().uninstallPackage(WRITE_PKG_2);
728         }
729     }
730 
731     /**
732      * Check the behavior when the app calls MediaStore#createTrashRequest,
733      * MediaStore#createDeleteRequest or MediaStore#createWriteRequest, the user
734      * click the deny button on confirmation dialog.
735      *
736      * @throws Exception
737      */
738     @Test
testCreateRequest_userDenied()739     public void testCreateRequest_userDenied() throws Exception {
740         installPackage(MEDIA.apk);
741 
742         int user = getDevice().getCurrentUser();
743 
744         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
745                 "testMediaEscalationWithDenied_RequestWrite", user);
746         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
747                 "testMediaEscalationWithDenied_RequestDelete", user);
748         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
749                 "testMediaEscalationWithDenied_RequestTrash", user);
750         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
751                 "testMediaEscalationWithDenied_RequestUnTrash", user);
752     }
753 
754     /**
755      * If the app is NOT granted {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
756      * and {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
757      * when it calls MediaStore#createTrashRequest,
758      * MediaStore#createDeleteRequest, or MediaStore#createWriteRequest,
759      * the system will show the user confirmation dialog.
760      *
761      * @throws Exception
762      */
763     @Test
testCreateRequest_noRESAndMES_showConfirmDialog()764     public void testCreateRequest_noRESAndMES_showConfirmDialog() throws Exception {
765         installPackage(MEDIA.apk);
766 
767         int user = getDevice().getCurrentUser();
768 
769         // grant permissions
770         updatePermissions(MEDIA.pkg, user, new String[] {
771                 PERM_ACCESS_MEDIA_LOCATION,
772         }, true);
773         // revoke permissions
774         updatePermissions(MEDIA.pkg, user, new String[] {
775                 PERM_READ_EXTERNAL_STORAGE,
776                 PERM_WRITE_EXTERNAL_STORAGE,
777         }, false);
778 
779 
780         // revoke the app ops permission
781         updateAppOp(MEDIA.pkg, user, APP_OPS_MANAGE_EXTERNAL_STORAGE, false);
782 
783         // grant the app ops permission
784         updateAppOp(MEDIA.pkg, user, APP_OPS_MANAGE_MEDIA, true);
785 
786         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
787                 "testMediaEscalation_RequestWrite_showConfirmDialog", user);
788     }
789 
790     /**
791      * If the app is NOT granted {@link android.Manifest.permission#MANAGE_MEDIA},
792      * when it calls MediaStore#createTrashRequest,
793      * MediaStore#createDeleteRequest, or MediaStore#createWriteRequest,
794      * the system will show the user confirmation dialog.
795      *
796      * @throws Exception
797      */
798     @Test
testCreateRequest_noMANAGEMEDIA_showConfirmDialog()799     public void testCreateRequest_noMANAGEMEDIA_showConfirmDialog() throws Exception {
800         installPackage(MEDIA.apk);
801 
802         int user = getDevice().getCurrentUser();
803         // grant permissions
804         updatePermissions(MEDIA.pkg, user, new String[] {
805                 PERM_READ_EXTERNAL_STORAGE,
806                 PERM_ACCESS_MEDIA_LOCATION,
807         }, true);
808 
809         // revoke the app ops permission
810         updateAppOp(MEDIA.pkg, user, APP_OPS_MANAGE_MEDIA, false);
811 
812         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
813                 "testMediaEscalation_RequestWrite_showConfirmDialog", user);
814         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
815                 "testMediaEscalation_RequestTrash_showConfirmDialog", user);
816         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
817                 "testMediaEscalation_RequestDelete_showConfirmDialog", user);
818     }
819 
820     /**
821      * If the app is granted {@link android.Manifest.permission#MANAGE_MEDIA},
822      * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}, without
823      * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION},
824      * when it calls MediaStore#createTrashRequest or
825      * MediaStore#createDeleteRequest, The system will NOT show the user
826      * confirmation dialog. When it calls MediaStore#createWriteRequest, the
827      * system will show the user confirmation dialog.
828      *
829      * @throws Exception
830      */
831     @Test
testCreateRequest_withNoAML_showConfirmDialog()832     public void testCreateRequest_withNoAML_showConfirmDialog() throws Exception {
833         installPackage(MEDIA.apk);
834 
835         int user = getDevice().getCurrentUser();
836         // grant permissions
837         updatePermissions(MEDIA.pkg, user, new String[] {
838                 PERM_READ_EXTERNAL_STORAGE,
839         }, true);
840         // revoke permission
841         updatePermissions(MEDIA.pkg, user, new String[] {
842                 PERM_ACCESS_MEDIA_LOCATION,
843         }, false);
844 
845         // grant the app ops permission
846         updateAppOp(MEDIA.pkg, user, APP_OPS_MANAGE_MEDIA, true);
847 
848         // show confirm dialog in requestWrite
849         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
850                 "testMediaEscalation_RequestWrite_showConfirmDialog", user);
851 
852         // not show confirm dialog in requestTrash and requestDelete
853         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
854                 "testMediaEscalation_RequestTrash_notShowConfirmDialog", user);
855         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
856                 "testMediaEscalation_RequestDelete_notShowConfirmDialog", user);
857     }
858 
859     /**
860      * If the app is granted {@link android.Manifest.permission#MANAGE_MEDIA},
861      * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}, and
862      * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION},
863      * when it calls MediaStore#createWriteRequest, MediaStore#createTrashRequest or
864      * MediaStore#createDeleteRequest, the system will NOT show the user confirmation dialog.
865      *
866      * @throws Exception
867      */
868     @Test
testCreateRequest_withPermission_notShowConfirmDialog()869     public void testCreateRequest_withPermission_notShowConfirmDialog() throws Exception {
870         installPackage(MEDIA.apk);
871 
872         int user = getDevice().getCurrentUser();
873         // grant permissions
874         updatePermissions(MEDIA.pkg, user, new String[] {
875                 PERM_READ_EXTERNAL_STORAGE,
876                 PERM_ACCESS_MEDIA_LOCATION,
877         }, true);
878 
879         // revoke the app ops permission
880         updateAppOp(MEDIA.pkg, user, APP_OPS_MANAGE_MEDIA, true);
881 
882         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
883                 "testMediaEscalation_RequestWrite_notShowConfirmDialog", user);
884         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
885                 "testMediaEscalation_RequestTrash_notShowConfirmDialog", user);
886         runDeviceTests(MEDIA.pkg, MEDIA.clazz,
887                 "testMediaEscalation_RequestDelete_notShowConfirmDialog", user);
888     }
889 
getDump(Parser<T> parser, String command)890     private <T extends MessageLite> T getDump(Parser<T> parser, String command) throws Exception {
891         final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
892         getDevice().executeShellCommand(command, receiver);
893         return parser.parseFrom(receiver.getOutput());
894     }
895 
getAllUsersRoleStates()896     private List<RoleUserStateProto> getAllUsersRoleStates() throws Exception {
897         final RoleServiceDumpProto dumpProto =
898                 getDump(RoleServiceDumpProto.parser(), "dumpsys role --proto");
899         final List<RoleUserStateProto> res = new ArrayList<>();
900         for (RoleUserStateProto userState : dumpProto.getUserStatesList()) {
901             for (int i : mUsers) {
902                 if (i == userState.getUserId()) {
903                     res.add(userState);
904                     break;
905                 }
906             }
907         }
908         return res;
909     }
910 
911     /**
912      * Bypasses the calling test case if ANY of the given features is available in the device.
913      */
bypassTestForFeatures(String... features)914     private void bypassTestForFeatures(String... features) throws DeviceNotAvailableException {
915         final String featureList = getDevice().executeShellCommand("pm list features");
916         for (String feature : features) {
917             Assume.assumeFalse(featureList.contains(feature));
918         }
919     }
920 
921     @Test
testSystemGalleryExists()922     public void testSystemGalleryExists() throws Exception {
923         // Watches, TVs and IoT devices are not obligated to have a system gallery
924         bypassTestForFeatures(FEATURE_AUTOMOTIVE, FEATURE_EMBEDDED, FEATURE_LEANBACK_ONLY,
925                 FEATURE_WATCH);
926 
927         final List<RoleUserStateProto> usersRoleStates = getAllUsersRoleStates();
928 
929         assertEquals("Unexpected number of users returned by dumpsys role",
930                 mUsers.length, usersRoleStates.size());
931 
932         for (RoleUserStateProto userState : usersRoleStates) {
933             final List<RoleProto> roles = userState.getRolesList();
934             boolean systemGalleryRoleFound = false;
935 
936             // Iterate through the roles until we find the System Gallery role
937             for (RoleProto roleProto : roles) {
938                 if ("android.app.role.SYSTEM_GALLERY".equals(roleProto.getName())) {
939                     assertEquals(1, roleProto.getHoldersList().size());
940                     systemGalleryRoleFound = true;
941                     break;
942                 }
943             }
944             assertTrue("SYSTEM_GALLERY not defined for user " + userState.getUserId(),
945                     systemGalleryRoleFound);
946         }
947     }
948 
access(String path)949     private boolean access(String path) throws DeviceNotAvailableException {
950         final long nonce = System.nanoTime();
951         return getDevice().executeShellCommand("ls -la " + path + " && echo " + nonce)
952                 .contains(Long.toString(nonce));
953     }
954 
updatePermissions(String packageName, int userId, String[] permissions, boolean grant)955     private void updatePermissions(String packageName, int userId, String[] permissions,
956             boolean grant) throws Exception {
957         final String verb = grant ? "grant" : "revoke";
958         for (String permission : permissions) {
959             getDevice().executeShellCommand(
960                     "cmd package " + verb + " --user " + userId + " --uid " + packageName + " "
961                             + permission);
962         }
963     }
964 
965     /** Wait until all broadcast queues are idle. */
waitForBroadcastIdle()966     private void waitForBroadcastIdle() throws Exception{
967         getDevice().executeShellCommand("am wait-for-broadcast-idle");
968     }
969 
updateAppOp(String packageName, int userId, String appOp, boolean allow)970     private void updateAppOp(String packageName, int userId, String appOp, boolean allow)
971             throws Exception {
972         updateAppOp(packageName, false, userId, appOp, allow);
973     }
974 
updateAppOp(String packageName, boolean targetsUid, int userId, String appOp, boolean allow)975     private void updateAppOp(String packageName, boolean targetsUid, int userId,
976             String appOp, boolean allow)
977             throws Exception {
978         final String verb = allow ? "allow" : "default";
979         getDevice().executeShellCommand(
980                 "cmd appops set --user " + userId + (targetsUid ? " --uid " : " ") + packageName
981                         + " " + appOp + " " + verb);
982     }
983 
wipePrimaryExternalStorage()984     private void wipePrimaryExternalStorage() throws DeviceNotAvailableException {
985         // Can't delete everything under /sdcard as that's going to remove the mounts.
986         getDevice().executeShellCommand("find /sdcard -type f -delete");
987         getDevice().executeShellCommand("rm -rf /sdcard/DCIM/*");
988         getDevice().executeShellCommand("rm -rf /sdcard/MUST_*");
989     }
990 
runDeviceTests(String packageName, String testClassName, int userId)991     private void runDeviceTests(String packageName, String testClassName, int userId)
992             throws DeviceNotAvailableException {
993         runDeviceTests(getDevice(), packageName, testClassName, null, userId, null);
994     }
995 
runDeviceTests(String packageName, String testClassName, String testMethodName, int userId)996     private void runDeviceTests(String packageName, String testClassName, String testMethodName,
997             int userId) throws DeviceNotAvailableException {
998         runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, null);
999     }
1000 }
1001