Loading src/com/android/settings/users/UserDetailsSettings.java +42 −0 Original line number Diff line number Diff line Loading @@ -18,11 +18,13 @@ package com.android.settings.users; import static android.os.UserHandle.USER_NULL; import android.app.Activity; import android.app.ActivityManager; import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.pm.UserInfo; import android.multiuser.Flags; import android.os.Bundle; import android.os.RemoteException; import android.os.Trace; Loading @@ -30,6 +32,9 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.TwoStatePreference; Loading @@ -38,6 +43,7 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; Loading Loading @@ -80,9 +86,12 @@ public class UserDetailsSettings extends SettingsPreferenceFragment /** Whether to enable the app_copying fragment. */ private static final boolean SHOW_APP_COPYING_PREF = false; private static final int MESSAGE_PADDING = 20; @VisibleForTesting static final int REQUEST_CONFIRM_REMOVE = 1; private UserManager mUserManager; private UserCapabilities mUserCaps; private ActivityResultLauncher mUserRemovalCredentialConfirmationActivityResultLauncher; private boolean mGuestUserAutoCreated; private final AtomicBoolean mGuestCreationScheduled = new AtomicBoolean(); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); Loading Loading @@ -121,6 +130,11 @@ public class UserDetailsSettings extends SettingsPreferenceFragment com.android.internal.R.bool.config_guestUserAutoCreated); initialize(context, getArguments()); if (Flags.requirePinBeforeUserDeletion()) { mUserRemovalCredentialConfirmationActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> onRemoveUserConfirmationActivityLauncherResult(result)); } } @Override Loading Loading @@ -516,10 +530,38 @@ public class UserDetailsSettings extends SettingsPreferenceFragment } private void removeUser() { if (Flags.requirePinBeforeUserDeletion() && runUserRemovalKeyguardConfirmation()) { // User deletion will be handled when the credential authentication result is successful return; } mUserManager.removeUser(mUserInfo.id); finishFragment(); } /** * Shows keyguard validation activity if screen lock is set for the current user. If screen lock * is not set up, no activity is launched. * * @return true if the authentication activity has been launched. */ @VisibleForTesting boolean runUserRemovalKeyguardConfirmation() { final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(getActivity(), this); return builder .setActivityResultLauncher(mUserRemovalCredentialConfirmationActivityResultLauncher) .setRequestCode(REQUEST_CONFIRM_REMOVE) .setUserId(UserHandle.myUserId()) .show(); } private void onRemoveUserConfirmationActivityLauncherResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { mUserManager.removeUser(mUserInfo.id); finishFragment(); } } /** * @param isNewUser indicates if a user was created recently, for new users * AppRestrictionsFragment should set the default restrictions Loading src/com/android/settings/users/UserSettings.java +43 −3 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.MainSwitchBarController; import com.android.settings.widget.SettingsMainSwitchBar; Loading Loading @@ -151,7 +152,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final IntentFilter USER_REMOVED_INTENT_FILTER; private static final int DIALOG_CONFIRM_REMOVE = 1; @VisibleForTesting static final int DIALOG_CONFIRM_REMOVE = 1; private static final int DIALOG_ADD_USER = 2; // Dialogs with id 3 and 4 got removed private static final int DIALOG_USER_CANNOT_MANAGE = 5; Loading @@ -177,6 +179,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final int REQUEST_CHOOSE_LOCK = 10; private static final int REQUEST_EDIT_GUEST = 11; private static final int REQUEST_ADD_USER = 12; @VisibleForTesting static final int REQUEST_DELETE_USER = 13; static final int RESULT_GUEST_REMOVED = 100; Loading Loading @@ -213,6 +217,7 @@ public class UserSettings extends SettingsPreferenceFragment SparseArray<Bitmap> mUserIcons = new SparseArray<>(); private int mRemovingUserId = -1; private boolean mAddingUser; private boolean mUserRemovalCredentialConfirmationPending; private boolean mGuestUserAutoCreated; private String mConfigSupervisedUserCreationPackage; private String mAddingUserName; Loading Loading @@ -589,6 +594,13 @@ public class UserSettings extends SettingsPreferenceFragment } else if (mGuestUserAutoCreated && requestCode == REQUEST_EDIT_GUEST && resultCode == RESULT_GUEST_REMOVED) { scheduleGuestCreation(); } else if (Flags.requirePinBeforeUserDeletion() && requestCode == REQUEST_DELETE_USER) { if (resultCode == Activity.RESULT_OK) { removeUserNow(); } else { mRemovingUserId = -1; mUserRemovalCredentialConfirmationPending = false; } } else { mCreateUserDialogController.onActivityResult(requestCode, resultCode, data); mEditUserInfoController.onActivityResult(requestCode, resultCode, data); Loading Loading @@ -721,6 +733,11 @@ public class UserSettings extends SettingsPreferenceFragment UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (Flags.requirePinBeforeUserDeletion() && runUserRemovalKeyguardConfirmation()) { mUserRemovalCredentialConfirmationPending = true; return; } removeUserNow(); } } Loading Loading @@ -889,6 +906,21 @@ public class UserSettings extends SettingsPreferenceFragment } } /** * Shows keyguard validation activity if screen lock is set for a user being deleted. If screen * lock is not set up, no activity is launched. * * @return true if the authentication activity has been launched. */ @VisibleForTesting boolean runUserRemovalKeyguardConfirmation() { final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(getActivity(), this); return builder .setRequestCode(REQUEST_DELETE_USER) .show(); } private Dialog buildEditCurrentUserDialog() { final Activity activity = getActivity(); if (activity == null) { Loading Loading @@ -1000,7 +1032,13 @@ public class UserSettings extends SettingsPreferenceFragment private void removeUserNow() { if (mRemovingUserId == UserHandle.myUserId()) { removeThisUser(); } else { synchronized (mUserLock) { mRemovingUserId = -1; mUserRemovalCredentialConfirmationPending = false; } } else if (!Flags.requirePinBeforeUserDeletion()) { // This method is only called when a user deletes themselves so this part of code is // never executed and can be removed. ThreadUtils.postOnBackgroundThread(new Runnable() { @Override public void run() { Loading Loading @@ -1745,7 +1783,9 @@ public class UserSettings extends SettingsPreferenceFragment @Override public void onDismiss(DialogInterface dialog) { synchronized (mUserLock) { if (!mUserRemovalCredentialConfirmationPending) { mRemovingUserId = -1; } updateUserList(); if (mCreateUserDialogController.isActive()) { mCreateUserDialogController.finish(); Loading tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java +41 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; import static com.android.settings.users.UserDetailsSettings.REQUEST_CONFIRM_REMOVE; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeTrue; Loading Loading @@ -61,6 +63,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; Loading Loading @@ -88,7 +91,8 @@ import java.util.List; @Config(shadows = { ShadowUserManager.class, com.android.settings.testutils.shadow.ShadowFragment.class, ShadowDevicePolicyManager.class ShadowDevicePolicyManager.class, ShadowLockPatternUtils.class }) public class UserDetailsSettingsTest { Loading Loading @@ -627,6 +631,42 @@ public class UserDetailsSettingsTest { verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void runKeyguardConfirmation_userHasScreenLock_shouldLaunchAuthenticationActivity() { setupSelectedUser(); mFragment.mUserInfo = mUserInfo; mUserManager.setIsAdminUser(true); mUserManager.addProfile(new UserInfo(UserHandle.myUserId(), "Bob", null, UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN)); ShadowLockPatternUtils.setKeyguardStoredPasswordQuality( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); doNothing().when(mFragment).startActivityForResult(any(), anyInt(), any()); ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{UserHandle.myUserId()}); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isTrue(); verify(mFragment).startActivityForResult(any(Intent.class), eq(REQUEST_CONFIRM_REMOVE), any()); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void runKeyguardConfirmation_userHasNoScreenLock_shouldNotLaunchAuthentication() { setupSelectedUser(); mFragment.mUserInfo = mUserInfo; mUserManager.setIsAdminUser(true); mUserManager.addProfile(new UserInfo(UserHandle.myUserId(), "Bob", null, UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN)); ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{UserHandle.myUserId()}); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isFalse(); verify(mFragment, never()).startActivityForResult(any(Intent.class), eq(REQUEST_CONFIRM_REMOVE), any()); } @Test public void onPreferenceClick_removeClicked_canNotDelete_doNothing() { setupSelectedUser(); Loading tests/robotests/src/com/android/settings/users/UserSettingsTest.java +73 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; import static com.android.settings.users.UserSettings.DIALOG_CONFIRM_REMOVE; import static com.android.settings.users.UserSettings.REQUEST_DELETE_USER; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -37,6 +40,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.app.Dialog; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; Loading @@ -62,6 +66,7 @@ import android.text.SpannableStringBuilder; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Button; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; Loading @@ -73,6 +78,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; Loading Loading @@ -106,6 +112,7 @@ import java.util.List; ShadowUserManager.class, ShadowDevicePolicyManager.class, SettingsShadowResources.class, ShadowLockPatternUtils.class, com.android.settings.testutils.shadow.ShadowFragment.class, }) public class UserSettingsTest { Loading Loading @@ -419,6 +426,72 @@ public class UserSettingsTest { verify(menuItem, never()).setTitle(AdditionalMatchers.not(eq(defaultTitle))); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void removeUserSelf_userHasScreenlock_shouldAskForCredentials() { doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); ShadowLockPatternUtils.setKeyguardStoredPasswordQuality( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE); doNothing().when(mFragment).startActivityForResult(any(), anyInt(), any()); UserInfo user = new UserInfo(UserHandle.myUserId(), SECONDARY_USER_NAME, null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, UserManager.USER_TYPE_FULL_SECONDARY); doReturn(user).when(mUserManager).getUserInfo(anyInt()); doReturn(UserHandle.myUserId()).when(mUserManager).getCredentialOwnerProfile(anyInt()); doReturn(new int[]{UserHandle.myUserId()}).when(mUserManager) .getProfileIdsWithDisabled(UserHandle.myUserId()); Dialog confirmDialog = mFragment.onCreateDialog(DIALOG_CONFIRM_REMOVE); confirmDialog.show(); Button positiveButton = confirmDialog.findViewById(android.R.id.button1); assertThat(positiveButton).isNotNull(); positiveButton.performClick(); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isTrue(); verify(mFragment).startActivityForResult(any(Intent.class), eq(REQUEST_DELETE_USER), any()); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void removeUserSelf_userHasNoScreenlock_shouldNotAskForCredentials() { doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE); doNothing().when(mFragment).startActivityForResult(any(), anyInt(), any()); UserInfo user = new UserInfo(UserHandle.myUserId(), SECONDARY_USER_NAME, null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, UserManager.USER_TYPE_FULL_SECONDARY); doReturn(user).when(mUserManager).getUserInfo(anyInt()); doReturn(UserHandle.myUserId()).when(mUserManager).getCredentialOwnerProfile(anyInt()); doReturn(new int[]{UserHandle.myUserId()}).when(mUserManager) .getProfileIdsWithDisabled(UserHandle.myUserId()); Dialog confirmDialog = mFragment.onCreateDialog(DIALOG_CONFIRM_REMOVE); confirmDialog.show(); Button positiveButton = confirmDialog.findViewById(android.R.id.button1); assertThat(positiveButton).isNotNull(); positiveButton.performClick(); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isFalse(); verify(mFragment, never()).startActivityForResult(any(Intent.class), eq(REQUEST_DELETE_USER), any()); } @Test public void updateUserList_canAddUserAndSwitchUser_shouldShowAddUser() { mUserCapabilities.mCanAddUser = true; Loading Loading
src/com/android/settings/users/UserDetailsSettings.java +42 −0 Original line number Diff line number Diff line Loading @@ -18,11 +18,13 @@ package com.android.settings.users; import static android.os.UserHandle.USER_NULL; import android.app.Activity; import android.app.ActivityManager; import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.pm.UserInfo; import android.multiuser.Flags; import android.os.Bundle; import android.os.RemoteException; import android.os.Trace; Loading @@ -30,6 +32,9 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.TwoStatePreference; Loading @@ -38,6 +43,7 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; Loading Loading @@ -80,9 +86,12 @@ public class UserDetailsSettings extends SettingsPreferenceFragment /** Whether to enable the app_copying fragment. */ private static final boolean SHOW_APP_COPYING_PREF = false; private static final int MESSAGE_PADDING = 20; @VisibleForTesting static final int REQUEST_CONFIRM_REMOVE = 1; private UserManager mUserManager; private UserCapabilities mUserCaps; private ActivityResultLauncher mUserRemovalCredentialConfirmationActivityResultLauncher; private boolean mGuestUserAutoCreated; private final AtomicBoolean mGuestCreationScheduled = new AtomicBoolean(); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); Loading Loading @@ -121,6 +130,11 @@ public class UserDetailsSettings extends SettingsPreferenceFragment com.android.internal.R.bool.config_guestUserAutoCreated); initialize(context, getArguments()); if (Flags.requirePinBeforeUserDeletion()) { mUserRemovalCredentialConfirmationActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> onRemoveUserConfirmationActivityLauncherResult(result)); } } @Override Loading Loading @@ -516,10 +530,38 @@ public class UserDetailsSettings extends SettingsPreferenceFragment } private void removeUser() { if (Flags.requirePinBeforeUserDeletion() && runUserRemovalKeyguardConfirmation()) { // User deletion will be handled when the credential authentication result is successful return; } mUserManager.removeUser(mUserInfo.id); finishFragment(); } /** * Shows keyguard validation activity if screen lock is set for the current user. If screen lock * is not set up, no activity is launched. * * @return true if the authentication activity has been launched. */ @VisibleForTesting boolean runUserRemovalKeyguardConfirmation() { final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(getActivity(), this); return builder .setActivityResultLauncher(mUserRemovalCredentialConfirmationActivityResultLauncher) .setRequestCode(REQUEST_CONFIRM_REMOVE) .setUserId(UserHandle.myUserId()) .show(); } private void onRemoveUserConfirmationActivityLauncherResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { mUserManager.removeUser(mUserInfo.id); finishFragment(); } } /** * @param isNewUser indicates if a user was created recently, for new users * AppRestrictionsFragment should set the default restrictions Loading
src/com/android/settings/users/UserSettings.java +43 −3 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.MainSwitchBarController; import com.android.settings.widget.SettingsMainSwitchBar; Loading Loading @@ -151,7 +152,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final IntentFilter USER_REMOVED_INTENT_FILTER; private static final int DIALOG_CONFIRM_REMOVE = 1; @VisibleForTesting static final int DIALOG_CONFIRM_REMOVE = 1; private static final int DIALOG_ADD_USER = 2; // Dialogs with id 3 and 4 got removed private static final int DIALOG_USER_CANNOT_MANAGE = 5; Loading @@ -177,6 +179,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final int REQUEST_CHOOSE_LOCK = 10; private static final int REQUEST_EDIT_GUEST = 11; private static final int REQUEST_ADD_USER = 12; @VisibleForTesting static final int REQUEST_DELETE_USER = 13; static final int RESULT_GUEST_REMOVED = 100; Loading Loading @@ -213,6 +217,7 @@ public class UserSettings extends SettingsPreferenceFragment SparseArray<Bitmap> mUserIcons = new SparseArray<>(); private int mRemovingUserId = -1; private boolean mAddingUser; private boolean mUserRemovalCredentialConfirmationPending; private boolean mGuestUserAutoCreated; private String mConfigSupervisedUserCreationPackage; private String mAddingUserName; Loading Loading @@ -589,6 +594,13 @@ public class UserSettings extends SettingsPreferenceFragment } else if (mGuestUserAutoCreated && requestCode == REQUEST_EDIT_GUEST && resultCode == RESULT_GUEST_REMOVED) { scheduleGuestCreation(); } else if (Flags.requirePinBeforeUserDeletion() && requestCode == REQUEST_DELETE_USER) { if (resultCode == Activity.RESULT_OK) { removeUserNow(); } else { mRemovingUserId = -1; mUserRemovalCredentialConfirmationPending = false; } } else { mCreateUserDialogController.onActivityResult(requestCode, resultCode, data); mEditUserInfoController.onActivityResult(requestCode, resultCode, data); Loading Loading @@ -721,6 +733,11 @@ public class UserSettings extends SettingsPreferenceFragment UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (Flags.requirePinBeforeUserDeletion() && runUserRemovalKeyguardConfirmation()) { mUserRemovalCredentialConfirmationPending = true; return; } removeUserNow(); } } Loading Loading @@ -889,6 +906,21 @@ public class UserSettings extends SettingsPreferenceFragment } } /** * Shows keyguard validation activity if screen lock is set for a user being deleted. If screen * lock is not set up, no activity is launched. * * @return true if the authentication activity has been launched. */ @VisibleForTesting boolean runUserRemovalKeyguardConfirmation() { final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(getActivity(), this); return builder .setRequestCode(REQUEST_DELETE_USER) .show(); } private Dialog buildEditCurrentUserDialog() { final Activity activity = getActivity(); if (activity == null) { Loading Loading @@ -1000,7 +1032,13 @@ public class UserSettings extends SettingsPreferenceFragment private void removeUserNow() { if (mRemovingUserId == UserHandle.myUserId()) { removeThisUser(); } else { synchronized (mUserLock) { mRemovingUserId = -1; mUserRemovalCredentialConfirmationPending = false; } } else if (!Flags.requirePinBeforeUserDeletion()) { // This method is only called when a user deletes themselves so this part of code is // never executed and can be removed. ThreadUtils.postOnBackgroundThread(new Runnable() { @Override public void run() { Loading Loading @@ -1745,7 +1783,9 @@ public class UserSettings extends SettingsPreferenceFragment @Override public void onDismiss(DialogInterface dialog) { synchronized (mUserLock) { if (!mUserRemovalCredentialConfirmationPending) { mRemovingUserId = -1; } updateUserList(); if (mCreateUserDialogController.isActive()) { mCreateUserDialogController.finish(); Loading
tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java +41 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; import static com.android.settings.users.UserDetailsSettings.REQUEST_CONFIRM_REMOVE; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeTrue; Loading Loading @@ -61,6 +63,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; Loading Loading @@ -88,7 +91,8 @@ import java.util.List; @Config(shadows = { ShadowUserManager.class, com.android.settings.testutils.shadow.ShadowFragment.class, ShadowDevicePolicyManager.class ShadowDevicePolicyManager.class, ShadowLockPatternUtils.class }) public class UserDetailsSettingsTest { Loading Loading @@ -627,6 +631,42 @@ public class UserDetailsSettingsTest { verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void runKeyguardConfirmation_userHasScreenLock_shouldLaunchAuthenticationActivity() { setupSelectedUser(); mFragment.mUserInfo = mUserInfo; mUserManager.setIsAdminUser(true); mUserManager.addProfile(new UserInfo(UserHandle.myUserId(), "Bob", null, UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN)); ShadowLockPatternUtils.setKeyguardStoredPasswordQuality( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); doNothing().when(mFragment).startActivityForResult(any(), anyInt(), any()); ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{UserHandle.myUserId()}); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isTrue(); verify(mFragment).startActivityForResult(any(Intent.class), eq(REQUEST_CONFIRM_REMOVE), any()); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void runKeyguardConfirmation_userHasNoScreenLock_shouldNotLaunchAuthentication() { setupSelectedUser(); mFragment.mUserInfo = mUserInfo; mUserManager.setIsAdminUser(true); mUserManager.addProfile(new UserInfo(UserHandle.myUserId(), "Bob", null, UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN)); ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{UserHandle.myUserId()}); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isFalse(); verify(mFragment, never()).startActivityForResult(any(Intent.class), eq(REQUEST_CONFIRM_REMOVE), any()); } @Test public void onPreferenceClick_removeClicked_canNotDelete_doNothing() { setupSelectedUser(); Loading
tests/robotests/src/com/android/settings/users/UserSettingsTest.java +73 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_OK; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; import static com.android.settings.users.UserSettings.DIALOG_CONFIRM_REMOVE; import static com.android.settings.users.UserSettings.REQUEST_DELETE_USER; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -37,6 +40,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.app.Dialog; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; Loading @@ -62,6 +66,7 @@ import android.text.SpannableStringBuilder; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Button; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; Loading @@ -73,6 +78,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; Loading Loading @@ -106,6 +112,7 @@ import java.util.List; ShadowUserManager.class, ShadowDevicePolicyManager.class, SettingsShadowResources.class, ShadowLockPatternUtils.class, com.android.settings.testutils.shadow.ShadowFragment.class, }) public class UserSettingsTest { Loading Loading @@ -419,6 +426,72 @@ public class UserSettingsTest { verify(menuItem, never()).setTitle(AdditionalMatchers.not(eq(defaultTitle))); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void removeUserSelf_userHasScreenlock_shouldAskForCredentials() { doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); ShadowLockPatternUtils.setKeyguardStoredPasswordQuality( DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE); doNothing().when(mFragment).startActivityForResult(any(), anyInt(), any()); UserInfo user = new UserInfo(UserHandle.myUserId(), SECONDARY_USER_NAME, null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, UserManager.USER_TYPE_FULL_SECONDARY); doReturn(user).when(mUserManager).getUserInfo(anyInt()); doReturn(UserHandle.myUserId()).when(mUserManager).getCredentialOwnerProfile(anyInt()); doReturn(new int[]{UserHandle.myUserId()}).when(mUserManager) .getProfileIdsWithDisabled(UserHandle.myUserId()); Dialog confirmDialog = mFragment.onCreateDialog(DIALOG_CONFIRM_REMOVE); confirmDialog.show(); Button positiveButton = confirmDialog.findViewById(android.R.id.button1); assertThat(positiveButton).isNotNull(); positiveButton.performClick(); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isTrue(); verify(mFragment).startActivityForResult(any(Intent.class), eq(REQUEST_DELETE_USER), any()); } @Test @RequiresFlagsEnabled(Flags.FLAG_REQUIRE_PIN_BEFORE_USER_DELETION) public void removeUserSelf_userHasNoScreenlock_shouldNotAskForCredentials() { doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE); doNothing().when(mFragment).startActivityForResult(any(), anyInt(), any()); UserInfo user = new UserInfo(UserHandle.myUserId(), SECONDARY_USER_NAME, null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, UserManager.USER_TYPE_FULL_SECONDARY); doReturn(user).when(mUserManager).getUserInfo(anyInt()); doReturn(UserHandle.myUserId()).when(mUserManager).getCredentialOwnerProfile(anyInt()); doReturn(new int[]{UserHandle.myUserId()}).when(mUserManager) .getProfileIdsWithDisabled(UserHandle.myUserId()); Dialog confirmDialog = mFragment.onCreateDialog(DIALOG_CONFIRM_REMOVE); confirmDialog.show(); Button positiveButton = confirmDialog.findViewById(android.R.id.button1); assertThat(positiveButton).isNotNull(); positiveButton.performClick(); assertThat(mFragment.runUserRemovalKeyguardConfirmation()).isFalse(); verify(mFragment, never()).startActivityForResult(any(Intent.class), eq(REQUEST_DELETE_USER), any()); } @Test public void updateUserList_canAddUserAndSwitchUser_shouldShowAddUser() { mUserCapabilities.mCanAddUser = true; Loading