Make ENSURE_VERIFY_APPS global even when set by PO.
Currently only device owner can set global user restrictions.
With this CL ENSURE_VERIFY_APPS will be global no matter who
enforces it, DO or PO.
To make it possible for system apps to check who enforces a
particular restriction in this case a new API method is added
to UserManager: getUserRestrictionSources which returns a list
of users who enforce the restriction.
Bug:31000521
Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.UserRestrictionsTest (ag/1732744)
Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
Test: installed M on a Nexus5x device, created a managed profile with some user restrictions, and checked that after upgrading M->O all restrictions are preserved and split correctly into base, global and local.
Change-Id: I543d3ec9ef0cf2b730da6f7406021c0bba43b785
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index f5b8669..d301463 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -30,11 +30,13 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.service.persistentdata.PersistentDataBlockManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -117,9 +119,10 @@
);
/**
- * User restrictions that can not be set by profile owners.
+ * User restrictions that cannot be set by profile owners of secondary users. When set by DO
+ * they will be applied to all users.
*/
- private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+ private static final Set<String> PRIMARY_USER_ONLY_RESTRICTIONS = Sets.newArraySet(
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_CONFIG_TETHERING,
@@ -163,6 +166,13 @@
UserManager.DISALLOW_ADD_MANAGED_PROFILE
);
+ /*
+ * Special user restrictions that are always applied to all users no matter who sets them.
+ */
+ private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
+ UserManager.ENSURE_VERIFY_APPS
+ );
+
/**
* Throws {@link IllegalArgumentException} if the given restriction name is invalid.
*/
@@ -205,6 +215,12 @@
}
}
+ public static Bundle readRestrictions(XmlPullParser parser) {
+ final Bundle result = new Bundle();
+ readRestrictions(parser, result);
+ return result;
+ }
+
/**
* @return {@code in} itself when it's not null, or an empty bundle (which can writable).
*/
@@ -217,6 +233,14 @@
}
/**
+ * Returns {@code true} if given bundle is not null and contains {@code true} for a given
+ * restriction.
+ */
+ public static boolean contains(@Nullable Bundle in, String restriction) {
+ return in != null && in.getBoolean(restriction);
+ }
+
+ /**
* Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
* bundle.
*
@@ -241,6 +265,22 @@
}
/**
+ * Merges a sparse array of restrictions bundles into one.
+ */
+ @Nullable
+ public static Bundle mergeAll(SparseArray<Bundle> restrictions) {
+ if (restrictions.size() == 0) {
+ return null;
+ } else {
+ final Bundle result = new Bundle();
+ for (int i = 0; i < restrictions.size(); i++) {
+ merge(result, restrictions.valueAt(i));
+ }
+ return result;
+ }
+ }
+
+ /**
* @return true if a restriction is settable by device owner.
*/
public static boolean canDeviceOwnerChange(String restriction) {
@@ -254,7 +294,7 @@
public static boolean canProfileOwnerChange(String restriction, int userId) {
return !IMMUTABLE_BY_OWNERS.contains(restriction)
&& !(userId != UserHandle.USER_SYSTEM
- && DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction));
+ && PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
}
/**
@@ -269,8 +309,15 @@
* Takes restrictions that can be set by device owner, and sort them into what should be applied
* globally and what should be applied only on the current user.
*/
- public static void sortToGlobalAndLocal(@Nullable Bundle in, @NonNull Bundle global,
- @NonNull Bundle local) {
+ public static void sortToGlobalAndLocal(@Nullable Bundle in, boolean isDeviceOwner,
+ int cameraRestrictionScope,
+ @NonNull Bundle global, @NonNull Bundle local) {
+ // Camera restriction (as well as all others) goes to at most one bundle.
+ if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_GLOBALLY) {
+ global.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ } else if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_LOCALLY) {
+ local.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ }
if (in == null || in.size() == 0) {
return;
}
@@ -278,7 +325,7 @@
if (!in.getBoolean(key)) {
continue;
}
- if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)) {
+ if (isGlobal(isDeviceOwner, key)) {
global.putBoolean(key, true);
} else {
local.putBoolean(key, true);
@@ -287,6 +334,15 @@
}
/**
+ * Whether given user restriction should be enforced globally.
+ */
+ private static boolean isGlobal(boolean isDeviceOwner, String key) {
+ return (isDeviceOwner &&
+ (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key)|| GLOBAL_RESTRICTIONS.contains(key)))
+ || PROFILE_GLOBAL_RESTRICTIONS.contains(key);
+ }
+
+ /**
* @return true if two Bundles contain the same user restriction.
* A null bundle and an empty bundle are considered to be equal.
*/
@@ -485,4 +541,29 @@
pw.println(prefix + "null");
}
}
+
+ /**
+ * Moves a particular restriction from one array of bundles to another, e.g. for all users.
+ */
+ public static void moveRestriction(String restrictionKey, SparseArray<Bundle> srcRestrictions,
+ SparseArray<Bundle> destRestrictions) {
+ for (int i = 0; i < srcRestrictions.size(); i++) {
+ int key = srcRestrictions.keyAt(i);
+ Bundle from = srcRestrictions.valueAt(i);
+ if (contains(from, restrictionKey)) {
+ from.remove(restrictionKey);
+ Bundle to = destRestrictions.get(key);
+ if (to == null) {
+ to = new Bundle();
+ destRestrictions.append(key, to);
+ }
+ to.putBoolean(restrictionKey, true);
+ // Don't keep empty bundles.
+ if (from.isEmpty()) {
+ srcRestrictions.removeAt(i);
+ i--;
+ }
+ }
+ }
+ }
}