• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.permissionpolicy.cts;
18 
19 import static android.permission.cts.PermissionUtils.isGranted;
20 import static android.permission.cts.PermissionUtils.isPermissionGranted;
21 
22 import static com.android.compatibility.common.util.SystemUtil.eventually;
23 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.junit.Assert.fail;
28 
29 import android.Manifest;
30 import android.Manifest.permission;
31 import android.app.AppOpsManager;
32 import android.content.Context;
33 import android.content.pm.PackageInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PermissionInfo;
36 import android.platform.test.annotations.AppModeFull;
37 import android.util.ArraySet;
38 
39 import androidx.annotation.NonNull;
40 import androidx.test.platform.app.InstrumentationRegistry;
41 
42 import com.android.compatibility.common.util.ThrowingRunnable;
43 import com.android.modules.utils.build.SdkLevel;
44 
45 import org.junit.After;
46 import org.junit.Test;
47 
48 import java.util.Collections;
49 import java.util.Set;
50 
51 import javax.annotation.Nullable;
52 
53 /** Tests for restricted storage-related permissions. */
54 public class RestrictedStoragePermissionTest {
55     private static final String APK_USES_STORAGE_DEFAULT_22 =
56             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserDefaultSdk22.apk";
57 
58     private static final String APK_USES_STORAGE_DEFAULT_28 =
59             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserDefaultSdk28.apk";
60 
61     private static final String APK_USES_STORAGE_DEFAULT_29 =
62             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserDefaultSdk29.apk";
63 
64     private static final String APK_USES_STORAGE_OPT_IN_22 =
65             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptInSdk22.apk";
66 
67     private static final String APK_USES_STORAGE_OPT_IN_28 =
68             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptInSdk28.apk";
69 
70     private static final String APK_USES_STORAGE_OPT_OUT_29 =
71             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptOutSdk29.apk";
72 
73     private static final String APK_USES_STORAGE_OPT_OUT_30 =
74             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptOutSdk30.apk";
75 
76     private static final String APK_USES_STORAGE_PRESERVED_OPT_OUT_30 =
77             "/data/local/tmp/cts/permissions2/CtsStoragePermissionsPreservedUserOptOutSdk30.apk";
78 
79     private static final String PKG = "android.permissionpolicy.cts.restrictedpermissionuser";
80 
81     @Test
82     @AppModeFull
testTargetingSdk22DefaultWhitelistedHasFullAccess()83     public void testTargetingSdk22DefaultWhitelistedHasFullAccess() throws Exception {
84         // Install with whitelisted permissions.
85         installApp(APK_USES_STORAGE_DEFAULT_22, null /*whitelistedPermissions*/);
86 
87         // Check expected storage mode
88         assertHasFullStorageAccess();
89     }
90 
91     @Test
92     @AppModeFull
testTargetingSdk22OptInWhitelistedHasIsolatedAccess()93     public void testTargetingSdk22OptInWhitelistedHasIsolatedAccess() throws Exception {
94         // Install with whitelisted permissions.
95         installApp(APK_USES_STORAGE_OPT_IN_22, null /*whitelistedPermissions*/);
96 
97         // Check expected storage mode
98         assertHasIsolatedStorageAccess();
99     }
100 
101     @Test
102     @AppModeFull
testTargetingSdk28DefaultWhitelistedHasFullAccess()103     public void testTargetingSdk28DefaultWhitelistedHasFullAccess() throws Exception {
104         // Install with whitelisted permissions.
105         installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
106 
107         // Check expected storage mode
108         assertHasFullStorageAccess();
109     }
110 
111     @Test
112     @AppModeFull
testTargetingSdk28OptInWhitelistedHasIsolatedAccess()113     public void testTargetingSdk28OptInWhitelistedHasIsolatedAccess() throws Exception {
114         // Install with whitelisted permissions.
115         installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
116 
117         // Check expected storage mode
118         assertHasIsolatedStorageAccess();
119     }
120 
121     @Test
122     @AppModeFull
testTargetingSdk29DefaultWhitelistedHasIsolatedAccess()123     public void testTargetingSdk29DefaultWhitelistedHasIsolatedAccess() throws Exception {
124         // Install with whitelisted permissions.
125         installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
126 
127         // Check expected storage mode
128         assertHasIsolatedStorageAccess();
129     }
130 
131     @Test
132     @AppModeFull
testTargetingSdk29DefaultNotWhitelistedHasIsolatedAccess()133     public void testTargetingSdk29DefaultNotWhitelistedHasIsolatedAccess() throws Exception {
134         // Install with no whitelisted permissions.
135         installApp(APK_USES_STORAGE_DEFAULT_29, null /*whitelistedPermissions*/);
136 
137         // Check expected storage mode
138         assertHasIsolatedStorageAccess();
139     }
140 
141     @Test
142     @AppModeFull
testTargetingSdk29OptOutWhitelistedHasFullAccess()143     public void testTargetingSdk29OptOutWhitelistedHasFullAccess() throws Exception {
144         // Install with whitelisted permissions.
145         installApp(APK_USES_STORAGE_OPT_OUT_29, null /*whitelistedPermissions*/);
146 
147         // Check expected storage mode
148         assertHasFullStorageAccess();
149     }
150 
151     @Test
152     @AppModeFull
testTargetingSdk29OptOutNotWhitelistedHasIsolatedAccess()153     public void testTargetingSdk29OptOutNotWhitelistedHasIsolatedAccess() throws Exception {
154         // Install with no whitelisted permissions.
155         installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
156 
157         // Check expected storage mode
158         assertHasIsolatedStorageAccess();
159     }
160 
161     @Test
162     @AppModeFull
testTargetingSdk29CanOptOutViaUpdate()163     public void testTargetingSdk29CanOptOutViaUpdate() throws Exception {
164         installApp(APK_USES_STORAGE_DEFAULT_29, null);
165         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
166 
167         assertHasFullStorageAccess();
168     }
169 
170     @Test
171     @AppModeFull
testTargetingSdk29CanOptOutViaDowngradeTo28()172     public void testTargetingSdk29CanOptOutViaDowngradeTo28() throws Exception {
173         installApp(APK_USES_STORAGE_DEFAULT_29, null);
174         installApp(APK_USES_STORAGE_DEFAULT_28, null);
175 
176         assertHasFullStorageAccess();
177     }
178 
179     @Test
180     @AppModeFull
testTargetingSdk30_cannotOptOut()181     public void testTargetingSdk30_cannotOptOut() throws Exception {
182         // Apps that target R and above cannot opt out of isolated storage.
183         installApp(APK_USES_STORAGE_OPT_OUT_30, null);
184 
185         // Check expected storage mode
186         assertHasIsolatedStorageAccess();
187     }
188 
189     @Test
190     @AppModeFull
testTargetingSdk28CanRemoveOptInViaUpdate()191     public void testTargetingSdk28CanRemoveOptInViaUpdate() throws Exception {
192         installApp(APK_USES_STORAGE_OPT_IN_28, null);
193         installApp(APK_USES_STORAGE_DEFAULT_28, null);
194 
195         assertHasFullStorageAccess();
196     }
197 
198     @Test
199     @AppModeFull
testTargetingSdk28CanRemoveOptInByOptingOut()200     public void testTargetingSdk28CanRemoveOptInByOptingOut() throws Exception {
201         installApp(APK_USES_STORAGE_OPT_IN_28, null);
202         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
203 
204         assertHasFullStorageAccess();
205     }
206 
207     @Test
208     @AppModeFull
testTargetingSdk28DoesNotLoseAccessWhenOptingIn()209     public void testTargetingSdk28DoesNotLoseAccessWhenOptingIn() throws Exception {
210         installApp(APK_USES_STORAGE_DEFAULT_28, null);
211         assertHasFullStorageAccess();
212         installApp(APK_USES_STORAGE_OPT_IN_28, null);
213 
214         assertHasFullStorageAccess();
215     }
216 
217     @Test
218     @AppModeFull
testTargetingSdk28DoesNotLoseAccessViaUpdate()219     public void testTargetingSdk28DoesNotLoseAccessViaUpdate() throws Exception {
220         installApp(APK_USES_STORAGE_DEFAULT_28, null);
221         assertHasFullStorageAccess();
222         installApp(APK_USES_STORAGE_DEFAULT_29, null);
223 
224         assertHasFullStorageAccess();
225     }
226 
227     @Test
228     @AppModeFull
testTargetingSdk29DoesNotLoseAccessViaUpdate()229     public void testTargetingSdk29DoesNotLoseAccessViaUpdate() throws Exception {
230         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
231         assertHasFullStorageAccess();
232         installApp(APK_USES_STORAGE_DEFAULT_29, null);
233 
234         assertHasFullStorageAccess();
235     }
236 
237     @Test
238     @AppModeFull
testTargetingSdk29DoesNotLoseAccessWhenOptingIn()239     public void testTargetingSdk29DoesNotLoseAccessWhenOptingIn() throws Exception {
240         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
241         assertHasFullStorageAccess();
242         installApp(APK_USES_STORAGE_OPT_IN_28, null);
243 
244         assertHasFullStorageAccess();
245     }
246 
247     @Test
248     @AppModeFull
testTargetingSdk29LosesAccessViaUpdateToTargetSdk30()249     public void testTargetingSdk29LosesAccessViaUpdateToTargetSdk30() throws Exception {
250         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
251         assertHasFullStorageAccess();
252 
253         installApp(APK_USES_STORAGE_OPT_OUT_30, null); // opt-out is a no-op on 30
254         assertHasIsolatedStorageAccess();
255     }
256 
257     @Test
258     @AppModeFull
testTargetingSdk28LosesAccessViaUpdateToTargetSdk30()259     public void testTargetingSdk28LosesAccessViaUpdateToTargetSdk30() throws Exception {
260         installApp(APK_USES_STORAGE_DEFAULT_28, null);
261         assertHasFullStorageAccess();
262 
263         installApp(APK_USES_STORAGE_OPT_OUT_30, null); // opt-out is a no-op on 30
264         assertHasIsolatedStorageAccess();
265     }
266 
267     @Test
268     @AppModeFull
testCannotControlStorageWhitelistPostInstall1()269     public void testCannotControlStorageWhitelistPostInstall1() throws Exception {
270         // Install with whitelisted permissions.
271         installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
272 
273         // Check expected state of restricted permissions.
274         assertCannotUnWhitelistStorage();
275     }
276 
277     @Test
278     @AppModeFull
testCannotControlStorageWhitelistPostInstall2()279     public void testCannotControlStorageWhitelistPostInstall2() throws Exception {
280         // Install with no whitelisted permissions.
281         installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
282 
283         // Check expected state of restricted permissions.
284         assertCannotWhitelistStorage();
285     }
286 
287     @Test
288     @AppModeFull
cannotGrantStorageTargetingSdk22NotWhitelisted()289     public void cannotGrantStorageTargetingSdk22NotWhitelisted() throws Exception {
290         // Install with no whitelisted permissions.
291         installApp(APK_USES_STORAGE_DEFAULT_22, Collections.emptySet());
292 
293         eventually(() -> {
294             // Could not grant permission+app-op as targetSDK<29 and not whitelisted
295             assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse();
296 
297             // Permissions are always granted for pre-23 apps
298             assertThat(isPermissionGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE))
299                     .isTrue();
300         });
301     }
302 
303     @Test
304     @AppModeFull
cannotGrantStorageTargetingSdk22OptInNotWhitelisted()305     public void cannotGrantStorageTargetingSdk22OptInNotWhitelisted() throws Exception {
306         // Install with no whitelisted permissions.
307         installApp(APK_USES_STORAGE_OPT_IN_22, Collections.emptySet());
308 
309         eventually(() -> {
310             // Could not grant permission as targetSDK<29 and not whitelisted
311             assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse();
312 
313             // Permissions are always granted for pre-23 apps
314             assertThat(isPermissionGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE))
315                     .isTrue();
316         });
317     }
318 
319     @Test
320     @AppModeFull
canGrantStorageTargetingSdk22Whitelisted()321     public void canGrantStorageTargetingSdk22Whitelisted() throws Exception {
322         // Install with whitelisted permissions.
323         installApp(APK_USES_STORAGE_DEFAULT_22, null);
324 
325         // Could grant permission
326         eventually(() ->
327                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
328     }
329 
330     @Test
331     @AppModeFull
canGrantStorageTargetingSdk22OptInWhitelisted()332     public void canGrantStorageTargetingSdk22OptInWhitelisted() throws Exception {
333         // Install with whitelisted permissions.
334         installApp(APK_USES_STORAGE_OPT_IN_22, null);
335 
336         // Could grant permission
337         eventually(() ->
338                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
339     }
340 
341     @Test
342     @AppModeFull
cannotGrantStorageTargetingSdk28NotWhitelisted()343     public void cannotGrantStorageTargetingSdk28NotWhitelisted() throws Exception {
344         // Install with no whitelisted permissions.
345         installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
346 
347         // Could not grant permission as targetSDK<29 and not whitelisted
348         eventually(() ->
349                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse());
350     }
351 
352     @Test
353     @AppModeFull
cannotGrantStorageTargetingSdk28OptInNotWhitelisted()354     public void cannotGrantStorageTargetingSdk28OptInNotWhitelisted() throws Exception {
355         // Install with no whitelisted permissions.
356         installApp(APK_USES_STORAGE_OPT_IN_28, Collections.emptySet());
357 
358         // Could not grant permission as targetSDK<29 and not whitelisted
359         eventually(() ->
360                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse());
361     }
362 
363     @Test
364     @AppModeFull
canGrantStorageTargetingSdk28Whitelisted()365     public void canGrantStorageTargetingSdk28Whitelisted() throws Exception {
366         // Install with whitelisted permissions.
367         installApp(APK_USES_STORAGE_DEFAULT_28, null);
368 
369         // Could grant permission
370         eventually(() ->
371                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
372     }
373 
374     @Test
375     @AppModeFull
canGrantStorageTargetingSdk28OptInWhitelisted()376     public void canGrantStorageTargetingSdk28OptInWhitelisted() throws Exception {
377         // Install with whitelisted permissions.
378         installApp(APK_USES_STORAGE_OPT_IN_28, null);
379 
380         // Could grant permission
381         eventually(() ->
382                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
383     }
384 
385     @Test
386     @AppModeFull
canGrantStorageTargetingSdk29NotWhitelisted()387     public void canGrantStorageTargetingSdk29NotWhitelisted() throws Exception {
388         // Install with no whitelisted permissions.
389         installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
390 
391         // Could grant permission as targetSDK=29 apps can always grant
392         eventually(() ->
393                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
394     }
395 
396     @Test
397     @AppModeFull
canGrantStorageTargetingSdk29OptOutNotWhitelisted()398     public void canGrantStorageTargetingSdk29OptOutNotWhitelisted() throws Exception {
399         // Install with no whitelisted permissions.
400         installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
401 
402         // Could grant permission as targetSDK=29 apps can always grant
403         eventually(() ->
404                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
405     }
406 
407     @Test
408     @AppModeFull
canGrantStorageTargetingSdk29Whitelisted()409     public void canGrantStorageTargetingSdk29Whitelisted() throws Exception {
410         // Install with whitelisted permissions.
411         installApp(APK_USES_STORAGE_DEFAULT_29, null);
412 
413         // Could grant permission as targetSDK=29 apps can always grant
414         eventually(() ->
415                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
416     }
417 
418     @Test
419     @AppModeFull
canGrantStorageTargetingSdk29OptOutWhitelisted()420     public void canGrantStorageTargetingSdk29OptOutWhitelisted() throws Exception {
421         // Install with whitelisted permissions.
422         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
423 
424         // Could grant permission as targetSDK=29 apps can always grant
425         eventually(() ->
426                 assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
427     }
428 
429     @Test
430     @AppModeFull
restrictedWritePermDoesNotImplyIsolatedStorageAccess()431     public void restrictedWritePermDoesNotImplyIsolatedStorageAccess() throws Exception {
432         // Install with whitelisted read permissions.
433         installApp(
434                 APK_USES_STORAGE_OPT_OUT_29,
435                 Collections.singleton(Manifest.permission.READ_EXTERNAL_STORAGE));
436 
437         // It does not matter that write is restricted as the storage access level is only
438         // controlled by the read perm
439         assertHasFullStorageAccess();
440     }
441 
442     @Test
443     @AppModeFull
whitelistedWritePermDoesNotImplyFullStorageAccess()444     public void whitelistedWritePermDoesNotImplyFullStorageAccess() throws Exception {
445         // Install with whitelisted read permissions.
446         installApp(
447                 APK_USES_STORAGE_OPT_OUT_29,
448                 Collections.singleton(Manifest.permission.WRITE_EXTERNAL_STORAGE));
449 
450         // It does not matter that write is white listed as the storage access level is only
451         // controlled by the read perm
452         assertHasIsolatedStorageAccess();
453     }
454 
455     @Test
456     @AppModeFull
testStorageTargetingSdk30CanPreserveLegacyOnUpdateFromLegacy()457     public void testStorageTargetingSdk30CanPreserveLegacyOnUpdateFromLegacy() throws Exception {
458         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
459         assertHasFullStorageAccess();
460 
461         // Updating with the flag preserves legacy
462         installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
463         assertHasFullStorageAccess();
464 
465         // And with the flag still preserves legacy
466         installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
467         assertHasFullStorageAccess();
468 
469         // But without the flag loses legacy
470         installApp(APK_USES_STORAGE_OPT_OUT_30, null);
471         assertHasIsolatedStorageAccess();
472 
473         // And again with the flag doesn't bring back legacy
474         installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
475         assertHasIsolatedStorageAccess();
476     }
477 
478     @Test
479     @AppModeFull
testStorageTargetingSdk30CannotPreserveLegacyAfterLegacyUninstall()480     public void testStorageTargetingSdk30CannotPreserveLegacyAfterLegacyUninstall()
481             throws Exception {
482         installApp(APK_USES_STORAGE_OPT_OUT_29, null);
483         assertHasFullStorageAccess();
484 
485         runShellCommand("pm uninstall " + PKG);
486 
487         installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
488         assertHasIsolatedStorageAccess();
489     }
490 
491     @Test
492     @AppModeFull
testStorageTargetingSdk30CannotPreserveLegacyOnUpdateFromNonLegacy()493     public void testStorageTargetingSdk30CannotPreserveLegacyOnUpdateFromNonLegacy()
494             throws Exception {
495         installApp(APK_USES_STORAGE_DEFAULT_29, null);
496         installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
497 
498         assertHasIsolatedStorageAccess();
499     }
500 
501     @Test
502     @AppModeFull
testStorageTargetingSdk30CannotPreserveLegacyOnInstall()503     public void testStorageTargetingSdk30CannotPreserveLegacyOnInstall() throws Exception {
504         installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
505 
506         assertHasIsolatedStorageAccess();
507     }
508 
assertHasFullStorageAccess()509     private void assertHasFullStorageAccess() throws Exception {
510         runWithShellPermissionIdentity(() -> {
511             AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
512             final int uid = getContext().getPackageManager().getPackageUid(PKG, 0);
513             eventually(() -> assertThat(appOpsManager.unsafeCheckOpRawNoThrow(
514                     AppOpsManager.OPSTR_LEGACY_STORAGE,
515                     uid, PKG)).isEqualTo(AppOpsManager.MODE_ALLOWED));
516         });
517     }
518 
assertHasIsolatedStorageAccess()519     private void assertHasIsolatedStorageAccess() throws Exception {
520         runWithShellPermissionIdentity(() -> {
521             AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
522             final int uid = getContext().getPackageManager().getPackageUid(PKG, 0);
523             eventually(() -> assertThat(appOpsManager.unsafeCheckOpRawNoThrow(
524                     AppOpsManager.OPSTR_LEGACY_STORAGE,
525                     uid, PKG)).isNotEqualTo(AppOpsManager.MODE_ALLOWED));
526         });
527     }
528 
assertCannotWhitelistStorage()529     private void assertCannotWhitelistStorage() throws Exception {
530         final PackageManager packageManager = getContext().getPackageManager();
531 
532         runWithShellPermissionIdentity(() -> {
533             // Assert added only to none whitelist.
534             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
535                     PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
536                             | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
537                             | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
538                     .doesNotContain(permission.READ_EXTERNAL_STORAGE);
539             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
540                     PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
541                             | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
542                             | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
543                     .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
544         });
545 
546         // Assert we cannot add.
547         try {
548             packageManager.addWhitelistedRestrictedPermission(
549                     PKG,
550                     permission.READ_EXTERNAL_STORAGE,
551                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
552             fail();
553         } catch (SecurityException expected) {
554         }
555         try {
556             packageManager.addWhitelistedRestrictedPermission(
557                     PKG,
558                     permission.WRITE_EXTERNAL_STORAGE,
559                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
560             fail();
561         } catch (SecurityException expected) {
562         }
563 
564         runWithShellPermissionIdentity(() -> {
565             // Assert added only to none whitelist.
566             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
567                     PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
568                             | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
569                             | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
570                     .doesNotContain(permission.READ_EXTERNAL_STORAGE);
571             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
572                     PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
573                             | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
574                             | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
575                     .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
576         });
577     }
578 
assertCannotUnWhitelistStorage()579     private void assertCannotUnWhitelistStorage() throws Exception {
580         final PackageManager packageManager = getContext().getPackageManager();
581 
582         runWithShellPermissionIdentity(() -> {
583             // Assert added only to install whitelist.
584             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
585                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
586                     .contains(permission.READ_EXTERNAL_STORAGE);
587             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
588                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
589                     .contains(permission.WRITE_EXTERNAL_STORAGE);
590             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
591                     PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
592                             | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
593                     .doesNotContain(permission.READ_EXTERNAL_STORAGE);
594             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
595                     PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
596                             | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
597                     .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
598         });
599 
600         try {
601             // Assert we cannot remove.
602             packageManager.removeWhitelistedRestrictedPermission(
603                     PKG,
604                     permission.READ_EXTERNAL_STORAGE,
605                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
606             fail();
607         } catch (SecurityException expected) {
608         }
609         try {
610             packageManager.removeWhitelistedRestrictedPermission(
611                     PKG,
612                     permission.WRITE_EXTERNAL_STORAGE,
613                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
614             fail();
615         } catch (SecurityException expected) {
616         }
617 
618         runWithShellPermissionIdentity(() -> {
619             // Assert added only to install whitelist.
620             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
621                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
622                     .contains(permission.READ_EXTERNAL_STORAGE);
623             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
624                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
625                     .contains(permission.WRITE_EXTERNAL_STORAGE);
626             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
627                     PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
628                             | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
629                     .doesNotContain(permission.READ_EXTERNAL_STORAGE);
630             assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
631                     PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
632                             | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
633                     .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
634         });
635     }
636 
getPermissionsOfAppWithAnyOfFlags(int flags)637     private @NonNull Set<String> getPermissionsOfAppWithAnyOfFlags(int flags) throws Exception {
638         final PackageManager packageManager = getContext().getPackageManager();
639         final Set<String> restrictedPermissions = new ArraySet<>();
640         for (String permission : getRequestedPermissionsOfApp()) {
641             PermissionInfo permInfo = packageManager.getPermissionInfo(permission, 0);
642 
643             if ((permInfo.flags & flags) != 0) {
644                 restrictedPermissions.add(permission);
645             }
646         }
647         return restrictedPermissions;
648     }
649 
getRestrictedPermissionsOfApp()650     private @NonNull Set<String> getRestrictedPermissionsOfApp() throws Exception {
651         return getPermissionsOfAppWithAnyOfFlags(
652                 PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED);
653     }
654 
getRequestedPermissionsOfApp()655     private @NonNull String[] getRequestedPermissionsOfApp() throws Exception {
656         final PackageManager packageManager = getContext().getPackageManager();
657         final PackageInfo packageInfo =
658                 packageManager.getPackageInfo(PKG, PackageManager.GET_PERMISSIONS);
659         return packageInfo.requestedPermissions;
660     }
661 
getContext()662     private static @NonNull Context getContext() {
663         return InstrumentationRegistry.getInstrumentation().getContext();
664     }
665 
runWithShellPermissionIdentity(@onNull ThrowingRunnable command)666     private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
667             throws Exception {
668         InstrumentationRegistry.getInstrumentation()
669                 .getUiAutomation()
670                 .adoptShellPermissionIdentity();
671         try {
672             command.run();
673         } finally {
674             InstrumentationRegistry.getInstrumentation()
675                     .getUiAutomation()
676                     .dropShellPermissionIdentity();
677         }
678     }
679 
680     /**
681      * Install an app.
682      *
683      * @param app The app to be installed
684      * @param whitelistedPermissions The permission to be whitelisted. {@code null} == all
685      * @param grantedPermissions The permission to be granted. {@code null} == all
686      */
installApp( @onNull String app, @Nullable Set<String> whitelistedPermissions)687     private void installApp(
688             @NonNull String app,
689             @Nullable Set<String> whitelistedPermissions)
690             throws Exception {
691         String bypassLowTargetSdkFlag = "";
692         if (SdkLevel.isAtLeastU()) {
693             bypassLowTargetSdkFlag = " --bypass-low-target-sdk-block";
694         }
695 
696         // Install the app and whitelist/grant all permission if requested.
697         String installResult = runShellCommand("pm install"
698                 + bypassLowTargetSdkFlag + " -t -r --restrict-permissions " + app);
699         assertThat(installResult.trim()).isEqualTo("Success");
700 
701         final Set<String> adjustedWhitelistedPermissions;
702         if (whitelistedPermissions == null) {
703             adjustedWhitelistedPermissions = getRestrictedPermissionsOfApp();
704         } else {
705             adjustedWhitelistedPermissions = whitelistedPermissions;
706         }
707 
708         final Set<String> adjustedGrantedPermissions = getRestrictedPermissionsOfApp();
709 
710         // Whitelist subset of permissions if requested
711         runWithShellPermissionIdentity(() -> {
712             final PackageManager packageManager = getContext().getPackageManager();
713             for (String permission : adjustedWhitelistedPermissions) {
714                 packageManager.addWhitelistedRestrictedPermission(
715                         PKG,
716                         permission,
717                         PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
718             }
719         });
720 
721         // Grant subset of permissions if requested
722         runWithShellPermissionIdentity(() -> {
723             final PackageManager packageManager = getContext().getPackageManager();
724             for (String permission : adjustedGrantedPermissions) {
725                 packageManager.grantRuntimePermission(PKG, permission, getContext().getUser());
726                 packageManager.updatePermissionFlags(
727                         permission,
728                         PKG,
729                         PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
730                         0,
731                         getContext().getUser());
732             }
733         });
734 
735         // Mark all permissions as reviewed as for pre-22 apps the restriction state might not be
736         // applied until reviewed
737         runWithShellPermissionIdentity(() -> {
738             final PackageManager packageManager = getContext().getPackageManager();
739             for (String permission : getRequestedPermissionsOfApp()) {
740                 packageManager.updatePermissionFlags(
741                         permission,
742                         PKG,
743                         PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
744                         0,
745                         getContext().getUser());
746             }
747         });
748     }
749 
750     @After
uninstallApp()751     public void uninstallApp() {
752         runShellCommand("pm uninstall " + PKG);
753     }
754 }
755