diff --git a/core/api/current.txt b/core/api/current.txt index fcb3a14d3cecc..32c552369e20f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7708,7 +7708,7 @@ package android.app.admin { method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy(); method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy(); method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName); - method @Nullable public CharSequence getOrganizationName(@NonNull android.content.ComponentName); + method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY) public CharSequence getOrganizationName(@Nullable android.content.ComponentName); method public java.util.List getOverrideApns(@NonNull android.content.ComponentName); method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@NonNull android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY) public int getPasswordComplexity(); @@ -7863,7 +7863,7 @@ package android.app.admin { method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean); method @Deprecated public void setOrganizationColor(@NonNull android.content.ComponentName, int); method public void setOrganizationId(@NonNull String); - method public void setOrganizationName(@NonNull android.content.ComponentName, @Nullable CharSequence); + method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY) public void setOrganizationName(@Nullable android.content.ComponentName, @Nullable CharSequence); method public void setOverrideApnsEnabled(@NonNull android.content.ComponentName, boolean); method @NonNull public String[] setPackagesSuspended(@NonNull android.content.ComponentName, @NonNull String[], boolean); method public void setPasswordExpirationTimeout(@NonNull android.content.ComponentName, long); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3fe63d8af6625..03099f8eb0e45 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -18,6 +18,7 @@ package android.app.admin; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY; import static android.Manifest.permission.QUERY_ADMIN_POLICY; import static android.Manifest.permission.SET_TIME; import static android.Manifest.permission.SET_TIME_ZONE; @@ -13485,18 +13486,24 @@ public class DevicePolicyManager { } /** - * Called by the device owner (since API 26) or profile owner (since API 24) to set the name of - * the organization under management. + * Called by the device owner (since API 26) or profile owner (since API 24) or, starting from + * Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * holders of the permission + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY} to set the + * name of the organization under management. * - *

If the organization name needs to be localized, it is the responsibility of the {@link - * DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast and set - * a new version of this string accordingly. + *

If the organization name needs to be localized, it is the responsibility of the caller + * to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast and set a new version of this + * string accordingly. * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with or can be + * {@code null} if accessing with a permission without association with a DeviceAdminReceiver. * @param title The organization name or {@code null} to clear a previously set name. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a device or profile owner or holder of the + * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY}. */ - public void setOrganizationName(@NonNull ComponentName admin, @Nullable CharSequence title) { + @RequiresPermission(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY) + public void setOrganizationName(@Nullable ComponentName admin, @Nullable CharSequence title) { throwIfParentInstance("setOrganizationName"); try { mService.setOrganizationName(admin, title); @@ -13506,14 +13513,21 @@ public class DevicePolicyManager { } /** - * Called by a profile owner of a managed profile to retrieve the name of the organization under - * management. + * Called by the device owner (since API 26) or profile owner (since API 24) or, starting from + * Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * holders of the permission + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY + * to retrieve the name of the organization under management. * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with or can be + * {@code null} if accessing with a permission without association with a DeviceAdminReceiver. * @return The organization name or {@code null} if none is set. - * @throws SecurityException if {@code admin} is not a profile owner. + * @throws SecurityException if {@code admin} if {@code admin} is not a device or profile + * owner or holder of the + * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY}. */ - public @Nullable CharSequence getOrganizationName(@NonNull ComponentName admin) { + @RequiresPermission(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY) + public @Nullable CharSequence getOrganizationName(@Nullable ComponentName admin) { throwIfParentInstance("getOrganizationName"); try { return mService.getOrganizationName(admin); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index fbde1ba1bbb14..dad5482f96b05 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY; import static android.Manifest.permission.QUERY_ADMIN_POLICY; import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY; import static android.Manifest.permission.SET_TIME; @@ -15436,16 +15437,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public void setOrganizationName(@NonNull ComponentName who, CharSequence text) { + public void setOrganizationName(@Nullable ComponentName who, CharSequence text) { if (!mHasFeature) { return; } - Objects.requireNonNull(who, "ComponentName is null"); - final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + CallerIdentity caller = getCallerIdentity(who); + ActiveAdmin admin = null; + + if (isPermissionCheckFlagEnabled()) { + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, + caller.getUserId()); + admin = enforcingAdmin.getActiveAdmin(); + } else { + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + } synchronized (getLockObject()) { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); + if (!isPermissionCheckFlagEnabled()) { + admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); + } if (!TextUtils.equals(admin.organizationName, text)) { admin.organizationName = (text == null || text.length() == 0) ? null : text.toString(); @@ -15455,20 +15467,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public CharSequence getOrganizationName(@NonNull ComponentName who) { + public CharSequence getOrganizationName(@Nullable ComponentName who) { if (!mHasFeature) { return null; } - Objects.requireNonNull(who, "ComponentName is null"); + CallerIdentity caller = getCallerIdentity(who); + ActiveAdmin admin = null; - final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallingUser(isManagedProfile(caller.getUserId())); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + if (isPermissionCheckFlagEnabled()) { + EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin( + who, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, + caller.getUserId()); + admin = enforcingAdmin.getActiveAdmin(); + } else { + Preconditions.checkCallingUser(isManagedProfile(caller.getUserId())); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); - synchronized (getLockObject()) { - ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); - return admin.organizationName; + synchronized (getLockObject()) { + admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); + } } + + return admin.organizationName; } /** @@ -20092,22 +20113,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, SET_TIME, - SET_TIME_ZONE); + SET_TIME_ZONE, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY); private static final List FINANCED_DEVICE_OWNER_PERMISSIONS = List.of( MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL, MANAGE_DEVICE_POLICY_ACROSS_USERS, - MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); + MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY); private static final List PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS = List.of( MANAGE_DEVICE_POLICY_ACROSS_USERS, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, SET_TIME, - SET_TIME_ZONE); + SET_TIME_ZONE, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY); private static final List PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of( SET_TIME, SET_TIME_ZONE); private static final List PROFILE_OWNER_PERMISSIONS = List.of( - MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); + MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY); private static final HashMap> DPC_PERMISSIONS = new HashMap<>(); { @@ -20125,9 +20150,48 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final HashMap CROSS_USER_PERMISSIONS = new HashMap<>(); { - // Auto time is intrinsically global so there is no cross-user permission. + // Time and Timezone is intrinsically global so there is no cross-user permission. CROSS_USER_PERMISSIONS.put(SET_TIME, null); CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null); + // Organisation identity policy will involve data of other organisations on the device and + // therefore the FULL cross-user permission is required. + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + } + + /** + * Checks if the calling process has been granted permission to apply a device policy on a + * specific user. + * The given permission will be checked along with its associated cross-user permission if it + * exists and the target user is different to the calling user. + * Returns the {@link ActiveAdmin} of the caller. + * + * @param permission The name of the permission being checked. + * @param targetUserId The userId of the user which the caller needs permission to act on. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin, + String permission, int targetUserId) { + enforcePermission(permission, targetUserId); + return getEnforcingAdminForCaller(admin, getCallerIdentity()); + } + + /** + * Checks whether the calling process has been granted permission to query a device policy on + * a specific user. + * The given permission will be checked along with its associated cross-user permission if it + * exists and the target user is different to the calling user. + * + * @param permission The name of the permission being checked. + * @param targetUserId The userId of the user which the caller needs permission to act on. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private EnforcingAdmin enforceCanQueryAndGetEnforcingAdmin(@Nullable ComponentName admin, + String permission, int targetUserId) { + enforceCanQuery(permission, targetUserId); + return getEnforcingAdminForCaller(admin, getCallerIdentity()); } /** @@ -20139,7 +20203,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * @param permission The name of the permission being checked. * @param targetUserId The userId of the user which the caller needs permission to act on. * @throws SecurityException if the caller has not been granted the given permission, - * the associtated cross-user permission if the caller's user is different to the target user. + * the associated cross-user permission if the caller's user is different to the target user. */ private void enforcePermission(String permission, int targetUserId) throws SecurityException { @@ -20154,13 +20218,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Return whether the calling process has been granted permission to query a device policy on + * Checks whether the calling process has been granted permission to query a device policy on * a specific user. + * The given permission will be checked along with its associated cross-user permission if it + * exists and the target user is different to the calling user. * * @param permission The name of the permission being checked. * @param targetUserId The userId of the user which the caller needs permission to act on. * @throws SecurityException if the caller has not been granted the given permission, - * the associatated cross-user permission if the caller's user is different to the target user + * the associated cross-user permission if the caller's user is different to the target user * and if the user has not been granted {@link QUERY_ADMIN_POLICY}. */ private void enforceCanQuery(String permission, int targetUserId) throws SecurityException { @@ -20225,10 +20291,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Check if the caller is an active admin that uses a certain policy. if (ACTIVE_ADMIN_POLICIES.containsKey(permission)) { - return getActiveAdminForCallerLocked( - null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null; + try { + return getActiveAdminForCallerLocked( + null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null; + } catch (SecurityException e) { + // A security exception means there is not an active admin with permission and + // therefore + return false; + } } - return false; } @@ -20258,6 +20329,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } + private EnforcingAdmin getEnforcingAdminForCaller(@Nullable ComponentName who, + CallerIdentity caller) { + int userId = caller.getUserId(); + ActiveAdmin admin = null; + synchronized (getLockObject()) { + admin = getActiveAdminUncheckedLocked(who, userId); + } + if (isDeviceOwner(caller) || isProfileOwner(caller)) { + return EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userId, admin); + } + if (getActiveAdminUncheckedLocked(who, userId) != null) { + return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin); + } + return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId); + } + private boolean isPermissionCheckFlagEnabled() { return DeviceConfig.getBoolean( NAMESPACE_DEVICE_POLICY_MANAGER, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java index da895f46f1aae..641b30ae356f5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java @@ -63,6 +63,7 @@ final class EnforcingAdmin { private Set mAuthorities; private final int mUserId; private final boolean mIsRoleAuthority; + private final ActiveAdmin mActiveAdmin; static EnforcingAdmin createEnforcingAdmin(@NonNull String packageName, int userId) { Objects.requireNonNull(packageName); @@ -73,14 +74,31 @@ final class EnforcingAdmin { @NonNull ComponentName componentName, int userId) { Objects.requireNonNull(componentName); return new EnforcingAdmin( - componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId); + componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId, + /* activeAdmin=*/ null); + } + + static EnforcingAdmin createEnterpriseEnforcingAdmin( + @NonNull ComponentName componentName, int userId, ActiveAdmin activeAdmin) { + Objects.requireNonNull(componentName); + return new EnforcingAdmin( + componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId, + activeAdmin); } static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId) { Objects.requireNonNull(componentName); return new EnforcingAdmin( componentName.getPackageName(), componentName, Set.of(DEVICE_ADMIN_AUTHORITY), - userId); + userId, /* activeAdmin=*/ null); + } + + static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId, + ActiveAdmin activeAdmin) { + Objects.requireNonNull(componentName); + return new EnforcingAdmin( + componentName.getPackageName(), componentName, Set.of(DEVICE_ADMIN_AUTHORITY), + userId, activeAdmin); } static String getRoleAuthorityOf(String roleName) { @@ -88,7 +106,8 @@ final class EnforcingAdmin { } private EnforcingAdmin( - String packageName, ComponentName componentName, Set authorities, int userId) { + String packageName, ComponentName componentName, Set authorities, int userId, + ActiveAdmin activeAdmin) { Objects.requireNonNull(packageName); Objects.requireNonNull(componentName); Objects.requireNonNull(authorities); @@ -99,6 +118,7 @@ final class EnforcingAdmin { mComponentName = componentName; mAuthorities = new HashSet<>(authorities); mUserId = userId; + mActiveAdmin = activeAdmin; } private EnforcingAdmin(String packageName, int userId) { @@ -111,6 +131,7 @@ final class EnforcingAdmin { mComponentName = null; // authorities will be loaded when needed mAuthorities = null; + mActiveAdmin = null; } private static Set getRoleAuthoritiesOrDefault(String packageName, int userId) { @@ -156,6 +177,11 @@ final class EnforcingAdmin { return mUserId; } + @Nullable + public ActiveAdmin getActiveAdmin() { + return mActiveAdmin; + } + /** * For two EnforcingAdmins to be equal they must: * @@ -224,7 +250,7 @@ final class EnforcingAdmin { String className = parser.getAttributeValue(/* namespace= */ null, ATTR_CLASS_NAME); ComponentName componentName = new ComponentName(packageName, className); Set authorities = Set.of(authoritiesStr.split(ATTR_AUTHORITIES_SEPARATOR)); - return new EnforcingAdmin(packageName, componentName, authorities, userId); + return new EnforcingAdmin(packageName, componentName, authorities, userId, null); } }