blob: de7e36e8a4364fd511839ae86e01c167241a4290 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.permission2.cts;
import static android.permission.cts.PermissionUtils.isGranted;
import static android.permission.cts.PermissionUtils.isPermissionGranted;
import static com.android.compatibility.common.util.SystemUtil.eventually;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.Manifest;
import android.Manifest.permission;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.platform.test.annotations.AppModeFull;
import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.SystemUtil.ThrowingRunnable;
import org.junit.After;
import org.junit.Test;
import java.util.Collections;
import java.util.Set;
import javax.annotation.Nullable;
/** Tests for restricted storage-related permissions. */
public class RestrictedStoragePermissionTest {
private static final String APK_USES_STORAGE_DEFAULT_22 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserDefaultSdk22.apk";
private static final String APK_USES_STORAGE_DEFAULT_28 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserDefaultSdk28.apk";
private static final String APK_USES_STORAGE_DEFAULT_29 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserDefaultSdk29.apk";
private static final String APK_USES_STORAGE_OPT_IN_22 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptInSdk22.apk";
private static final String APK_USES_STORAGE_OPT_IN_28 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptInSdk28.apk";
private static final String APK_USES_STORAGE_OPT_OUT_29 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptOutSdk29.apk";
private static final String APK_USES_STORAGE_OPT_OUT_30 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptOutSdk30.apk";
private static final String APK_USES_STORAGE_PRESERVED_OPT_OUT_30 =
"/data/local/tmp/cts/permissions2/CtsStoragePermissionsPreservedUserOptOutSdk30.apk";
private static final String PKG = "android.permission2.cts.restrictedpermissionuser";
@Test
@AppModeFull
public void testTargetingSdk22DefaultWhitelistedHasFullAccess() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_22, null /*whitelistedPermissions*/);
// Check expected storage mode
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk22OptInWhitelistedHasIsolatedAccess() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_IN_22, null /*whitelistedPermissions*/);
// Check expected storage mode
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28DefaultWhitelistedHasFullAccess() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
// Check expected storage mode
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28OptInWhitelistedHasIsolatedAccess() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
// Check expected storage mode
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29DefaultWhitelistedHasIsolatedAccess() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
// Check expected storage mode
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29DefaultNotWhitelistedHasIsolatedAccess() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_29, null /*whitelistedPermissions*/);
// Check expected storage mode
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29OptOutWhitelistedHasFullAccess() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_OUT_29, null /*whitelistedPermissions*/);
// Check expected storage mode
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29OptOutNotWhitelistedHasIsolatedAccess() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
// Check expected storage mode
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29CanOptOutViaUpdate() throws Exception {
installApp(APK_USES_STORAGE_DEFAULT_29, null);
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29CanOptOutViaDowngradeTo28() throws Exception {
installApp(APK_USES_STORAGE_DEFAULT_29, null);
installApp(APK_USES_STORAGE_DEFAULT_28, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk30_cannotOptOut() throws Exception {
// Apps that target R and above cannot opt out of isolated storage.
installApp(APK_USES_STORAGE_OPT_OUT_30, null);
// Check expected storage mode
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28CanRemoveOptInViaUpdate() throws Exception {
installApp(APK_USES_STORAGE_OPT_IN_28, null);
installApp(APK_USES_STORAGE_DEFAULT_28, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28CanRemoveOptInByOptingOut() throws Exception {
installApp(APK_USES_STORAGE_OPT_IN_28, null);
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28DoesNotLoseAccessWhenOptingIn() throws Exception {
installApp(APK_USES_STORAGE_DEFAULT_28, null);
assertHasFullStorageAccess();
installApp(APK_USES_STORAGE_OPT_IN_28, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28DoesNotLoseAccessViaUpdate() throws Exception {
installApp(APK_USES_STORAGE_DEFAULT_28, null);
assertHasFullStorageAccess();
installApp(APK_USES_STORAGE_DEFAULT_29, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29DoesNotLoseAccessViaUpdate() throws Exception {
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
installApp(APK_USES_STORAGE_DEFAULT_29, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29DoesNotLoseAccessWhenOptingIn() throws Exception {
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
installApp(APK_USES_STORAGE_OPT_IN_28, null);
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk29LosesAccessViaUpdateToTargetSdk30() throws Exception {
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
installApp(APK_USES_STORAGE_OPT_OUT_30, null); // opt-out is a no-op on 30
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testTargetingSdk28LosesAccessViaUpdateToTargetSdk30() throws Exception {
installApp(APK_USES_STORAGE_DEFAULT_28, null);
assertHasFullStorageAccess();
installApp(APK_USES_STORAGE_OPT_OUT_30, null); // opt-out is a no-op on 30
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testCannotControlStorageWhitelistPostInstall1() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
// Check expected state of restricted permissions.
assertCannotUnWhitelistStorage();
}
@Test
@AppModeFull
public void testCannotControlStorageWhitelistPostInstall2() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
// Check expected state of restricted permissions.
assertCannotWhitelistStorage();
}
@Test
@AppModeFull
public void cannotGrantStorageTargetingSdk22NotWhitelisted() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_22, Collections.emptySet());
eventually(() -> {
// Could not grant permission+app-op as targetSDK<29 and not whitelisted
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse();
// Permissions are always granted for pre-23 apps
assertThat(isPermissionGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE))
.isTrue();
});
}
@Test
@AppModeFull
public void cannotGrantStorageTargetingSdk22OptInNotWhitelisted() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_IN_22, Collections.emptySet());
eventually(() -> {
// Could not grant permission as targetSDK<29 and not whitelisted
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse();
// Permissions are always granted for pre-23 apps
assertThat(isPermissionGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE))
.isTrue();
});
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk22Whitelisted() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_22, null);
// Could grant permission
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk22OptInWhitelisted() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_IN_22, null);
// Could grant permission
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void cannotGrantStorageTargetingSdk28NotWhitelisted() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
// Could not grant permission as targetSDK<29 and not whitelisted
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse());
}
@Test
@AppModeFull
public void cannotGrantStorageTargetingSdk28OptInNotWhitelisted() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_IN_28, Collections.emptySet());
// Could not grant permission as targetSDK<29 and not whitelisted
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk28Whitelisted() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_28, null);
// Could grant permission
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk28OptInWhitelisted() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_IN_28, null);
// Could grant permission
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk29NotWhitelisted() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
// Could grant permission as targetSDK=29 apps can always grant
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk29OptOutNotWhitelisted() throws Exception {
// Install with no whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
// Could grant permission as targetSDK=29 apps can always grant
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk29Whitelisted() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_DEFAULT_29, null);
// Could grant permission as targetSDK=29 apps can always grant
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void canGrantStorageTargetingSdk29OptOutWhitelisted() throws Exception {
// Install with whitelisted permissions.
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
// Could grant permission as targetSDK=29 apps can always grant
eventually(() ->
assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
}
@Test
@AppModeFull
public void restrictedWritePermDoesNotImplyIsolatedStorageAccess() throws Exception {
// Install with whitelisted read permissions.
installApp(
APK_USES_STORAGE_OPT_OUT_29,
Collections.singleton(Manifest.permission.READ_EXTERNAL_STORAGE));
// It does not matter that write is restricted as the storage access level is only
// controlled by the read perm
assertHasFullStorageAccess();
}
@Test
@AppModeFull
public void whitelistedWritePermDoesNotImplyFullStorageAccess() throws Exception {
// Install with whitelisted read permissions.
installApp(
APK_USES_STORAGE_OPT_OUT_29,
Collections.singleton(Manifest.permission.WRITE_EXTERNAL_STORAGE));
// It does not matter that write is white listed as the storage access level is only
// controlled by the read perm
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testStorageTargetingSdk30CanPreserveLegacyOnUpdateFromLegacy() throws Exception {
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
// Updating with the flag preserves legacy
installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
assertHasFullStorageAccess();
// And with the flag still preserves legacy
installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
assertHasFullStorageAccess();
// But without the flag loses legacy
installApp(APK_USES_STORAGE_OPT_OUT_30, null);
assertHasIsolatedStorageAccess();
// And again with the flag doesn't bring back legacy
installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testStorageTargetingSdk30CannotPreserveLegacyAfterLegacyUninstall()
throws Exception {
installApp(APK_USES_STORAGE_OPT_OUT_29, null);
assertHasFullStorageAccess();
runShellCommand("pm uninstall " + PKG);
installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testStorageTargetingSdk30CannotPreserveLegacyOnUpdateFromNonLegacy()
throws Exception {
installApp(APK_USES_STORAGE_DEFAULT_29, null);
installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
assertHasIsolatedStorageAccess();
}
@Test
@AppModeFull
public void testStorageTargetingSdk30CannotPreserveLegacyOnInstall() throws Exception {
installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
assertHasIsolatedStorageAccess();
}
private void assertHasFullStorageAccess() throws Exception {
runWithShellPermissionIdentity(() -> {
AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
final int uid = getContext().getPackageManager().getPackageUid(PKG, 0);
eventually(() -> assertThat(appOpsManager.unsafeCheckOpRawNoThrow(
AppOpsManager.OPSTR_LEGACY_STORAGE,
uid, PKG)).isEqualTo(AppOpsManager.MODE_ALLOWED));
});
}
private void assertHasIsolatedStorageAccess() throws Exception {
runWithShellPermissionIdentity(() -> {
AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
final int uid = getContext().getPackageManager().getPackageUid(PKG, 0);
eventually(() -> assertThat(appOpsManager.unsafeCheckOpRawNoThrow(
AppOpsManager.OPSTR_LEGACY_STORAGE,
uid, PKG)).isNotEqualTo(AppOpsManager.MODE_ALLOWED));
});
}
private void assertCannotWhitelistStorage() throws Exception {
final PackageManager packageManager = getContext().getPackageManager();
runWithShellPermissionIdentity(() -> {
// Assert added only to none whitelist.
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.doesNotContain(permission.READ_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
});
// Assert we cannot add.
try {
packageManager.addWhitelistedRestrictedPermission(
PKG,
permission.READ_EXTERNAL_STORAGE,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
fail();
} catch (SecurityException expected) {
}
try {
packageManager.addWhitelistedRestrictedPermission(
PKG,
permission.WRITE_EXTERNAL_STORAGE,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
fail();
} catch (SecurityException expected) {
}
runWithShellPermissionIdentity(() -> {
// Assert added only to none whitelist.
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.doesNotContain(permission.READ_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
});
}
private void assertCannotUnWhitelistStorage() throws Exception {
final PackageManager packageManager = getContext().getPackageManager();
runWithShellPermissionIdentity(() -> {
// Assert added only to install whitelist.
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.contains(permission.READ_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.contains(permission.WRITE_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
.doesNotContain(permission.READ_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
.doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
});
try {
// Assert we cannot remove.
packageManager.removeWhitelistedRestrictedPermission(
PKG,
permission.READ_EXTERNAL_STORAGE,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
fail();
} catch (SecurityException expected) {
}
try {
packageManager.removeWhitelistedRestrictedPermission(
PKG,
permission.WRITE_EXTERNAL_STORAGE,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
fail();
} catch (SecurityException expected) {
}
runWithShellPermissionIdentity(() -> {
// Assert added only to install whitelist.
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.contains(permission.READ_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
.contains(permission.WRITE_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
.doesNotContain(permission.READ_EXTERNAL_STORAGE);
assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
.doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
});
}
private @NonNull Set<String> getPermissionsOfAppWithAnyOfFlags(int flags) throws Exception {
final PackageManager packageManager = getContext().getPackageManager();
final Set<String> restrictedPermissions = new ArraySet<>();
for (String permission : getRequestedPermissionsOfApp()) {
PermissionInfo permInfo = packageManager.getPermissionInfo(permission, 0);
if ((permInfo.flags & flags) != 0) {
restrictedPermissions.add(permission);
}
}
return restrictedPermissions;
}
private @NonNull Set<String> getRestrictedPermissionsOfApp() throws Exception {
return getPermissionsOfAppWithAnyOfFlags(
PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED);
}
private @NonNull String[] getRequestedPermissionsOfApp() throws Exception {
final PackageManager packageManager = getContext().getPackageManager();
final PackageInfo packageInfo =
packageManager.getPackageInfo(PKG, PackageManager.GET_PERMISSIONS);
return packageInfo.requestedPermissions;
}
private static @NonNull Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
throws Exception {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity();
try {
command.runOrThrow();
} finally {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.dropShellPermissionIdentity();
}
}
/**
* Install an app.
*
* @param app The app to be installed
* @param whitelistedPermissions The permission to be whitelisted. {@code null} == all
* @param grantedPermissions The permission to be granted. {@code null} == all
*/
private void installApp(
@NonNull String app,
@Nullable Set<String> whitelistedPermissions)
throws Exception {
// Install the app and whitelist/grant all permission if requested.
String installResult = runShellCommand("pm install -r --restrict-permissions " + app);
assertThat(installResult.trim()).isEqualTo("Success");
final Set<String> adjustedWhitelistedPermissions;
if (whitelistedPermissions == null) {
adjustedWhitelistedPermissions = getRestrictedPermissionsOfApp();
} else {
adjustedWhitelistedPermissions = whitelistedPermissions;
}
final Set<String> adjustedGrantedPermissions = getRestrictedPermissionsOfApp();
// Whitelist subset of permissions if requested
runWithShellPermissionIdentity(() -> {
final PackageManager packageManager = getContext().getPackageManager();
for (String permission : adjustedWhitelistedPermissions) {
packageManager.addWhitelistedRestrictedPermission(
PKG,
permission,
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
}
});
// Grant subset of permissions if requested
runWithShellPermissionIdentity(() -> {
final PackageManager packageManager = getContext().getPackageManager();
for (String permission : adjustedGrantedPermissions) {
packageManager.grantRuntimePermission(PKG, permission, getContext().getUser());
packageManager.updatePermissionFlags(
permission,
PKG,
PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
0,
getContext().getUser());
}
});
// Mark all permissions as reviewed as for pre-22 apps the restriction state might not be
// applied until reviewed
runWithShellPermissionIdentity(() -> {
final PackageManager packageManager = getContext().getPackageManager();
for (String permission : getRequestedPermissionsOfApp()) {
packageManager.updatePermissionFlags(
permission,
PKG,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
0,
getContext().getUser());
}
});
}
@After
public void uninstallApp() {
runShellCommand("pm uninstall " + PKG);
}
}