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