diff options
| author | 2021-03-04 15:32:36 -0800 | |
|---|---|---|
| committer | 2021-03-11 15:20:13 -0800 | |
| commit | 4691ce920a25a05454b415634c884e0fbd316b7c (patch) | |
| tree | 890eafb17c2ff4706bb2a6fe34dbf9efe78e066c | |
| parent | e73587d10d05d2f4a92bf3a6b7bdb2da2f4a38dc (diff) | |
Created new @TestApi (and Shell command) to get policy-exempt apps.
Test: adb shell cmd device_policy list-policy-exempt-apps
Test: atest FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest#testGetPolicyExemptApps_noPermission,testGetPolicyExemptApps_empty,testGetPolicyExemptApps_baseOnly,testGetPolicyExemptApps_vendorOnly,testGetPolicyExemptApps_baseAndVendor
Bug: 181238156
Fixes: 182373142
Change-Id: I990c53d1a2b9a6e15ad9fcae310badb7aae834d1
6 files changed, 135 insertions, 11 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 7b5b1989c1e5..f1cc45f2aa0f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -414,6 +414,7 @@ package android.app.admin { method public long getLastNetworkLogRetrievalTime(); method public long getLastSecurityLogRetrievalTime(); method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle); + method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps(); method public boolean isCurrentInputMethodSetByOwner(); method public boolean isFactoryResetProtectionPolicySupported(); method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ccf41e5f3063..08e670fae2ec 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -13725,4 +13725,22 @@ public class DevicePolicyManager { throw re.rethrowFromSystemServer(); } } + + /** + * Lists apps that are exempt from policies (such as + * {@link #setPackagesSuspended(ComponentName, String[], boolean)}). + * + * @hide + */ + @TestApi + @RequiresPermission(value = android.Manifest.permission.MANAGE_DEVICE_ADMINS) + public @NonNull Set<String> getPolicyExemptApps() { + if (mService == null) return Collections.emptySet(); + + try { + return new HashSet<>(mService.listPolicyExemptApps()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 25ca59963d4b..e98720c0d96c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -177,6 +177,7 @@ interface IDevicePolicyManager { String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended); boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName); + List<String> listPolicyExemptApps(); boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer); void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 92bb51f18287..44b06c11c568 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1588,7 +1588,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { CryptoTestHelper.runAndLogSelfTest(); } - public String[] getPersonalAppsForSuspension(int userId) { + public String[] getPersonalAppsForSuspension(@UserIdInt int userId) { return PersonalAppsSuspensionHelper.forUser(mContext, userId) .getPersonalAppsForSuspension(); } @@ -10614,6 +10614,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public List<String> listPolicyExemptApps() { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + + // TODO(b/181238156): decide whether it should only list the apps set by the resources, + // or also the "critical" apps defined by PersonalAppsSuspensionHelper (like SMS app). + // If it's the latter, refactor PersonalAppsSuspensionHelper so it (or a superclass) takes + // the resources on constructor. + String[] core = mContext.getResources().getStringArray(R.array.policy_exempt_apps); + String[] vendor = mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps); + + int size = core.length + vendor.length; + Set<String> apps = new ArraySet<>(size); + for (String app : core) { + apps.add(app); + } + for (String app : vendor) { + apps.add(app); + } + + return new ArrayList<>(apps); + } + + @Override public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner, boolean parent) { Objects.requireNonNull(who, "ComponentName is null"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index 5484a148b0b6..8e31029769d0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -21,6 +21,7 @@ import android.os.ShellCommand; import com.android.server.devicepolicy.Owners.OwnerDto; import java.io.PrintWriter; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -30,6 +31,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason"; private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe"; private static final String CMD_LIST_OWNERS = "list-owners"; + private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps"; private final DevicePolicyManagerService mService; @@ -60,6 +62,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return runSetSafeOperation(pw); case CMD_LIST_OWNERS: return runListOwners(pw); + case CMD_LIST_POLICY_EXEMPT_APPS: + return runListPolicyExemptApps(pw); default: return onInvalidCommand(pw, cmd); } @@ -88,6 +92,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { + " \n\n"); pw.printf(" %s\n", CMD_LIST_OWNERS); pw.printf(" Lists the device / profile owners per user \n\n"); + pw.printf(" %s\n", CMD_LIST_POLICY_EXEMPT_APPS); + pw.printf(" Lists the apps that are exempt from policies\n\n"); } private int runIsSafeOperation(PrintWriter pw) { @@ -119,18 +125,20 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return 0; } - private int runListOwners(PrintWriter pw) { - List<OwnerDto> owners = mService.listAllOwners(); - if (owners.isEmpty()) { - pw.println("none"); + private int printAndGetSize(PrintWriter pw, Collection<?> collection, String nameOnSingular) { + if (collection.isEmpty()) { + pw.printf("no %ss\n", nameOnSingular); return 0; } - int size = owners.size(); - if (size == 1) { - pw.println("1 owner:"); - } else { - pw.printf("%d owners:\n", size); - } + int size = collection.size(); + pw.printf("%d %s%s:\n", size, nameOnSingular, (size == 1 ? "" : "s")); + return size; + } + + private int runListOwners(PrintWriter pw) { + List<OwnerDto> owners = mService.listAllOwners(); + int size = printAndGetSize(pw, owners, "owner"); + if (size == 0) return 0; for (int i = 0; i < size; i++) { OwnerDto owner = owners.get(i); @@ -150,4 +158,17 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return 0; } + + private int runListPolicyExemptApps(PrintWriter pw) { + List<String> apps = mService.listPolicyExemptApps(); + int size = printAndGetSize(pw, apps, "policy exempt app"); + + if (size == 0) return 0; + + for (int i = 0; i < size; i++) { + String app = apps.get(i); + pw.printf(" %d: %s\n", i, app); + } + return 0; + } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 87100a63e35e..318533f1ef63 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -107,6 +107,7 @@ import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth import android.util.ArraySet; +import android.util.Log; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -154,6 +155,9 @@ import java.util.concurrent.TimeUnit; @SmallTest @Presubmit public class DevicePolicyManagerTest extends DpmTestBase { + + private static final String TAG = DevicePolicyManagerTest.class.getSimpleName(); + private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList( permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL); @@ -7215,6 +7219,47 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled); } + @Test + public void testGetPolicyExemptApps_noPermission() { + assertThrows(SecurityException.class, () -> dpm.getPolicyExemptApps()); + } + + @Test + public void testGetPolicyExemptApps_empty() { + grantManageDeviceAdmins(); + mockPolicyExemptApps(); + mockVendorPolicyExemptApps(); + + assertThat(dpm.getPolicyExemptApps()).isEmpty(); + } + + @Test + public void testGetPolicyExemptApps_baseOnly() { + grantManageDeviceAdmins(); + mockPolicyExemptApps("foo"); + mockVendorPolicyExemptApps(); + + assertThat(dpm.getPolicyExemptApps()).containsExactly("foo"); + } + + @Test + public void testGetPolicyExemptApps_vendorOnly() { + grantManageDeviceAdmins(); + mockPolicyExemptApps(); + mockVendorPolicyExemptApps("bar"); + + assertThat(dpm.getPolicyExemptApps()).containsExactly("bar"); + } + + @Test + public void testGetPolicyExemptApps_baseAndVendor() { + grantManageDeviceAdmins(); + mockPolicyExemptApps("4", "23", "15", "42", "8"); + mockVendorPolicyExemptApps("16", "15", "4"); + + assertThat(dpm.getPolicyExemptApps()).containsExactly("4", "8", "15", "16", "23", "42"); + } + private void setUserUnlocked(int userHandle, boolean unlocked) { when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked); } @@ -7436,4 +7481,18 @@ public class DevicePolicyManagerTest extends DpmTestBase { return new StringParceledListSlice(Arrays.asList(s)); } + private void grantManageDeviceAdmins() { + Log.d(TAG, "Granting " + permission.MANAGE_DEVICE_ADMINS); + mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); + } + + private void mockPolicyExemptApps(String... apps) { + Log.d(TAG, "Mocking R.array.policy_exempt_apps to return " + Arrays.toString(apps)); + when(mContext.resources.getStringArray(R.array.policy_exempt_apps)).thenReturn(apps); + } + + private void mockVendorPolicyExemptApps(String... apps) { + Log.d(TAG, "Mocking R.array.vendor_policy_exempt_apps to return " + Arrays.toString(apps)); + when(mContext.resources.getStringArray(R.array.vendor_policy_exempt_apps)).thenReturn(apps); + } } |