diff --git a/res/drawable/ic_admin_panel_settings.xml b/res/drawable/ic_admin_panel_settings.xml
new file mode 100644
index 00000000000..2d3a7f1f43d
--- /dev/null
+++ b/res/drawable/ic_admin_panel_settings.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3d73571e060..41d465a8be8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -401,6 +401,8 @@
Share
Add
+
+ Remove
Settings
@@ -6020,12 +6022,18 @@
Turn on phone calls & SMS
+
+ Give this user admin privileges
Delete user
Turn on phone calls & SMS?
Call and SMS history will be shared with this user.
+
+ Remove admin privileges?
+
+ Are you sure you want to remove this user\'s admin privileges?
Emergency information
diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml
index 2301bac1d42..068039cbd04 100644
--- a/res/xml/user_details_settings.xml
+++ b/res/xml/user_details_settings.xml
@@ -21,6 +21,10 @@
+
switchUser());
}
+ case DIALOG_CONFIRM_REVOKE_ADMIN:
+ return UserDialogs.createConfirmRevokeAdmin(getActivity(),
+ (dialog, which) -> updateUserAdminStatus(false));
}
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
@@ -277,6 +292,9 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
mRemoveUserPref = findPreference(KEY_REMOVE_USER);
mAppAndContentAccessPref = findPreference(KEY_APP_AND_CONTENT_ACCESS);
mAppCopyingPref = findPreference(KEY_APP_COPYING);
+ mGrantAdminPref = findPreference(KEY_GRANT_ADMIN);
+
+ mGrantAdminPref.setChecked(mUserInfo.isAdmin());
mSwitchUserPref.setTitle(
context.getString(com.android.settingslib.R.string.user_switch_to_user,
@@ -289,10 +307,15 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
mSwitchUserPref.setSelectable(true);
mSwitchUserPref.setOnPreferenceClickListener(this);
}
-
+ //TODO(b/261700461): remove preference for supervised user
+ //TODO(b/262371063): check whether multiple admins allowed, not for HSUM
+ if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isHeadlessSystemUserMode()) {
+ removePreference(KEY_GRANT_ADMIN);
+ }
if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls
removePreference(KEY_ENABLE_TELEPHONY);
removePreference(KEY_REMOVE_USER);
+ removePreference(KEY_GRANT_ADMIN);
removePreference(KEY_APP_AND_CONTENT_ACCESS);
removePreference(KEY_APP_COPYING);
} else {
@@ -339,6 +362,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
mRemoveUserPref.setOnPreferenceClickListener(this);
mPhonePref.setOnPreferenceChangeListener(this);
+ mGrantAdminPref.setOnPreferenceChangeListener(this);
mAppAndContentAccessPref.setOnPreferenceClickListener(this);
mAppCopyingPref.setOnPreferenceClickListener(this);
}
@@ -401,6 +425,20 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
mUserManager.setUserRestriction(UserManager.DISALLOW_SMS, !enabled, userHandle);
}
+ /**
+ * Sets admin status of selected user. Method is called when toggle in
+ * user details settings is switched.
+ * @param isSetAdmin indicates if user admin status needs to be set to true.
+ */
+ private void updateUserAdminStatus(boolean isSetAdmin) {
+ mGrantAdminPref.setChecked(isSetAdmin);
+ if (!isSetAdmin) {
+ mUserManager.revokeUserAdmin(mUserInfo.id);
+ } else if ((mUserInfo.flags & UserInfo.FLAG_ADMIN) == 0) {
+ mUserManager.setUserAdmin(mUserInfo.id);
+ }
+ }
+
private void removeUser() {
mUserManager.removeUser(mUserInfo.id);
finishFragment();
diff --git a/src/com/android/settings/users/UserDialogs.java b/src/com/android/settings/users/UserDialogs.java
index c2f2528530b..1e26effd5c3 100644
--- a/src/com/android/settings/users/UserDialogs.java
+++ b/src/com/android/settings/users/UserDialogs.java
@@ -202,4 +202,19 @@ public final class UserDialogs {
.setNegativeButton(android.R.string.cancel, null)
.create();
}
+
+ /**
+ * Creates a dialog to confirm that the admin privileges of the user should be revoked.
+ *
+ * @param onConfirmListener Callback object for positive action
+ */
+ public static Dialog createConfirmRevokeAdmin(Context context,
+ DialogInterface.OnClickListener onConfirmListener) {
+ return new AlertDialog.Builder(context)
+ .setTitle(R.string.user_revoke_admin_confirm_title)
+ .setMessage(R.string.user_revoke_admin_confirm_message)
+ .setPositiveButton(R.string.remove, onConfirmListener)
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ }
}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index da6339beb7e..54d0b452246 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -84,6 +84,7 @@ import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.users.EditUserInfoController;
+import com.android.settingslib.users.GrantAdminDialogController;
import com.android.settingslib.users.UserCreatingDialog;
import com.android.settingslib.utils.ThreadUtils;
@@ -156,6 +157,7 @@ public class UserSettings extends SettingsPreferenceFragment
private static final int DIALOG_CONFIRM_RESET_AND_RESTART_GUEST = 13;
private static final int DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL = 14;
private static final int DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL = 15;
+ private static final int DIALOG_GRANT_ADMIN = 16;
private static final int MESSAGE_UPDATE_LIST = 1;
private static final int MESSAGE_USER_CREATED = 2;
@@ -215,6 +217,9 @@ public class UserSettings extends SettingsPreferenceFragment
private static SparseArray sDarkDefaultUserBitmapCache = new SparseArray<>();
private MultiUserSwitchBarController mSwitchBarController;
+
+ private GrantAdminDialogController mGrantAdminDialogController =
+ new GrantAdminDialogController();
private EditUserInfoController mEditUserInfoController =
new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY);
private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
@@ -228,6 +233,7 @@ public class UserSettings extends SettingsPreferenceFragment
private CharSequence mPendingUserName;
private Drawable mPendingUserIcon;
+ private boolean mGrantAdmin;
// A place to cache the generated default avatar
private Drawable mDefaultIconDrawable;
@@ -287,7 +293,6 @@ public class UserSettings extends SettingsPreferenceFragment
mSwitchBarController = new MultiUserSwitchBarController(activity,
new MainSwitchBarController(switchBar), this /* listener */);
getSettingsLifecycle().addObserver(mSwitchBarController);
-
boolean openUserEditDialog = getIntent().getBooleanExtra(
EXTRA_OPEN_DIALOG_USER_PROFILE_EDITOR, false);
if (switchBar.isChecked() && openUserEditDialog) {
@@ -306,7 +311,7 @@ public class UserSettings extends SettingsPreferenceFragment
}
mGuestUserAutoCreated = getPrefContext().getResources().getBoolean(
- com.android.internal.R.bool.config_guestUserAutoCreated);
+ com.android.internal.R.bool.config_guestUserAutoCreated);
mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
activity, KEY_ADD_USER_WHEN_LOCKED);
@@ -712,18 +717,27 @@ public class UserSettings extends SettingsPreferenceFragment
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER);
if (!longMessageDisplayed) {
preferences.edit().putBoolean(
KEY_ADD_USER_LONG_MESSAGE_DISPLAYED,
true).apply();
}
+ //TODO(b/262371063): check whether multiple admins allowed,
+ // not for HSUM
+ if (UserManager.isHeadlessSystemUserMode()) {
+ showDialog(DIALOG_GRANT_ADMIN);
+ } else {
+ showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER);
+ }
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
return dlg;
}
+ case DIALOG_GRANT_ADMIN: {
+ return buildGrantAdminDialog();
+ }
case DIALOG_CHOOSE_USER_TYPE: {
List> data = new ArrayList>();
HashMap addUserItem = new HashMap();
@@ -931,6 +945,19 @@ public class UserSettings extends SettingsPreferenceFragment
return d;
}
+ private Dialog buildGrantAdminDialog() {
+ return mGrantAdminDialogController.createDialog(
+ getActivity(),
+ (grantAdmin) -> {
+ mGrantAdmin = grantAdmin;
+ showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER);
+ },
+ () -> {
+ mGrantAdmin = false;
+ }
+ );
+ }
+
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {
@@ -938,6 +965,8 @@ public class UserSettings extends SettingsPreferenceFragment
return SettingsEnums.DIALOG_USER_REMOVE;
case DIALOG_USER_CANNOT_MANAGE:
return SettingsEnums.DIALOG_USER_CANNOT_MANAGE;
+ case DIALOG_GRANT_ADMIN:
+ return SettingsEnums.DIALOG_GRANT_USER_ADMIN;
case DIALOG_ADD_USER:
return SettingsEnums.DIALOG_USER_ADD;
case DIALOG_CHOOSE_USER_TYPE:
@@ -1031,6 +1060,9 @@ public class UserSettings extends SettingsPreferenceFragment
userName,
mUserManager.USER_TYPE_FULL_SECONDARY,
0);
+ if (mGrantAdmin) {
+ mUserManager.setUserAdmin(user.id);
+ }
} else {
user = mUserManager.createRestrictedProfile(userName);
}
@@ -1351,20 +1383,20 @@ public class UserSettings extends SettingsPreferenceFragment
mGuestResetPreference.setVisible(true);
boolean isGuestFirstLogin = Settings.Secure.getIntForUser(
- getContext().getContentResolver(),
- SETTING_GUEST_HAS_LOGGED_IN,
- 0,
- UserHandle.myUserId()) <= 1;
+ getContext().getContentResolver(),
+ SETTING_GUEST_HAS_LOGGED_IN,
+ 0,
+ UserHandle.myUserId()) <= 1;
String guestExitSummary;
if (mUserCaps.mIsEphemeral) {
guestExitSummary = getContext().getString(
- R.string.guest_notification_ephemeral);
+ R.string.guest_notification_ephemeral);
} else if (isGuestFirstLogin) {
guestExitSummary = getContext().getString(
- R.string.guest_notification_non_ephemeral);
+ R.string.guest_notification_non_ephemeral);
} else {
guestExitSummary = getContext().getString(
- R.string.guest_notification_non_ephemeral_non_first_login);
+ R.string.guest_notification_non_ephemeral_non_first_login);
}
mGuestExitPreference.setSummary(guestExitSummary);
}
diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
index 14ca76fa9a7..5cd513ee147 100644
--- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
@@ -22,6 +22,7 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -84,6 +85,7 @@ import java.util.List;
})
public class UserDetailsSettingsTest {
+ private static final String KEY_GRANT_ADMIN = "user_grant_admin";
private static final String KEY_SWITCH_USER = "switch_user";
private static final String KEY_ENABLE_TELEPHONY = "enable_calling";
private static final String KEY_REMOVE_USER = "remove_user";
@@ -103,6 +105,8 @@ public class UserDetailsSettingsTest {
@Mock
private SwitchPreference mPhonePref;
@Mock
+ private SwitchPreference mGrantAdminPref;
+ @Mock
private Preference mRemoveUserPref;
@Mock
private Preference mAppAndContentAccessPref;
@@ -144,6 +148,7 @@ public class UserDetailsSettingsTest {
doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
doReturn(mSwitchUserPref).when(mFragment).findPreference(KEY_SWITCH_USER);
+ doReturn(mGrantAdminPref).when(mFragment).findPreference(KEY_GRANT_ADMIN);
doReturn(mPhonePref).when(mFragment).findPreference(KEY_ENABLE_TELEPHONY);
doReturn(mRemoveUserPref).when(mFragment).findPreference(KEY_REMOVE_USER);
doReturn(mAppAndContentAccessPref)
@@ -678,6 +683,38 @@ public class UserDetailsSettingsTest {
assertThat(result).isFalse();
}
+ @Test
+ public void initialize_userSelected_shouldShowGrantAdminPref_HSUM() {
+ setupSelectedUser();
+ ShadowUserManager.setIsHeadlessSystemUserMode(true);
+ mFragment.initialize(mActivity, mArguments);
+ assertTrue(UserManager.isHeadlessSystemUserMode());
+ verify(mFragment, never()).removePreference(KEY_GRANT_ADMIN);
+ }
+
+ @Test
+ public void initialize_userSelected_shouldNotShowGrantAdminPref() {
+ setupSelectedUser();
+ mFragment.initialize(mActivity, mArguments);
+ verify(mFragment).removePreference(KEY_GRANT_ADMIN);
+ }
+
+ @Test
+ public void initialize_mainUserSelected_shouldShowGrantAdminPref_HSUM() {
+ setupSelectedMainUser();
+ ShadowUserManager.setIsHeadlessSystemUserMode(true);
+ mFragment.initialize(mActivity, mArguments);
+ verify(mFragment).removePreference(KEY_GRANT_ADMIN);
+ }
+
+ @Test
+ public void initialize_guestSelected_shouldNotShowGrantAdminPref_HSUM() {
+ setupSelectedGuest();
+ ShadowUserManager.setIsHeadlessSystemUserMode(true);
+ mFragment.initialize(mActivity, mArguments);
+ verify(mFragment).removePreference(KEY_GRANT_ADMIN);
+ }
+
private void setupSelectedUser() {
mArguments.putInt("user_id", 1);
mUserInfo = new UserInfo(1, "Tom", null,