diff options
10 files changed, 621 insertions, 225 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 24f9aadb4aeb..b23edacb805d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -32688,7 +32688,8 @@ package android.os { method public android.os.UserHandle getUserForSerialNumber(long); method public java.lang.String getUserName(); method public java.util.List<android.os.UserHandle> getUserProfiles(); - method public int getUserRestrictionSource(java.lang.String, android.os.UserHandle); + method public deprecated int getUserRestrictionSource(java.lang.String, android.os.UserHandle); + method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle); method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); @@ -32754,6 +32755,14 @@ package android.os { field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2 } + public static final class UserManager.EnforcingUser implements android.os.Parcelable { + method public int describeContents(); + method public android.os.UserHandle getUserHandle(); + method public int getUserRestrictionSource(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.os.UserManager.EnforcingUser> CREATOR; + } + public static abstract class UserManager.UserRestrictionSource implements java.lang.annotation.Annotation { } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 9513854c1e29..1c2588ada181 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -19,6 +19,7 @@ package android.os; import android.os.Bundle; import android.os.PersistableBundle; +import android.os.UserManager; import android.content.pm.UserInfo; import android.content.IntentSender; import android.content.RestrictionEntry; @@ -61,6 +62,7 @@ interface IUserManager { int getUserSerialNumber(int userHandle); int getUserHandle(int userSerialNumber); int getUserRestrictionSource(String restrictionKey, int userHandle); + List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userHandle); Bundle getUserRestrictions(int userHandle); boolean hasBaseUserRestriction(String restrictionKey, int userHandle); boolean hasUserRestriction(in String restrictionKey, int userHandle); diff --git a/core/java/android/os/UserManager.aidl b/core/java/android/os/UserManager.aidl new file mode 100644 index 000000000000..2611b0f36141 --- /dev/null +++ b/core/java/android/os/UserManager.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2016, 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.os; + +parcelable UserManager.EnforcingUser;
\ No newline at end of file diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 3478eaa83982..efacb205c36d 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1187,7 +1187,9 @@ public class UserManager { * @return The source of user restriction. Any combination of {@link #RESTRICTION_NOT_SET}, * {@link #RESTRICTION_SOURCE_SYSTEM}, {@link #RESTRICTION_SOURCE_DEVICE_OWNER} * and {@link #RESTRICTION_SOURCE_PROFILE_OWNER} + * @deprecated use {@link #getUserRestrictionSources(String, int)} instead. */ + @Deprecated @SystemApi @UserRestrictionSource public int getUserRestrictionSource(String restrictionKey, UserHandle userHandle) { @@ -1199,6 +1201,25 @@ public class UserManager { } /** + * @hide + * + * Returns a list of users who set a user restriction on a given user. + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * @param restrictionKey the string key representing the restriction + * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. + * @return a list of user ids enforcing this restriction. + */ + @SystemApi + public List<EnforcingUser> getUserRestrictionSources( + String restrictionKey, UserHandle userHandle) { + try { + return mService.getUserRestrictionSources(restrictionKey, userHandle.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Returns the user-wide restrictions imposed on this user. * @return a Bundle containing all the restrictions. */ @@ -2310,8 +2331,8 @@ public class UserManager { * @hide * Checks if any uninitialized user has the specific seed account name and type. * - * @param mAccountName The account name to check for - * @param mAccountType The account type of the account to check for + * @param accountName The account name to check for + * @param accountType The account type of the account to check for * @return whether the seed account was found */ public boolean someUserHasSeedAccount(String accountName, String accountType) { @@ -2321,4 +2342,73 @@ public class UserManager { throw re.rethrowFromSystemServer(); } } + + /** + * @hide + * User that enforces a restriction. + * + * @see #getUserRestrictionSources(String, UserHandle) + */ + @SystemApi + public static final class EnforcingUser implements Parcelable { + private final @UserIdInt int userId; + private final @UserRestrictionSource int userRestrictionSource; + + /** + * @hide + */ + public EnforcingUser( + @UserIdInt int userId, @UserRestrictionSource int userRestrictionSource) { + this.userId = userId; + this.userRestrictionSource = userRestrictionSource; + } + + private EnforcingUser(Parcel in) { + userId = in.readInt(); + userRestrictionSource = in.readInt(); + } + + public static final Creator<EnforcingUser> CREATOR = new Creator<EnforcingUser>() { + @Override + public EnforcingUser createFromParcel(Parcel in) { + return new EnforcingUser(in); + } + + @Override + public EnforcingUser[] newArray(int size) { + return new EnforcingUser[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(userId); + dest.writeInt(userRestrictionSource); + } + + /** + * Returns an id of the enforcing user. + * + * <p> Will be UserHandle.USER_NULL when restriction is set by the system. + */ + public UserHandle getUserHandle() { + return UserHandle.of(userId); + } + + /** + * Returns the status of the enforcing user. + * + * <p> One of {@link #RESTRICTION_SOURCE_SYSTEM}, + * {@link #RESTRICTION_SOURCE_DEVICE_OWNER} and + * {@link #RESTRICTION_SOURCE_PROFILE_OWNER} + */ + public @UserRestrictionSource int getUserRestrictionSource() { + return userRestrictionSource; + } + } } diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index 466a7e35be68..97da5889a186 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -15,7 +15,6 @@ */ package android.os; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -24,6 +23,10 @@ import android.graphics.Bitmap; * @hide Only for use within the system server. */ public abstract class UserManagerInternal { + public static final int CAMERA_NOT_DISABLED = 0; + public static final int CAMERA_DISABLED_LOCALLY = 1; + public static final int CAMERA_DISABLED_GLOBALLY = 2; + public interface UserRestrictionsListener { /** * Called when a user restriction changes. @@ -36,18 +39,19 @@ public abstract class UserManagerInternal { } /** - * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} - * to set per-user as well as global user restrictions. + * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set + * restrictions enforced by the user. * * @param userId target user id for the local restrictions. - * @param localRestrictions per-user restrictions. - * Caller must not change it once passed to this method. - * @param globalRestrictions global restrictions set by DO. Must be null when PO changed user - * restrictions, in which case global restrictions won't change. - * Caller must not change it once passed to this method. - */ - public abstract void setDevicePolicyUserRestrictions(int userId, - @NonNull Bundle localRestrictions, @Nullable Bundle globalRestrictions); + * @param restrictions a bundle of user restrictions. + * @param isDeviceOwner whether {@code userId} corresponds to device owner user id. + * @param cameraRestrictionScope is camera disabled and if so what is the scope of restriction. + * Should be one of {@link #CAMERA_NOT_DISABLED}, {@link #CAMERA_DISABLED_LOCALLY} or + * {@link #CAMERA_DISABLED_GLOBALLY} + */ + public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions, + boolean isDeviceOwner, int cameraRestrictionScope); + /** * Returns the "base" user restrictions. * diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 9b47beba232e..08744244a454 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -67,6 +67,7 @@ import android.os.ShellCommand; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManager.EnforcingUser; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; @@ -118,6 +119,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -162,7 +164,11 @@ public class UserManagerService extends IUserManager.Stub { private static final String TAG_USER = "user"; private static final String TAG_RESTRICTIONS = "restrictions"; private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions"; + private static final String TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS = + "device_policy_global_restrictions"; + /** Legacy name for device owner id tag. */ private static final String TAG_GLOBAL_RESTRICTION_OWNER_ID = "globalRestrictionOwnerUserId"; + private static final String TAG_DEVICE_OWNER_USER_ID = "deviceOwnerUserId"; private static final String TAG_ENTRY = "entry"; private static final String TAG_VALUE = "value"; private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions"; @@ -202,7 +208,7 @@ public class UserManagerService extends IUserManager.Stub { @VisibleForTesting static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100; - private static final int USER_VERSION = 6; + private static final int USER_VERSION = 7; private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms @@ -267,7 +273,7 @@ public class UserManagerService extends IUserManager.Stub { /** * User restrictions set via UserManager. This doesn't include restrictions set by - * device owner / profile owners. + * device owner / profile owners. Only non-empty restriction bundles are stored. * * DO NOT Change existing {@link Bundle} in it. When changing a restriction for a user, * a new {@link Bundle} should always be created and set. This is because a {@link Bundle} @@ -305,20 +311,21 @@ public class UserManagerService extends IUserManager.Stub { /** * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService} - * that should be applied to all users, including guests. + * that should be applied to all users, including guests. Only non-empty restriction bundles are + * stored. */ @GuardedBy("mRestrictionsLock") - private Bundle mDevicePolicyGlobalUserRestrictions; + private final SparseArray<Bundle> mDevicePolicyGlobalUserRestrictions = new SparseArray<>(); /** * Id of the user that set global restrictions. */ @GuardedBy("mRestrictionsLock") - private int mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL; + private int mDeviceOwnerUserId = UserHandle.USER_NULL; /** * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService} - * for each user. + * for each user. Only non-empty restriction bundles are stored. */ @GuardedBy("mRestrictionsLock") private final SparseArray<Bundle> mDevicePolicyLocalUserRestrictions = new SparseArray<>(); @@ -1176,39 +1183,36 @@ public class UserManagerService extends IUserManager.Stub { } /** - * See {@link UserManagerInternal#setDevicePolicyUserRestrictions(int, Bundle, Bundle)} + * See {@link UserManagerInternal#setDevicePolicyUserRestrictions} */ - void setDevicePolicyUserRestrictionsInner(int userId, @NonNull Bundle local, - @Nullable Bundle global) { - Preconditions.checkNotNull(local); - boolean globalChanged = false; - boolean localChanged; + private void setDevicePolicyUserRestrictionsInner(int userId, @Nullable Bundle restrictions, + boolean isDeviceOwner, int cameraRestrictionScope) { + final Bundle global = new Bundle(); + final Bundle local = new Bundle(); + + // Sort restrictions into local and global ensuring they don't overlap. + UserRestrictionsUtils.sortToGlobalAndLocal(restrictions, isDeviceOwner, + cameraRestrictionScope, global, local); + + boolean globalChanged, localChanged; synchronized (mRestrictionsLock) { - if (global != null) { - // Update global. - globalChanged = !UserRestrictionsUtils.areEqual( - mDevicePolicyGlobalUserRestrictions, global); - if (globalChanged) { - mDevicePolicyGlobalUserRestrictions = global; - } + // Update global and local restrictions if they were changed. + globalChanged = updateRestrictionsIfNeededLR( + userId, global, mDevicePolicyGlobalUserRestrictions); + localChanged = updateRestrictionsIfNeededLR( + userId, local, mDevicePolicyLocalUserRestrictions); + + if (isDeviceOwner) { // Remember the global restriction owner userId to be able to make a distinction // in getUserRestrictionSource on who set local policies. - mGlobalRestrictionOwnerUserId = userId; + mDeviceOwnerUserId = userId; } else { - if (mGlobalRestrictionOwnerUserId == userId) { + if (mDeviceOwnerUserId == userId) { // When profile owner sets restrictions it passes null global bundle and we // reset global restriction owner userId. // This means this user used to have DO, but now the DO is gone and the user // instead has PO. - mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL; - } - } - { - // Update local. - final Bundle prev = mDevicePolicyLocalUserRestrictions.get(userId); - localChanged = !UserRestrictionsUtils.areEqual(prev, local); - if (localChanged) { - mDevicePolicyLocalUserRestrictions.put(userId, local); + mDeviceOwnerUserId = UserHandle.USER_NULL; } } } @@ -1220,12 +1224,9 @@ public class UserManagerService extends IUserManager.Stub { } // Don't call them within the mRestrictionsLock. synchronized (mPackagesLock) { - if (localChanged) { + if (localChanged || globalChanged) { writeUserLP(getUserDataNoChecks(userId)); } - if (globalChanged) { - writeUserListLP(); - } } synchronized (mRestrictionsLock) { @@ -1237,11 +1238,30 @@ public class UserManagerService extends IUserManager.Stub { } } + /** + * Updates restriction bundle for a given user in a given restriction array. If new bundle is + * empty, record is removed from the array. + * @return whether restrictions bundle is different from the old one. + */ + private boolean updateRestrictionsIfNeededLR(int userId, @Nullable Bundle restrictions, + SparseArray<Bundle> restrictionsArray) { + final boolean changed = + !UserRestrictionsUtils.areEqual(restrictionsArray.get(userId), restrictions); + if (changed) { + if (!UserRestrictionsUtils.isEmpty(restrictions)) { + restrictionsArray.put(userId, restrictions); + } else { + restrictionsArray.delete(userId); + } + } + return changed; + } + @GuardedBy("mRestrictionsLock") private Bundle computeEffectiveUserRestrictionsLR(int userId) { final Bundle baseRestrictions = UserRestrictionsUtils.nonNull(mBaseUserRestrictions.get(userId)); - final Bundle global = mDevicePolicyGlobalUserRestrictions; + final Bundle global = UserRestrictionsUtils.mergeAll(mDevicePolicyGlobalUserRestrictions); final Bundle local = mDevicePolicyLocalUserRestrictions.get(userId); if (UserRestrictionsUtils.isEmpty(global) && UserRestrictionsUtils.isEmpty(local)) { @@ -1299,39 +1319,58 @@ public class UserManagerService extends IUserManager.Stub { */ @Override public int getUserRestrictionSource(String restrictionKey, int userId) { - checkManageUsersPermission("getUserRestrictionSource"); + List<EnforcingUser> enforcingUsers = getUserRestrictionSources(restrictionKey, userId); + // Get "bitwise or" of restriction sources for all enforcing users. int result = UserManager.RESTRICTION_NOT_SET; + for (int i = enforcingUsers.size() - 1; i >= 0; i--) { + result |= enforcingUsers.get(i).getUserRestrictionSource(); + } + return result; + } + + @Override + public List<EnforcingUser> getUserRestrictionSources( + String restrictionKey, @UserIdInt int userId) { + checkManageUsersPermission("getUserRestrictionSource"); // Shortcut for the most common case if (!hasUserRestriction(restrictionKey, userId)) { - return result; + return Collections.emptyList(); } + final List<EnforcingUser> result = new ArrayList<>(); + + // Check if it is base restriction. if (hasBaseUserRestriction(restrictionKey, userId)) { - result |= UserManager.RESTRICTION_SOURCE_SYSTEM; + result.add(new EnforcingUser( + UserHandle.USER_NULL, UserManager.RESTRICTION_SOURCE_SYSTEM)); } - synchronized(mRestrictionsLock) { - Bundle localRestrictions = mDevicePolicyLocalUserRestrictions.get(userId); - if (!UserRestrictionsUtils.isEmpty(localRestrictions) - && localRestrictions.getBoolean(restrictionKey)) { - // Local restrictions may have been set by device owner the userId of which is - // stored in mGlobalRestrictionOwnerUserId. - if (mGlobalRestrictionOwnerUserId == userId) { - result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER; - } else { - result |= UserManager.RESTRICTION_SOURCE_PROFILE_OWNER; + synchronized (mRestrictionsLock) { + // Check if it is set by profile owner. + Bundle profileOwnerRestrictions = mDevicePolicyLocalUserRestrictions.get(userId); + if (UserRestrictionsUtils.contains(profileOwnerRestrictions, restrictionKey)) { + result.add(getEnforcingUserLocked(userId)); + } + + // Iterate over all users who enforce global restrictions. + for (int i = mDevicePolicyGlobalUserRestrictions.size() - 1; i >= 0; i--) { + Bundle globalRestrictions = mDevicePolicyGlobalUserRestrictions.valueAt(i); + int profileUserId = mDevicePolicyGlobalUserRestrictions.keyAt(i); + if (UserRestrictionsUtils.contains(globalRestrictions, restrictionKey)) { + result.add(getEnforcingUserLocked(profileUserId)); } } - if (!UserRestrictionsUtils.isEmpty(mDevicePolicyGlobalUserRestrictions) - && mDevicePolicyGlobalUserRestrictions.getBoolean(restrictionKey)) { - result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER; - } } - return result; } + private EnforcingUser getEnforcingUserLocked(@UserIdInt int userId) { + int source = mDeviceOwnerUserId == userId ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER + : UserManager.RESTRICTION_SOURCE_PROFILE_OWNER; + return new EnforcingUser(userId, source); + } + /** * @return UserRestrictions that are in effect currently. This always returns a new * {@link Bundle}. @@ -1374,28 +1413,26 @@ public class UserManagerService extends IUserManager.Stub { * Optionally updating user restrictions, calculate the effective user restrictions and also * propagate to other services and system settings. * - * @param newRestrictions User restrictions to set. + * @param newBaseRestrictions User restrictions to set. * If null, will not update user restrictions and only does the propagation. * @param userId target user ID. */ @GuardedBy("mRestrictionsLock") private void updateUserRestrictionsInternalLR( - @Nullable Bundle newRestrictions, int userId) { - + @Nullable Bundle newBaseRestrictions, int userId) { final Bundle prevAppliedRestrictions = UserRestrictionsUtils.nonNull( mAppliedUserRestrictions.get(userId)); // Update base restrictions. - if (newRestrictions != null) { - // If newRestrictions == the current one, it's probably a bug. + if (newBaseRestrictions != null) { + // If newBaseRestrictions == the current one, it's probably a bug. final Bundle prevBaseRestrictions = mBaseUserRestrictions.get(userId); - Preconditions.checkState(prevBaseRestrictions != newRestrictions); + Preconditions.checkState(prevBaseRestrictions != newBaseRestrictions); Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId) - != newRestrictions); + != newBaseRestrictions); - if (!UserRestrictionsUtils.areEqual(prevBaseRestrictions, newRestrictions)) { - mBaseUserRestrictions.put(userId, newRestrictions); + if (updateRestrictionsIfNeededLR(userId, newBaseRestrictions, mBaseUserRestrictions)) { scheduleWriteUser(getUserDataNoChecks(userId)); } } @@ -1746,7 +1783,9 @@ public class UserManagerService extends IUserManager.Stub { } } - final Bundle newDevicePolicyGlobalUserRestrictions = new Bundle(); + // Pre-O global user restriction were stored as a single bundle (as opposed to per-user + // currently), take care of it in case of upgrade. + Bundle oldDevicePolicyGlobalUserRestrictions = null; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { if (type == XmlPullParser.START_TAG) { @@ -1771,29 +1810,30 @@ public class UserManagerService extends IUserManager.Stub { if (type == XmlPullParser.START_TAG) { if (parser.getName().equals(TAG_RESTRICTIONS)) { synchronized (mGuestRestrictions) { - UserRestrictionsUtils - .readRestrictions(parser, mGuestRestrictions); + mGuestRestrictions.putAll( + UserRestrictionsUtils.readRestrictions(parser)); } } break; } } - } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) { - UserRestrictionsUtils.readRestrictions(parser, - newDevicePolicyGlobalUserRestrictions); - } else if (name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) { + } else if (name.equals(TAG_DEVICE_OWNER_USER_ID) + // Legacy name, should only be encountered when upgrading from pre-O. + || name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) { String ownerUserId = parser.getAttributeValue(null, ATTR_ID); if (ownerUserId != null) { - mGlobalRestrictionOwnerUserId = Integer.parseInt(ownerUserId); + mDeviceOwnerUserId = Integer.parseInt(ownerUserId); } + } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) { + // Should only happen when upgrading from pre-O (version < 7). + oldDevicePolicyGlobalUserRestrictions = + UserRestrictionsUtils.readRestrictions(parser); } } } - synchronized (mRestrictionsLock) { - mDevicePolicyGlobalUserRestrictions = newDevicePolicyGlobalUserRestrictions; - } + updateUserIds(); - upgradeIfNecessaryLP(); + upgradeIfNecessaryLP(oldDevicePolicyGlobalUserRestrictions); } catch (IOException | XmlPullParserException e) { fallbackToSingleUserLP(); } finally { @@ -1803,8 +1843,9 @@ public class UserManagerService extends IUserManager.Stub { /** * Upgrade steps between versions, either for fixing bugs or changing the data format. + * @param oldGlobalUserRestrictions Pre-O global device policy restrictions. */ - private void upgradeIfNecessaryLP() { + private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) { final int originalVersion = mUserVersion; int userVersion = mUserVersion; if (userVersion < 1) { @@ -1855,6 +1896,23 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 6; } + if (userVersion < 7) { + // Previously only one user could enforce global restrictions, now it is per-user. + synchronized (mRestrictionsLock) { + if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions) + && mDeviceOwnerUserId != UserHandle.USER_NULL) { + mDevicePolicyGlobalUserRestrictions.put( + mDeviceOwnerUserId, oldGlobalUserRestrictions); + } + // ENSURE_VERIFY_APPS is now enforced globally even if put by profile owner, so move + // it from local to global bundle for all users who set it. + UserRestrictionsUtils.moveRestriction(UserManager.ENSURE_VERIFY_APPS, + mDevicePolicyLocalUserRestrictions, mDevicePolicyGlobalUserRestrictions + ); + } + userVersion = 7; + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); @@ -1893,8 +1951,10 @@ public class UserManagerService extends IUserManager.Stub { Log.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e); } - synchronized (mRestrictionsLock) { - mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions); + if (!restrictions.isEmpty()) { + synchronized (mRestrictionsLock) { + mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions); + } } updateUserIds(); @@ -2004,6 +2064,9 @@ public class UserManagerService extends IUserManager.Stub { UserRestrictionsUtils.writeRestrictions(serializer, mDevicePolicyLocalUserRestrictions.get(userInfo.id), TAG_DEVICE_POLICY_RESTRICTIONS); + UserRestrictionsUtils.writeRestrictions(serializer, + mDevicePolicyGlobalUserRestrictions.get(userInfo.id), + TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS); } if (userData.account != null) { @@ -2057,13 +2120,9 @@ public class UserManagerService extends IUserManager.Stub { .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_GUEST_RESTRICTIONS); - synchronized (mRestrictionsLock) { - UserRestrictionsUtils.writeRestrictions(serializer, - mDevicePolicyGlobalUserRestrictions, TAG_DEVICE_POLICY_RESTRICTIONS); - } - serializer.startTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID); - serializer.attribute(null, ATTR_ID, Integer.toString(mGlobalRestrictionOwnerUserId)); - serializer.endTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID); + serializer.startTag(null, TAG_DEVICE_OWNER_USER_ID); + serializer.attribute(null, ATTR_ID, Integer.toString(mDeviceOwnerUserId)); + serializer.endTag(null, TAG_DEVICE_OWNER_USER_ID); int[] userIdsToWrite; synchronized (mUsersLock) { userIdsToWrite = new int[mUsers.size()]; @@ -2125,8 +2184,9 @@ public class UserManagerService extends IUserManager.Stub { String seedAccountName = null; String seedAccountType = null; PersistableBundle seedAccountOptions = null; - Bundle baseRestrictions = new Bundle(); - Bundle localRestrictions = new Bundle(); + Bundle baseRestrictions = null; + Bundle localRestrictions = null; + Bundle globalRestrictions = null; XmlPullParser parser = Xml.newPullParser(); parser.setInput(is, StandardCharsets.UTF_8.name()); @@ -2187,9 +2247,11 @@ public class UserManagerService extends IUserManager.Stub { name = parser.getText(); } } else if (TAG_RESTRICTIONS.equals(tag)) { - UserRestrictionsUtils.readRestrictions(parser, baseRestrictions); + baseRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { - UserRestrictionsUtils.readRestrictions(parser, localRestrictions); + localRestrictions = UserRestrictionsUtils.readRestrictions(parser); + } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) { + globalRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_ACCOUNT.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { @@ -2224,8 +2286,15 @@ public class UserManagerService extends IUserManager.Stub { userData.seedAccountOptions = seedAccountOptions; synchronized (mRestrictionsLock) { - mBaseUserRestrictions.put(id, baseRestrictions); - mDevicePolicyLocalUserRestrictions.put(id, localRestrictions); + if (baseRestrictions != null) { + mBaseUserRestrictions.put(id, baseRestrictions); + } + if (localRestrictions != null) { + mDevicePolicyLocalUserRestrictions.put(id, localRestrictions); + } + if (globalRestrictions != null) { + mDevicePolicyGlobalUserRestrictions.put(id, globalRestrictions); + } } return userData; } @@ -2731,6 +2800,10 @@ public class UserManagerService extends IUserManager.Stub { mAppliedUserRestrictions.remove(userHandle); mCachedEffectiveUserRestrictions.remove(userHandle); mDevicePolicyLocalUserRestrictions.remove(userHandle); + if (mDevicePolicyGlobalUserRestrictions.get(userHandle) != null) { + mDevicePolicyGlobalUserRestrictions.remove(userHandle); + applyUserRestrictionsForAllUsersLR(); + } } // Update the user list synchronized (mPackagesLock) { @@ -3420,6 +3493,9 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mRestrictionsLock) { UserRestrictionsUtils.dumpRestrictions( pw, " ", mBaseUserRestrictions.get(userInfo.id)); + pw.println(" Device policy global restrictions:"); + UserRestrictionsUtils.dumpRestrictions( + pw, " ", mDevicePolicyGlobalUserRestrictions.get(userInfo.id)); pw.println(" Device policy local restrictions:"); UserRestrictionsUtils.dumpRestrictions( pw, " ", mDevicePolicyLocalUserRestrictions.get(userInfo.id)); @@ -3448,13 +3524,7 @@ public class UserManagerService extends IUserManager.Stub { } } pw.println(); - pw.println(" Device policy global restrictions:"); - synchronized (mRestrictionsLock) { - UserRestrictionsUtils - .dumpRestrictions(pw, " ", mDevicePolicyGlobalUserRestrictions); - } - pw.println(); - pw.println(" Global restrictions owner id:" + mGlobalRestrictionOwnerUserId); + pw.println(" Device owner id:" + mDeviceOwnerUserId); pw.println(); pw.println(" Guest restrictions:"); synchronized (mGuestRestrictions) { @@ -3508,10 +3578,10 @@ public class UserManagerService extends IUserManager.Stub { private class LocalService extends UserManagerInternal { @Override - public void setDevicePolicyUserRestrictions(int userId, @NonNull Bundle localRestrictions, - @Nullable Bundle globalRestrictions) { - UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, localRestrictions, - globalRestrictions); + public void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions, + boolean isDeviceOwner, int cameraRestrictionScope) { + UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, restrictions, + isDeviceOwner, cameraRestrictionScope); } @Override @@ -3525,8 +3595,10 @@ public class UserManagerService extends IUserManager.Stub { public void setBaseUserRestrictionsByDpmsForMigration( int userId, Bundle baseRestrictions) { synchronized (mRestrictionsLock) { - mBaseUserRestrictions.put(userId, new Bundle(baseRestrictions)); - invalidateEffectiveUserRestrictionsLR(userId); + if (updateRestrictionsIfNeededLR( + userId, new Bundle(baseRestrictions), mBaseUserRestrictions)) { + invalidateEffectiveUserRestrictionsLR(userId); + } } final UserData userData = getUserDataNoChecks(userId); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index f5b866981993..d301463db164 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.Bundle; 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 @@ public class UserRestrictionsUtils { ); /** - * 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 @@ public class UserRestrictionsUtils { 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 class UserRestrictionsUtils { } } + 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 @@ public class UserRestrictionsUtils { } /** + * 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 @@ public class UserRestrictionsUtils { } /** + * 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 class UserRestrictionsUtils { 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 @@ public class UserRestrictionsUtils { * 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 @@ public class UserRestrictionsUtils { 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 @@ public class UserRestrictionsUtils { } /** + * 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 @@ public class UserRestrictionsUtils { 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--; + } + } + } + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bdd1a0f5100a..6b4186d3ab86 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -49,7 +49,6 @@ import android.Manifest.permission; import android.accessibilityservice.AccessibilityServiceInfo; import android.accounts.Account; import android.accounts.AccountManager; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -180,8 +179,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -1143,7 +1140,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) { keepUninstalledPackages = readPackageList(parser, tag); } else if (TAG_USER_RESTRICTIONS.equals(tag)) { - UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions()); + userRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) { readAttributeValues( parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet); @@ -7767,7 +7764,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { - ActiveAdmin activeAdmin = + final ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); final boolean isDeviceOwner = isDeviceOwner(who, userHandle); @@ -7782,7 +7779,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Save the restriction to ActiveAdmin. - activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner); + final Bundle restrictions = activeAdmin.ensureUserRestrictions(); + if (enabledFromThisOwner) { + restrictions.putBoolean(key, true); + } else { + restrictions.remove(key); + } saveUserRestrictionsLocked(userHandle); } } @@ -7795,39 +7797,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void pushUserRestrictions(int userId) { synchronized (this) { - final Bundle global; - final Bundle local = new Bundle(); - if (mOwners.isDeviceOwnerUserId(userId)) { - global = new Bundle(); + final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId); + final Bundle userRestrictions; + // Whether device owner enforces camera restriction. + boolean disallowCameraGlobally = false; + if (isDeviceOwner) { final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner == null) { return; // Shouldn't happen. } - - UserRestrictionsUtils.sortToGlobalAndLocal(deviceOwner.userRestrictions, - global, local); + userRestrictions = deviceOwner.userRestrictions; // DO can disable camera globally. - if (deviceOwner.disableCamera) { - global.putBoolean(UserManager.DISALLOW_CAMERA, true); - } + disallowCameraGlobally = deviceOwner.disableCamera; } else { - global = null; - - ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); - if (profileOwner != null) { - UserRestrictionsUtils.merge(local, profileOwner.userRestrictions); - } + final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); + userRestrictions = profileOwner != null ? profileOwner.userRestrictions : null; } - // Also merge in *local* camera restriction. - if (getCameraDisabled(/* who= */ null, - userId, /* mergeDeviceOwnerRestriction= */ false)) { - local.putBoolean(UserManager.DISALLOW_CAMERA, true); - } - mUserManagerInternal.setDevicePolicyUserRestrictions(userId, local, global); + + // Whether any admin enforces camera restriction. + final int cameraRestrictionScope = + getCameraRestrictionScopeLocked(userId, disallowCameraGlobally); + + mUserManagerInternal.setDevicePolicyUserRestrictions(userId, userRestrictions, + isDeviceOwner, cameraRestrictionScope); } } + /** + * Get the scope of camera restriction for a given user if any. + */ + private int getCameraRestrictionScopeLocked(int userId, boolean disallowCameraGlobally) { + if (disallowCameraGlobally) { + return UserManagerInternal.CAMERA_DISABLED_GLOBALLY; + } else if (getCameraDisabled( + /* who= */ null, userId, /* mergeDeviceOwnerRestriction= */ false)) { + return UserManagerInternal.CAMERA_DISABLED_LOCALLY; + } + return UserManagerInternal.CAMERA_NOT_DISABLED; + } + @Override public Bundle getUserRestrictions(ComponentName who) { if (!mHasFeature) { 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 e68895e03f9c..af9caf222655 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -15,6 +15,10 @@ */ package com.android.server.devicepolicy; +import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY; +import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY; +import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED; + import android.Manifest.permission; import android.app.Activity; import android.app.admin.DeviceAdminReceiver; @@ -39,6 +43,7 @@ import android.os.IBinder; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; import android.provider.Settings; import android.telephony.TelephonyManager; import android.test.MoreAsserts; @@ -928,9 +933,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions() - ); + eq(null), + eq(true), eq(CAMERA_NOT_DISABLED)); assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)); @@ -1287,7 +1291,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(defaultRestrictions), - MockUtils.checkUserRestrictions() + eq(true) /* isDeviceOwner */, + eq(CAMERA_NOT_DISABLED) ); reset(mContext.userManagerInternal); @@ -1296,21 +1301,21 @@ public class DevicePolicyManagerTest extends DpmTestBase { } assertNoDeviceOwnerRestrictions(); + reset(mContext.userManagerInternal); dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER) - ); + MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER), + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER) - ); + MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS, + UserManager.DISALLOW_ADD_USER), + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); DpmTestUtils.assertRestrictions( @@ -1328,8 +1333,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS), - MockUtils.checkUserRestrictions() - ); + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); DpmTestUtils.assertRestrictions( @@ -1345,8 +1349,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions() - ); + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); assertNoDeviceOwnerRestrictions(); @@ -1358,42 +1361,38 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.addUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME, - UserManager.DISALLOW_UNMUTE_MICROPHONE) - ); + UserManager.DISALLOW_UNMUTE_MICROPHONE), + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME); dpm.clearUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE); - + reset(mContext.userManagerInternal); // More tests. dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER) - ); + MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER), + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); dpm.addUserRestriction(admin1, UserManager.DISALLOW_FUN); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN, - UserManager.DISALLOW_ADD_USER) - ); + UserManager.DISALLOW_ADD_USER), + eq(true), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); dpm.setCameraDisabled(admin1, true); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), // DISALLOW_CAMERA will be applied to both local and global. - MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA), MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN, - UserManager.DISALLOW_CAMERA, UserManager.DISALLOW_ADD_USER) - ); + UserManager.DISALLOW_ADD_USER), + eq(true), eq(CAMERA_DISABLED_GLOBALLY)); reset(mContext.userManagerInternal); // Set up another DA and let it disable camera. Now DISALLOW_CAMERA will only be applied @@ -1407,11 +1406,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - // DISALLOW_CAMERA will be applied to both local and global. - MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA), + // DISALLOW_CAMERA will be applied to both local and global. <- TODO: fix this MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN, - UserManager.DISALLOW_ADD_USER) - ); + UserManager.DISALLOW_ADD_USER), + eq(true), eq(CAMERA_DISABLED_LOCALLY)); reset(mContext.userManagerInternal); // TODO Make sure restrictions are written to the file. } @@ -1429,8 +1427,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(DpmMockContext.CALLER_USER_HANDLE), MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES), - isNull(Bundle.class) - ); + eq(false), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); @@ -1438,8 +1435,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(DpmMockContext.CALLER_USER_HANDLE), MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserManager.DISALLOW_OUTGOING_CALLS), - isNull(Bundle.class) - ); + eq(false), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); DpmTestUtils.assertRestrictions( @@ -1462,8 +1458,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(DpmMockContext.CALLER_USER_HANDLE), MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS), - isNull(Bundle.class) - ); + eq(false), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); DpmTestUtils.assertRestrictions( @@ -1484,8 +1479,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(DpmMockContext.CALLER_USER_HANDLE), MockUtils.checkUserRestrictions(), - isNull(Bundle.class) - ); + eq(false), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); DpmTestUtils.assertRestrictions( @@ -1507,18 +1501,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(DpmMockContext.CALLER_USER_HANDLE), MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_UNMUTE_MICROPHONE), - isNull(Bundle.class) - ); + eq(false), eq(CAMERA_NOT_DISABLED)); reset(mContext.userManagerInternal); dpm.setCameraDisabled(admin1, true); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(DpmMockContext.CALLER_USER_HANDLE), - MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA, - UserManager.DISALLOW_ADJUST_VOLUME, + MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_UNMUTE_MICROPHONE), - isNull(Bundle.class) - ); + eq(false), eq(CAMERA_DISABLED_LOCALLY)); reset(mContext.userManagerInternal); // TODO Make sure restrictions are written to the file. @@ -1558,7 +1549,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(defaultRestrictions), - MockUtils.checkUserRestrictions() + eq(true) /* isDeviceOwner */, + eq(CAMERA_NOT_DISABLED) ); reset(mContext.userManagerInternal); @@ -1600,7 +1592,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(mContext.userManagerInternal, atLeast(1)).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(newDefaultEnabledRestriction), - MockUtils.checkUserRestrictions() + eq(true) /* isDeviceOwner */, + eq(CAMERA_NOT_DISABLED) ); reset(mContext.userManagerInternal); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java index 11f9ebb52f44..480be2e90ed2 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java @@ -16,13 +16,16 @@ package com.android.server.pm; +import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions; +import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions; + import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; - -import com.android.server.devicepolicy.DpmTestUtils; +import android.util.SparseArray; /** * Tests for {@link com.android.server.pm.UserRestrictionsUtils}. @@ -49,14 +52,14 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase { public void testIsEmpty() { assertTrue(UserRestrictionsUtils.isEmpty(null)); assertTrue(UserRestrictionsUtils.isEmpty(new Bundle())); - assertFalse(UserRestrictionsUtils.isEmpty(DpmTestUtils.newRestrictions("a"))); + assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a"))); } public void testClone() { Bundle in = new Bundle(); Bundle out = UserRestrictionsUtils.clone(in); assertNotSame(in, out); - DpmTestUtils.assertRestrictions(out, new Bundle()); + assertRestrictions(out, new Bundle()); out = UserRestrictionsUtils.clone(null); assertNotNull(out); @@ -64,16 +67,16 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase { } public void testMerge() { - Bundle a = DpmTestUtils.newRestrictions("a", "d"); - Bundle b = DpmTestUtils.newRestrictions("b", "d", "e"); + Bundle a = newRestrictions("a", "d"); + Bundle b = newRestrictions("b", "d", "e"); UserRestrictionsUtils.merge(a, b); - DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a); + assertRestrictions(newRestrictions("a", "b", "d", "e"), a); UserRestrictionsUtils.merge(a, null); - DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a); + assertRestrictions(newRestrictions("a", "b", "d", "e"), a); try { UserRestrictionsUtils.merge(a, a); @@ -114,25 +117,32 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase { final Bundle local = new Bundle(); final Bundle global = new Bundle(); - UserRestrictionsUtils.sortToGlobalAndLocal(null, global, local); + UserRestrictionsUtils.sortToGlobalAndLocal(null, false /* isDeviceOwner */, + UserManagerInternal.CAMERA_NOT_DISABLED, global, local); assertEquals(0, global.size()); assertEquals(0, local.size()); - UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, global, local); + UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false /* isDeviceOwner */, + UserManagerInternal.CAMERA_NOT_DISABLED, global, local); assertEquals(0, global.size()); assertEquals(0, local.size()); - UserRestrictionsUtils.sortToGlobalAndLocal(DpmTestUtils.newRestrictions( + // Restrictions set by DO. + UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions( UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_UNMUTE_MICROPHONE, UserManager.DISALLOW_USB_FILE_TRANSFER, UserManager.DISALLOW_CONFIG_TETHERING, UserManager.DISALLOW_OUTGOING_BEAM, - UserManager.DISALLOW_APPS_CONTROL - ), global, local); + UserManager.DISALLOW_APPS_CONTROL, + UserManager.ENSURE_VERIFY_APPS + ), true /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local); - DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions( + assertRestrictions(newRestrictions( + // This one is global no matter who sets it. + UserManager.ENSURE_VERIFY_APPS, + // These can be set by PO too, but when DO sets them, they're global. UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_UNMUTE_MICROPHONE, @@ -142,11 +152,117 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase { UserManager.DISALLOW_CONFIG_TETHERING ), global); - DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions( + assertRestrictions(newRestrictions( // They can be set by both DO/PO. UserManager.DISALLOW_OUTGOING_BEAM, UserManager.DISALLOW_APPS_CONTROL ), local); + + local.clear(); + global.clear(); + + // Restrictions set by PO. + UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions( + UserManager.DISALLOW_ADJUST_VOLUME, + UserManager.DISALLOW_UNMUTE_MICROPHONE, + UserManager.DISALLOW_USB_FILE_TRANSFER, + UserManager.DISALLOW_CONFIG_TETHERING, + UserManager.DISALLOW_OUTGOING_BEAM, + UserManager.DISALLOW_APPS_CONTROL, + UserManager.ENSURE_VERIFY_APPS + ), false /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local); + + assertRestrictions(newRestrictions( + // This one is global no matter who sets it. + UserManager.ENSURE_VERIFY_APPS + ), global); + + assertRestrictions(newRestrictions( + // These can be set by PO too, but when PO sets them, they're local. + UserManager.DISALLOW_ADJUST_VOLUME, + UserManager.DISALLOW_UNMUTE_MICROPHONE, + + // They can be set by both DO/PO. + UserManager.DISALLOW_OUTGOING_BEAM, + UserManager.DISALLOW_APPS_CONTROL, + + // These can only be set by DO. + UserManager.DISALLOW_USB_FILE_TRANSFER, + UserManager.DISALLOW_CONFIG_TETHERING + ), local); + + } + + public void testSortToLocalAndGlobalWithCameraDisabled() { + final Bundle local = new Bundle(); + final Bundle global = new Bundle(); + + UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false, + UserManagerInternal.CAMERA_DISABLED_GLOBALLY, global, local); + assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global); + assertEquals(0, local.size()); + global.clear(); + + UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false, + UserManagerInternal.CAMERA_DISABLED_LOCALLY, global, local); + assertEquals(0, global.size()); + assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), local); + } + + public void testMergeAll() { + SparseArray<Bundle> restrictions = new SparseArray<>(); + assertNull(UserRestrictionsUtils.mergeAll(restrictions)); + + restrictions.put(0, newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME)); + restrictions.put(1, newRestrictions(UserManager.DISALLOW_USB_FILE_TRANSFER)); + restrictions.put(2, newRestrictions(UserManager.DISALLOW_APPS_CONTROL)); + + Bundle result = UserRestrictionsUtils.mergeAll(restrictions); + assertRestrictions( + newRestrictions( + UserManager.DISALLOW_ADJUST_VOLUME, + UserManager.DISALLOW_USB_FILE_TRANSFER, + UserManager.DISALLOW_APPS_CONTROL), + result); + } + + public void testMoveRestriction() { + SparseArray<Bundle> localRestrictions = new SparseArray<>(); + SparseArray<Bundle> globalRestrictions = new SparseArray<>(); + + // User 0 has only local restrictions, nothing should change. + localRestrictions.put(0, newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME)); + // User 1 has a local restriction to be moved to global and some global already. Local + // restrictions should be removed for this user. + localRestrictions.put(1, newRestrictions(UserManager.ENSURE_VERIFY_APPS)); + globalRestrictions.put(1, newRestrictions(UserManager.DISALLOW_ADD_USER)); + // User 2 has a local restriction to be moved and one to leave local. + localRestrictions.put(2, newRestrictions( + UserManager.ENSURE_VERIFY_APPS, + UserManager.DISALLOW_CONFIG_VPN)); + + UserRestrictionsUtils.moveRestriction( + UserManager.ENSURE_VERIFY_APPS, localRestrictions, globalRestrictions); + + // Check user 0. + assertRestrictions( + newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME), + localRestrictions.get(0)); + assertNull(globalRestrictions.get(0)); + + // Check user 1. + assertNull(localRestrictions.get(1)); + assertRestrictions( + newRestrictions(UserManager.ENSURE_VERIFY_APPS, UserManager.DISALLOW_ADD_USER), + globalRestrictions.get(1)); + + // Check user 2. + assertRestrictions( + newRestrictions(UserManager.DISALLOW_CONFIG_VPN), + localRestrictions.get(2)); + assertRestrictions( + newRestrictions(UserManager.ENSURE_VERIFY_APPS), + globalRestrictions.get(2)); } public void testAreEqual() { @@ -172,33 +288,33 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase { assertFalse(UserRestrictionsUtils.areEqual( null, - DpmTestUtils.newRestrictions("a"))); + newRestrictions("a"))); assertFalse(UserRestrictionsUtils.areEqual( - DpmTestUtils.newRestrictions("a"), + newRestrictions("a"), null)); assertTrue(UserRestrictionsUtils.areEqual( - DpmTestUtils.newRestrictions("a"), - DpmTestUtils.newRestrictions("a"))); + newRestrictions("a"), + newRestrictions("a"))); assertFalse(UserRestrictionsUtils.areEqual( - DpmTestUtils.newRestrictions("a"), - DpmTestUtils.newRestrictions("a", "b"))); + newRestrictions("a"), + newRestrictions("a", "b"))); assertFalse(UserRestrictionsUtils.areEqual( - DpmTestUtils.newRestrictions("a", "b"), - DpmTestUtils.newRestrictions("a"))); + newRestrictions("a", "b"), + newRestrictions("a"))); assertFalse(UserRestrictionsUtils.areEqual( - DpmTestUtils.newRestrictions("b", "a"), - DpmTestUtils.newRestrictions("a", "a"))); + newRestrictions("b", "a"), + newRestrictions("a", "a"))); // Make sure false restrictions are handled correctly. - final Bundle a = DpmTestUtils.newRestrictions("a"); + final Bundle a = newRestrictions("a"); a.putBoolean("b", true); - final Bundle b = DpmTestUtils.newRestrictions("a"); + final Bundle b = newRestrictions("a"); b.putBoolean("b", false); assertFalse(UserRestrictionsUtils.areEqual(a, b)); |