diff --git a/api/current.txt b/api/current.txt index 657c7c3925613..39493013f0dee 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28558,6 +28558,7 @@ package android.os { field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls"; field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user"; field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot"; + field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; diff --git a/api/system-current.txt b/api/system-current.txt index 459dc59966274..e312e717273e8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -30576,6 +30576,7 @@ package android.os { field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls"; field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user"; field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot"; + field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; diff --git a/api/test-current.txt b/api/test-current.txt index d78973f16d82a..c91c60a7e0944 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -28567,6 +28567,7 @@ package android.os { field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls"; field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user"; field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot"; + field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f01f59715f2f3..fe834a14e8f16 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -528,6 +528,21 @@ public class UserManager { */ public static final String DISALLOW_DATA_ROAMING = "no_data_roaming"; + /** + * Specifies if a user is not allowed to change their icon. Device owner and profile owner + * can set this restriction. When it is set by device owner, only the target user will be + * affected. The default value is false. + * + *

Key for user restrictions. + * + *

Type: Boolean + * + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_SET_USER_ICON = "no_set_user_icon"; + /** * Allows apps in the parent profile to handle web links from the managed profile. * diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index 898b6cffa9d4c..f76533697cda2 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Bitmap; /** * @hide Only for use within the system server. @@ -81,4 +82,13 @@ public abstract class UserManagerInternal { * whether the user is managed by profile owner. */ public abstract void setUserManaged(int userId, boolean isManaged); + + /** + * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to omit + * restriction check, because DevicePolicyManager must always be able to set user icon + * regardless of any restriction. + * Also called by {@link com.android.server.pm.UserManagerService} because the logic of setting + * the icon is in this method. + */ + public abstract void setUserIcon(int userId, Bitmap bitmap); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 13d7c35df5d4c..f747cf9979cb5 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -729,23 +729,15 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserIcon(int userId, Bitmap bitmap) { checkManageUsersPermission("update users"); - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mPackagesLock) { - UserData userData = getUserDataNoChecks(userId); - if (userData == null || userData.info.partial) { - Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId); - return; - } - writeBitmapLP(userData.info, bitmap); - writeUserLP(userData); - } - sendUserInfoChangedBroadcast(userId); - } finally { - Binder.restoreCallingIdentity(ident); + if (hasUserRestriction(UserManager.DISALLOW_SET_USER_ICON, userId)) { + Log.w(LOG_TAG, "Cannot set user icon. DISALLOW_SET_USER_ICON is enabled."); + return; } + mLocalService.setUserIcon(userId, bitmap); } + + private void sendUserInfoChangedBroadcast(int userId) { Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED); changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); @@ -2894,6 +2886,25 @@ public class UserManagerService extends IUserManager.Stub { mIsUserManaged.put(userId, isManaged); } } + + @Override + public void setUserIcon(int userId, Bitmap bitmap) { + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mPackagesLock) { + UserData userData = getUserDataNoChecks(userId); + if (userData == null || userData.info.partial) { + Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId); + return; + } + writeBitmapLP(userData.info, bitmap); + writeUserLP(userData); + } + sendUserInfoChangedBroadcast(userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } private class Shell extends ShellCommand { diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index f0ed7907770d8..87f505d24ccd6 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -92,7 +92,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_RECORD_AUDIO, UserManager.DISALLOW_CAMERA, UserManager.DISALLOW_RUN_IN_BACKGROUND, - UserManager.DISALLOW_DATA_ROAMING + UserManager.DISALLOW_DATA_ROAMING, + UserManager.DISALLOW_SET_USER_ICON ); /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1ada0ace44299..d3f9aebd2fb7b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -7028,7 +7028,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = UserHandle.getCallingUserId(); long id = mInjector.binderClearCallingIdentity(); try { - mUserManager.setUserIcon(userId, icon); + mUserManagerInternal.setUserIcon(userId, icon); } finally { mInjector.binderRestoreCallingIdentity(id); }