summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Elis Elliott <eliselliott@google.com> 2023-01-04 13:16:33 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-01-04 13:16:33 +0000
commit05bda46809a7e8ef93e66e32b1317dc80674effc (patch)
tree977e51022309d9f9bf36eac39df6d101c0d668e8
parent5d7d1378dfc94b23551101dd6a02c402eef74b4a (diff)
parent10cee472fb23c185c8d0b20f9123ab15f4fd7535 (diff)
Merge changes from topic "create_manage_cross_user_permissions"
* changes: Add permission checks for auto time APIs Create permission check methods in DPMS Create new permissions for managing policies across users.
-rw-r--r--core/api/current.txt16
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java75
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java25
-rw-r--r--core/res/AndroidManifest.xml28
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java291
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java18
6 files changed, 409 insertions, 44 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 326a8e781a60..878a63ec200b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -122,6 +122,10 @@ package android {
field public static final String LOADER_USAGE_STATS = "android.permission.LOADER_USAGE_STATS";
field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final String MANAGE_DEVICE_LOCK_STATE = "android.permission.MANAGE_DEVICE_LOCK_STATE";
+ field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS";
+ field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL";
+ field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL";
+ field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME";
field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
field public static final String MANAGE_MEDIA = "android.permission.MANAGE_MEDIA";
@@ -7499,9 +7503,9 @@ package android.app.admin {
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
- method public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean getAutoTimeRequired();
- method public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
@@ -7648,9 +7652,9 @@ package android.app.admin {
method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean);
+ method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean);
method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean);
- method public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean);
+ method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean);
method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
@@ -7729,8 +7733,8 @@ package android.app.admin {
method @Deprecated public int setStorageEncryption(@NonNull android.content.ComponentName, boolean);
method public void setSystemSetting(@NonNull android.content.ComponentName, @NonNull String, String);
method public void setSystemUpdatePolicy(@NonNull android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
- method public boolean setTime(@NonNull android.content.ComponentName, long);
- method public boolean setTimeZone(@NonNull android.content.ComponentName, String);
+ method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public boolean setTime(@NonNull android.content.ComponentName, long);
+ method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public boolean setTimeZone(@NonNull android.content.ComponentName, String);
method public void setTrustAgentConfiguration(@NonNull android.content.ComponentName, @NonNull android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(@Nullable android.content.ComponentName, String, boolean);
method public void setUsbDataSignalingEnabled(boolean);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e729e7d8f9be..585c014baf25 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,9 @@
package android.app.admin;
+import static android.Manifest.permission.QUERY_ADMIN_POLICY;
+import static android.Manifest.permission.SET_TIME;
+import static android.Manifest.permission.SET_TIME_ZONE;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
@@ -8466,8 +8469,10 @@ public class DevicePolicyManager {
}
/**
- * Called by a device owner, a profile owner for the primary user or a profile
- * owner of an organization-owned managed profile to turn auto time on and off.
+ * Called by a device owner, a profile owner for the primary user, a profile
+ * owner of an organization-owned managed profile or, starting from Android
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, holders of the permission
+ * {@link android.Manifest.permission#SET_TIME} to turn auto time on and off.
* Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
* to prevent the user from changing this setting.
* <p>
@@ -8478,8 +8483,10 @@ public class DevicePolicyManager {
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled Whether time should be obtained automatically from the network or not.
* @throws SecurityException if caller is not a device owner, a profile owner for the
- * primary user, or a profile owner of an organization-owned managed profile.
+ * primary user, or a profile owner of an organization-owned managed profile or a holder of the
+ * permission {@link android.Manifest.permission#SET_TIME}.
*/
+ @RequiresPermission(value = SET_TIME, conditional = true)
public void setAutoTimeEnabled(@NonNull ComponentName admin, boolean enabled) {
if (mService != null) {
try {
@@ -8491,10 +8498,18 @@ public class DevicePolicyManager {
}
/**
+ * Returns true if auto time is enabled on the device.
+ *
+ * <p> Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, callers
+ * are also able to call this method if they hold the permission
+ *{@link android.Manifest.permission#SET_TIME}.
+ *
* @return true if auto time is enabled on the device.
- * @throws SecurityException if caller is not a device owner, a profile owner for the
- * primary user, or a profile owner of an organization-owned managed profile.
+ * @throws SecurityException if the caller is not a device owner, a profile
+ * owner for the primary user, or a profile owner of an organization-owned managed profile or a
+ * holder of the permission {@link android.Manifest.permission#SET_TIME}.
*/
+ @RequiresPermission(anyOf = {SET_TIME, QUERY_ADMIN_POLICY}, conditional = true)
public boolean getAutoTimeEnabled(@NonNull ComponentName admin) {
if (mService != null) {
try {
@@ -8507,8 +8522,10 @@ public class DevicePolicyManager {
}
/**
- * Called by a device owner, a profile owner for the primary user or a profile
- * owner of an organization-owned managed profile to turn auto time zone on and off.
+ * Called by a device owner, a profile owner for the primary user, a profile
+ * owner of an organization-owned managed profile or, starting from Android
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, holders of the permission
+ * {@link android.Manifest.permission#SET_TIME} to turn auto time zone on and off.
* Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
* to prevent the user from changing this setting.
* <p>
@@ -8519,8 +8536,10 @@ public class DevicePolicyManager {
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled Whether time zone should be obtained automatically from the network or not.
* @throws SecurityException if caller is not a device owner, a profile owner for the
- * primary user, or a profile owner of an organization-owned managed profile.
+ * primary user, or a profile owner of an organization-owned managed profile or a holder of the
+ * permission {@link android.Manifest.permission#SET_TIME_ZONE}.
*/
+ @RequiresPermission(value = SET_TIME_ZONE, conditional = true)
public void setAutoTimeZoneEnabled(@NonNull ComponentName admin, boolean enabled) {
throwIfParentInstance("setAutoTimeZone");
if (mService != null) {
@@ -8533,10 +8552,18 @@ public class DevicePolicyManager {
}
/**
+ * Returns true if auto time zone is enabled on the device.
+ *
+ * <p> Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, callers
+ * are also able to call this method if they hold the permission
+ *{@link android.Manifest.permission#SET_TIME}.
+ *
* @return true if auto time zone is enabled on the device.
- * @throws SecurityException if caller is not a device owner, a profile owner for the
- * primary user, or a profile owner of an organization-owned managed profile.
+ * @throws SecurityException if the caller is not a device owner, a profile
+ * owner for the primary user, or a profile owner of an organization-owned managed profile or a
+ * holder of the permission {@link android.Manifest.permission#SET_TIME_ZONE}.
*/
+ @RequiresPermission(anyOf = {SET_TIME_ZONE, QUERY_ADMIN_POLICY}, conditional = true)
public boolean getAutoTimeZoneEnabled(@NonNull ComponentName admin) {
throwIfParentInstance("getAutoTimeZone");
if (mService != null) {
@@ -11875,17 +11902,21 @@ public class DevicePolicyManager {
}
/**
- * Called by a device owner or a profile owner of an organization-owned managed
- * profile to set the system wall clock time. This only takes effect if called when
- * {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false}
- * will be returned.
+ * Called by a device owner, a profile owner of an organization-owned managed
+ * profile or, starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * holders of the permission {@link android.Manifest.permission#SET_TIME} to set the system wall
+ * clock time. This only takes effect if called when
+ * {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
+ * returned.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param millis time in milliseconds since the Epoch
* @return {@code true} if set time succeeded, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner or a profile owner
- * of an organization-owned managed profile.
+ * of an organization-owned managed profile or a holder of the permission
+ * {@link android.Manifest.permission#SET_TIME}.
*/
+ @RequiresPermission(value = SET_TIME, conditional = true)
public boolean setTime(@NonNull ComponentName admin, long millis) {
throwIfParentInstance("setTime");
if (mService != null) {
@@ -11899,10 +11930,12 @@ public class DevicePolicyManager {
}
/**
- * Called by a device owner or a profile owner of an organization-owned managed
- * profile to set the system's persistent default time zone. This only takes
- * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE}
- * is 0, otherwise {@code false} will be returned.
+ * Called by a device owner, a profile owner of an organization-owned managed
+ * profile or, starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * holders of the permission {@link android.Manifest.permission#SET_TIME_ZONE} to set the
+ * system's persistent default time zone. This only take effect if called when
+ * {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise {@code false} will be
+ * returned.
*
* @see android.app.AlarmManager#setTimeZone(String)
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
@@ -11910,8 +11943,10 @@ public class DevicePolicyManager {
* {@link java.util.TimeZone#getAvailableIDs}
* @return {@code true} if set timezone succeeded, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner or a profile owner
- * of an organization-owned managed profile.
+ * of an organization-owned managed profile or a holder of the permissions
+ * {@link android.Manifest.permission#SET_TIME_ZONE}.
*/
+ @RequiresPermission(value = SET_TIME_ZONE, conditional = true)
public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
throwIfParentInstance("setTimeZone");
if (mService != null) {
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 840f3a3c6bb8..3a61ca1fbf93 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -271,6 +271,31 @@ public abstract class DevicePolicyManagerInternal {
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
/**
+ * 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.
+ *
+ * @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 calling process has not been granted the permission.
+ */
+ public abstract void enforcePermission(String permission, int targetUserId);
+
+ /**
+ * Return whether 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.
+ *
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ */
+ public abstract boolean hasPermission(String permission, int targetUserId);
+
+ /**
* Returns whether new "turn off work" behavior is enabled via feature flag.
*/
public abstract boolean isKeepProfilesRunningEnabled();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a7c48f3a4e8f..5f1a444d6cf6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3121,6 +3121,34 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS"
android:protectionLevel="signature|role" />
+ <!-- Allows an application to manage date and time device policy. -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set device policies outside the current user
+ that are critical for securing data within the current user.
+ <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
+ permissions across all users on the device provided they are required for securing data
+ within the current user.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set device policies outside the current user
+ that are required for securing device ownership without accessing user data.
+ <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
+ permissions across all users on the device provided they do not grant access to user
+ data. -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set device policies outside the current user.
+ <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS}
+ that removes the restriction on accessing user data.
+ <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_*
+ permissions across all users on the device.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"
+ android:protectionLevel="internal|role" />
+
<!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
<permission android:name="android.permission.PROVISION_DEMO_DEVICE"
android:protectionLevel="signature|setup" />
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8c2065e7f764..7e93522c5d65 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18,7 +18,13 @@ package com.android.server.devicepolicy;
import static android.Manifest.permission.BIND_DEVICE_ADMIN;
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.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
+import static android.Manifest.permission.SET_TIME;
+import static android.Manifest.permission.SET_TIME_ZONE;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -137,6 +143,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
@@ -715,7 +722,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ "management app's authentication policy";
private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s";
+ private static final String PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG =
+ "enable_permission_based_access";
private static final String ENABLE_COEXISTENCE_FLAG = "enable_coexistence";
+ private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
private static final boolean DEFAULT_ENABLE_COEXISTENCE_FLAG = false;
// TODO(b/258425381) remove the flag after rollout.
@@ -8033,9 +8043,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
+ if (isPermissionCheckFlagEnabled()) {
+ // The effect of this policy is device-wide.
+ enforcePermission(SET_TIME, UserHandle.USER_ALL);
+ } else {
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+ caller));
+ }
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0));
@@ -8057,8 +8073,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
+
+ if (isPermissionCheckFlagEnabled()) {
+ enforceCanQuery(SET_TIME, UserHandle.USER_ALL);
+ } else {
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+ caller));
+ }
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
}
@@ -8074,8 +8096,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
+
+ if (isPermissionCheckFlagEnabled()) {
+ // The effect of this policy is device-wide.
+ enforcePermission(SET_TIME_ZONE, UserHandle.USER_ALL);
+ } else {
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+ caller));
+ }
if (isCoexistenceEnabled(caller)) {
mDevicePolicyEngine.setGlobalPolicy(
@@ -8107,8 +8136,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller));
+
+ if (isPermissionCheckFlagEnabled()) {
+ // The effect of this policy is device-wide.
+ enforceCanQuery(SET_TIME_ZONE, UserHandle.USER_ALL);
+ } else {
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+ caller));
+ }
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
@@ -13031,8 +13067,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ if (isPermissionCheckFlagEnabled()) {
+ // This is a global action.
+ enforcePermission(SET_TIME, UserHandle.USER_ALL);
+ } else {
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ }
// Don't allow set time when auto time is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
@@ -13051,8 +13093,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ if (isPermissionCheckFlagEnabled()) {
+ // This is a global action.
+ enforcePermission(SET_TIME_ZONE, UserHandle.USER_ALL);
+ } else {
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ }
// Don't allow set timezone when auto timezone is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
@@ -13657,6 +13705,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
broadcastIntentToDevicePolicyManagerRoleHolder(intent, parentHandle);
}
+ @Override
+ public void enforcePermission(String permission, int targetUserId) {
+ DevicePolicyManagerService.this.enforcePermission(permission, targetUserId);
+ }
+
+ @Override
+ public boolean hasPermission(String permission, int targetUserId) {
+ return DevicePolicyManagerService.this.hasPermission(permission, targetUserId);
+ }
+
private void broadcastIntentToCrossProfileManifestReceivers(
Intent intent, UserHandle userHandle, boolean requiresPermission) {
final int userId = userHandle.getIdentifier();
@@ -18408,14 +18466,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+
private String getDevicePolicyManagementRoleHolderPackageName(Context context) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
- List<String> roleHolders =
- roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
- if (roleHolders.isEmpty()) {
- return null;
- }
- return roleHolders.get(0);
+
+ // Calling identity needs to be cleared as this method is used in the permissions checks.
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ List<String> roleHolders =
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
+ if (roleHolders.isEmpty()) {
+ return null;
+ }
+ return roleHolders.get(0);
+ });
+ }
+
+ private boolean isDevicePolicyManagementRoleHolder(CallerIdentity caller) {
+ String devicePolicyManagementRoleHolderPackageName =
+ getDevicePolicyManagementRoleHolderPackageName(mContext);
+ return caller.getPackageName().equals(devicePolicyManagementRoleHolderPackageName);
}
private void resetInteractAcrossProfilesAppOps() {
@@ -19513,6 +19582,192 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ // DPC types
+ private static final int DEFAULT_DEVICE_OWNER = 0;
+ private static final int FINANCED_DEVICE_OWNER = 1;
+ private static final int PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE = 2;
+ private static final int PROFILE_OWNER_ON_USER_0 = 3;
+ private static final int PROFILE_OWNER = 4;
+
+ // Permissions of existing DPC types.
+ private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
+ SET_TIME,
+ SET_TIME_ZONE);
+ private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ private static final List<String> 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);
+ private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of(
+ SET_TIME,
+ SET_TIME_ZONE);
+ private static final List<String> PROFILE_OWNER_PERMISSIONS = List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+
+ private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
+ {
+ DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS);
+ DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
+ PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS);
+ }
+
+ //TODO(b/254253251) Fill this map in as new permissions are added for policies.
+ private static final HashMap<String, Integer> ACTIVE_ADMIN_POLICIES = new HashMap<>();
+
+ private static final HashMap<String, String> CROSS_USER_PERMISSIONS =
+ new HashMap<>();
+ {
+ // Auto time 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);
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ */
+ private void enforcePermission(String permission, int targetUserId)
+ throws SecurityException {
+ if (!hasPermission(permission, targetUserId)) {
+ throw new SecurityException("Caller does not have the required permissions for "
+ + "this user. Permissions required: {"
+ + permission
+ + ", "
+ + CROSS_USER_PERMISSIONS.get(permission)
+ + "}");
+ }
+ }
+
+ /**
+ * Return whether the calling process has been granted permission to query a device policy on
+ * a specific 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
+ * and if the user has not been granted {@link QUERY_ADMIN_POLICY}.
+ */
+ private void enforceCanQuery(String permission, int targetUserId) throws SecurityException {
+ if (hasPermission(QUERY_ADMIN_POLICY)) {
+ return;
+ }
+ enforcePermission(permission, targetUserId);
+ }
+
+ /**
+ * Return whether the calling process has been granted permission to apply a device policy on
+ * a specific 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.
+ */
+ private boolean hasPermission(String permission, int targetUserId) {
+ boolean hasPermissionOnOwnUser = hasPermission(permission);
+ boolean hasPermissionOnTargetUser = true;
+ if (hasPermissionOnOwnUser & getCallerIdentity().getUserId() != targetUserId) {
+ hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission));
+ }
+ return hasPermissionOnOwnUser && hasPermissionOnTargetUser;
+ }
+
+ /**
+ * Return whether the calling process has been granted the given permission.
+ *
+ * @param permission The name of the permission being checked.
+ */
+ private boolean hasPermission(String permission) {
+ if (permission == null) {
+ return true;
+ }
+
+ CallerIdentity caller = getCallerIdentity();
+
+ // Check if the caller holds the permission
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return true;
+ }
+ // Check the permissions of DPCs
+ if (isDefaultDeviceOwner(caller)) {
+ return DPC_PERMISSIONS.get(DEFAULT_DEVICE_OWNER).contains(permission);
+ }
+ if (isFinancedDeviceOwner(caller)) {
+ return DPC_PERMISSIONS.get(FINANCED_DEVICE_OWNER).contains(permission);
+ }
+ if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE).contains(
+ permission);
+ }
+ if (isProfileOwnerOnUser0(caller)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_ON_USER_0).contains(permission);
+ }
+ if (isProfileOwner(caller)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER).contains(permission);
+ }
+ // Check the permission for the role-holder
+ if (isDevicePolicyManagementRoleHolder(caller)) {
+ return anyDpcHasPermission(permission, mContext.getUserId());
+ }
+ // 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;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether there is a DPC on the given user that has been granted the given permission.
+ *
+ * @param permission The name of the permission being checked.
+ * @param userId The id of the user to check.
+ */
+ private boolean anyDpcHasPermission(String permission, int userId) {
+ if (mOwners.isDefaultDeviceOwnerUserId(userId)) {
+ return DPC_PERMISSIONS.get(DEFAULT_DEVICE_OWNER).contains(permission);
+ }
+ if (mOwners.isFinancedDeviceOwnerUserId(userId)) {
+ return DPC_PERMISSIONS.get(FINANCED_DEVICE_OWNER).contains(permission);
+ }
+ if (mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE).contains(
+ permission);
+ }
+ if (userId == 0 && mOwners.hasProfileOwner(0)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER_ON_USER_0).contains(permission);
+ }
+ if (mOwners.hasProfileOwner(userId)) {
+ return DPC_PERMISSIONS.get(PROFILE_OWNER).contains(permission);
+ }
+ return false;
+ }
+
+ private boolean isPermissionCheckFlagEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_DEVICE_POLICY_MANAGER,
+ PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG,
+ DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
+ }
+
// TODO(b/260560985): properly gate coexistence changes
private boolean isCoexistenceEnabled(CallerIdentity caller) {
return isCoexistenceFlagEnabled()
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 6f172e4515fc..581a19913530 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -19,6 +19,7 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static com.android.server.devicepolicy.DeviceStateCacheImpl.NO_DEVICE_OWNER;
@@ -455,6 +456,23 @@ class Owners {
}
}
+ boolean isDefaultDeviceOwnerUserId(int userId) {
+ synchronized (mData) {
+ return mData.mDeviceOwner != null
+ && mData.mDeviceOwnerUserId == userId
+ && getDeviceOwnerType(getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_DEFAULT;
+ }
+ }
+
+ boolean isFinancedDeviceOwnerUserId(int userId) {
+ synchronized (mData) {
+ return mData.mDeviceOwner != null
+ && mData.mDeviceOwnerUserId == userId
+ && getDeviceOwnerType(getDeviceOwnerPackageName())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ }
+ }
+
boolean hasProfileOwner(int userId) {
synchronized (mData) {
return getProfileOwnerComponent(userId) != null;