blob: 94a8f9fd6f33a0f8bba9d2138ac4376a4db460d4 [file]
/*
* Copyright (C) 2014 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 com.android.settings.users;
import static android.os.UserHandle.USER_NULL;
import static com.android.settings.flags.Flags.showUserDetailsSettingsForSelf;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.admin.DevicePolicyIdentifiers;
import android.app.admin.DevicePolicyManager;
import android.app.admin.EnforcingAdmin;
import android.app.admin.PolicyEnforcementInfo;
import android.app.admin.flags.Flags;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.widget.Toast;
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;
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;
import com.android.settingslib.utils.CustomDialogHelper;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Settings screen for configuring, deleting or switching to a specific user.
* It is shown when you tap on a user in the user management (UserSettings) screen.
*
* Arguments to this fragment must include the userId of the user (in EXTRA_USER_ID) for whom
* to display controls.
*/
// LINT.IfChange
public class UserDetailsSettings extends SettingsPreferenceFragment
implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
private static final String TAG = UserDetailsSettings.class.getSimpleName();
private static final String KEY_SWITCH_USER = "switch_user";
private static final String KEY_ENABLE_TELEPHONY_CALLING = "enable_calling";
private static final String KEY_REMOVE_USER = "remove_user";
private static final String KEY_GRANT_ADMIN = "user_grant_admin";
private static final String KEY_APP_AND_CONTENT_ACCESS = "app_and_content_access";
private static final String KEY_APP_COPYING = "app_copying";
/** Integer extra containing the userId to manage */
static final String EXTRA_USER_ID = "user_id";
private static final int DIALOG_CONFIRM_REMOVE = 1;
private static final int DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS = 2;
private static final int DIALOG_SETUP_USER = 3;
private static final int DIALOG_CONFIRM_RESET_GUEST = 4;
private static final int DIALOG_CONFIRM_RESET_GUEST_AND_SWITCH_USER = 5;
private static final int DIALOG_CONFIRM_REVOKE_ADMIN = 6;
private static final int DIALOG_CONFIRM_GRANT_ADMIN = 7;
private static final int DIALOG_DELETE_LAST_ADMIN = 8;
private static final int DIALOG_REVOKE_LAST_ADMIN = 9;
/** 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();
@VisibleForTesting
RestrictedPreference mSwitchUserPref;
private TwoStatePreference mPhonePref;
@VisibleForTesting
Preference mAppAndContentAccessPref;
@VisibleForTesting
Preference mAppCopyingPref;
@VisibleForTesting
Preference mRemoveUserPref;
@VisibleForTesting
TwoStatePreference mGrantAdminPref;
@VisibleForTesting
/** The user being studied (not the user doing the studying). */
UserInfo mUserInfo;
@Override
public int getMetricsCategory() {
return SettingsEnums.USER_DETAILS;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getActivity();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mUserCaps = UserCapabilities.create(context);
addPreferencesFromResource(R.xml.user_details_settings);
mGuestUserAutoCreated = getPrefContext().getResources().getBoolean(
com.android.internal.R.bool.config_guestUserAutoCreated);
initialize(context, getArguments());
mUserRemovalCredentialConfirmationActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> onRemoveUserConfirmationActivityLauncherResult(result));
}
@Override
public void onResume() {
super.onResume();
mSwitchUserPref.setEnabled(canSwitchUserNow() && mUserCaps.mUserSwitcherEnabled);
if (mUserInfo.isGuest() && mGuestUserAutoCreated) {
mRemoveUserPref.setEnabled((mUserInfo.flags & UserInfo.FLAG_INITIALIZED) != 0);
}
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference != null && preference.getKey() != null) {
mMetricsFeatureProvider.logSettingsTileClick(preference.getKey(), getMetricsCategory());
}
if (preference == mRemoveUserPref) {
mMetricsFeatureProvider.action(getActivity(),
UserMetricsUtils.getRemoveUserMetricCategory(mUserInfo));
if (canDeleteUser()) {
if (mUserInfo.isGuest()) {
showDialog(DIALOG_CONFIRM_RESET_GUEST);
} else if (isLastAdminUser()) {
showDialog(DIALOG_DELETE_LAST_ADMIN);
} else {
showDialog(DIALOG_CONFIRM_REMOVE);
}
return true;
}
} else if (preference == mSwitchUserPref) {
mMetricsFeatureProvider.action(getActivity(),
UserMetricsUtils.getSwitchUserMetricCategory(mUserInfo));
if (canSwitchUserNow()) {
if (shouldShowSetupPromptDialog()) {
showDialog(DIALOG_SETUP_USER);
} else if (mUserCaps.mIsGuest && mUserCaps.mIsEphemeral) {
// if we are switching away from a ephemeral guest then,
// show a dialog that guest user will be reset and then switch
// the user
showDialog(DIALOG_CONFIRM_RESET_GUEST_AND_SWITCH_USER);
} else {
switchUser();
}
return true;
}
} else if (preference == mAppAndContentAccessPref) {
openAppAndContentAccessScreen(false);
return true;
} else if (preference == mAppCopyingPref) {
openAppCopyingScreen();
return true;
}
return false;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mPhonePref) {
if (Boolean.TRUE.equals(newValue)) {
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_ENABLE_USER_CALL);
showDialog(DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS);
return false;
}
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_DISABLE_USER_CALL);
enableCallsAndSms(false);
} else if (preference == mGrantAdminPref) {
if (Boolean.FALSE.equals(newValue)) {
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_REVOKE_ADMIN_FROM_SETTINGS);
if (isLastAdminUser()) {
showDialog(DIALOG_REVOKE_LAST_ADMIN);
} else {
showDialog(DIALOG_CONFIRM_REVOKE_ADMIN);
}
} else {
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_GRANT_ADMIN_FROM_SETTINGS);
showDialog(DIALOG_CONFIRM_GRANT_ADMIN);
}
return false;
}
return true;
}
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {
case DIALOG_CONFIRM_REMOVE:
case DIALOG_CONFIRM_RESET_GUEST:
case DIALOG_CONFIRM_RESET_GUEST_AND_SWITCH_USER:
return SettingsEnums.DIALOG_USER_REMOVE;
case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS:
return SettingsEnums.DIALOG_USER_ENABLE_CALLING_AND_SMS;
case DIALOG_CONFIRM_REVOKE_ADMIN:
return SettingsEnums.DIALOG_REVOKE_USER_ADMIN;
case DIALOG_CONFIRM_GRANT_ADMIN:
return SettingsEnums.DIALOG_GRANT_USER_ADMIN;
case DIALOG_SETUP_USER:
return SettingsEnums.DIALOG_USER_SETUP;
case DIALOG_DELETE_LAST_ADMIN:
case DIALOG_REVOKE_LAST_ADMIN:
return SettingsEnums.DIALOG_CANNOT_REMOVE_LAST_ADMIN_USER;
default:
return 0;
}
}
@Override
public Dialog onCreateDialog(int dialogId) {
Context context = getActivity();
if (context == null) {
return null;
}
switch (dialogId) {
case DIALOG_CONFIRM_REMOVE:
return UserDialogs.createRemoveDialog(getActivity(), mUserInfo.id,
(dialog, which) -> removeUserWithAuthCheck());
case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS:
return UserDialogs.createEnablePhoneCallsAndSmsDialog(getActivity(),
(dialog, which) -> enableCallsAndSms(true));
case DIALOG_SETUP_USER:
return UserDialogs.createSetupUserDialog(getActivity(),
(dialog, which) -> {
if (canSwitchUserNow()) {
switchUser();
}
});
case DIALOG_CONFIRM_RESET_GUEST:
if (mGuestUserAutoCreated) {
return UserDialogs.createResetGuestDialog(getActivity(),
(dialog, which) -> resetGuest());
} else {
return UserDialogs.createRemoveGuestDialog(getActivity(),
(dialog, which) -> resetGuest());
}
case DIALOG_CONFIRM_RESET_GUEST_AND_SWITCH_USER:
if (mGuestUserAutoCreated) {
return UserDialogs.createResetGuestDialog(getActivity(),
(dialog, which) -> switchUser());
} else {
return UserDialogs.createRemoveGuestDialog(getActivity(),
(dialog, which) -> switchUser());
}
case DIALOG_CONFIRM_REVOKE_ADMIN:
return createRevokeAdminDialog(getContext());
case DIALOG_CONFIRM_GRANT_ADMIN:
return createGrantAdminDialog(getContext());
case DIALOG_DELETE_LAST_ADMIN:
return createDeleteLastAdminDialog(getContext());
case DIALOG_REVOKE_LAST_ADMIN:
return createRevokeLastAdminDialog(getContext());
}
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
/**
* Creates dialog to confirm revoking admin rights.
* @return created confirmation dialog
*/
private Dialog createRevokeAdminDialog(Context context) {
CustomDialogHelper dialogHelper = new CustomDialogHelper(context);
dialogHelper.setIcon(
context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
if (mUserInfo.id == UserHandle.myUserId()) {
dialogHelper.setTitle(R.string.user_revoke_admin_for_self_confirm_title);
dialogHelper.setMessage(R.string.user_revoke_admin_for_self_confirm_message);
} else {
dialogHelper.setTitle(R.string.user_revoke_admin_confirm_title);
dialogHelper.setMessage(R.string.user_revoke_admin_confirm_message);
}
dialogHelper.setMessagePadding(MESSAGE_PADDING);
dialogHelper.setPositiveButton(R.string.remove, view -> {
updateUserAdminStatus(false);
dialogHelper.getDialog().dismiss();
});
dialogHelper.setBackButton(R.string.cancel, view -> {
dialogHelper.getDialog().dismiss();
});
return dialogHelper.getDialog();
}
/**
* Creates dialog to confirm granting admin rights.
*
* @return created confirmation dialog
*/
private Dialog createGrantAdminDialog(Context context) {
CustomDialogHelper dialogHelper = new CustomDialogHelper(context);
dialogHelper.setIcon(
context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
dialogHelper.setTitle(com.android.settingslib.R.string.user_grant_admin_title);
dialogHelper.setMessage(com.android.settingslib.R.string.user_grant_admin_message);
dialogHelper.setMessagePadding(MESSAGE_PADDING);
dialogHelper.setPositiveButton(com.android.settingslib.R.string.user_grant_admin_button,
view -> {
updateUserAdminStatus(true);
dialogHelper.getDialog().dismiss();
});
dialogHelper.setBackButton(R.string.cancel, view -> {
dialogHelper.getDialog().dismiss();
});
return dialogHelper.getDialog();
}
private Dialog createDeleteLastAdminDialog(Context context) {
CustomDialogHelper dialogHelper = new CustomDialogHelper(context);
dialogHelper.setIcon(context.getDrawable(R.drawable.ic_person_remove));
dialogHelper.setTitle(R.string.user_remove_last_admin_title);
dialogHelper.setMessage(R.string.user_remove_last_admin_message);
dialogHelper.setMessagePadding(MESSAGE_PADDING);
dialogHelper.setPositiveButton(R.string.okay,
view -> {
dialogHelper.getDialog().dismiss();
});
return dialogHelper.getDialog();
}
private Dialog createRevokeLastAdminDialog(Context context) {
CustomDialogHelper dialogHelper = new CustomDialogHelper(context);
dialogHelper.setIcon(context.getDrawable(R.drawable.ic_remove_moderator));
dialogHelper.setTitle(R.string.user_revoke_last_admin_title);
dialogHelper.setMessage(R.string.user_revoke_last_admin_message);
dialogHelper.setMessagePadding(MESSAGE_PADDING);
dialogHelper.setPositiveButton(R.string.okay,
view -> {
dialogHelper.getDialog().dismiss();
});
return dialogHelper.getDialog();
}
/**
* Erase the current guest user and create a new one in the background. UserSettings will
* handle guest creation after receiving the {@link UserSettings.RESULT_GUEST_REMOVED} result.
*/
private void resetGuest() {
// Just to be safe, check that the selected user is a guest
if (!mUserInfo.isGuest()) {
return;
}
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
mUserManager.removeUser(mUserInfo.id);
setResult(UserSettings.RESULT_GUEST_REMOVED);
finishFragment();
}
@VisibleForTesting
@Override
protected void showDialog(int dialogId) {
super.showDialog(dialogId);
}
@VisibleForTesting
void initialize(Context context, Bundle arguments) {
int userId = arguments != null ? arguments.getInt(EXTRA_USER_ID, USER_NULL) : USER_NULL;
if (userId == USER_NULL) {
throw new IllegalStateException("Arguments to this fragment must contain the user id");
}
boolean isNewUser =
arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false);
mUserInfo = mUserManager.getUserInfo(userId);
mSwitchUserPref = findPreference(KEY_SWITCH_USER);
mPhonePref = findPreference(KEY_ENABLE_TELEPHONY_CALLING);
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.setVisible(mUserCaps.mUserSwitchingUiEnabled
&& UserHandle.myUserId() != mUserInfo.id);
mSwitchUserPref.setTitle(
context.getString(com.android.settingslib.R.string.user_switch_to_user,
mUserInfo.name));
if (mUserCaps.mDisallowSwitchUser) {
if (Flags.policyTransparencyRefactorEnabled()) {
mSwitchUserPref.setDisabledByAdmin(
mUserCaps.mDisallowSwitchUserRestrictionEnforcementInfo
.getMostImportantEnforcingAdmin());
} else {
mSwitchUserPref.setDisabledByAdmin(
RestrictedLockUtilsInternal.getDeviceOwner(context));
}
} else {
mSwitchUserPref.setDisabledByAdmin((EnforcingAdmin) null);
mSwitchUserPref.setEnabled(mUserCaps.mUserSwitcherEnabled);
mSwitchUserPref.setSelectable(mUserCaps.mUserSwitcherEnabled);
mSwitchUserPref.setOnPreferenceClickListener(this);
}
if (android.multiuser.Flags.unicornModeRefactoringForHsumReadOnly()) {
if (isChangingAdminStatusRestricted()) {
removePreference(KEY_GRANT_ADMIN);
}
} else {
if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled()
|| mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
mUserInfo.getUserHandle()) || !mUserManager.isAdminUser()) {
removePreference(KEY_GRANT_ADMIN);
}
}
if (mUserManager.isAdminUser() || canRemoveSelf()) {
if (mUserInfo.isGuest()) {
mRemoveUserPref.setTitle(
mGuestUserAutoCreated
? com.android.settingslib.R.string.guest_reset_guest
: com.android.settingslib.R.string.guest_exit_guest);
if (mGuestUserAutoCreated) {
mRemoveUserPref.setEnabled((mUserInfo.flags & UserInfo.FLAG_INITIALIZED) != 0);
}
} else {
mRemoveUserPref.setTitle(R.string.user_remove_user);
}
mRemoveUserPref.setOnPreferenceClickListener(this);
} else {
removePreference(KEY_REMOVE_USER);
}
if (!mUserManager.isAdminUser()) { // not allow calls for non admin users.
removePreference(KEY_ENABLE_TELEPHONY_CALLING);
removePreference(KEY_APP_AND_CONTENT_ACCESS);
removePreference(KEY_APP_COPYING);
} else {
if (!Utils.isVoiceCapable(context)) { // no telephony
removePreference(KEY_ENABLE_TELEPHONY_CALLING);
}
if (mUserInfo.isMain()) {
removePreference(KEY_ENABLE_TELEPHONY_CALLING);
}
if (mUserInfo.isRestricted()) {
removePreference(KEY_ENABLE_TELEPHONY_CALLING);
if (isNewUser) {
// for newly created restricted users we should open the apps and content access
// screen to initialize the default restrictions
openAppAndContentAccessScreen(true);
}
} else {
removePreference(KEY_APP_AND_CONTENT_ACCESS);
}
if (mUserInfo.isGuest()) {
removePreference(KEY_ENABLE_TELEPHONY_CALLING);
if (!SHOW_APP_COPYING_PREF) {
removePreference(KEY_APP_COPYING);
}
} else {
mPhonePref.setChecked(!mUserManager.hasUserRestriction(
UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId)));
removePreference(KEY_APP_COPYING);
}
// Remove preference KEY_REMOVE_USER if DISALLOW_REMOVE_USER restriction is set
// on the current user or the user selected in user details settings is a main user.
if (isRemoveUserDisallowed(context) || mUserInfo.isMain()) {
removePreference(KEY_REMOVE_USER);
}
mPhonePref.setOnPreferenceChangeListener(this);
mGrantAdminPref.setOnPreferenceChangeListener(this);
mAppAndContentAccessPref.setOnPreferenceClickListener(this);
mAppCopyingPref.setOnPreferenceClickListener(this);
}
}
@VisibleForTesting
boolean canDeleteUser() {
if ((!mUserManager.isAdminUser() && !canRemoveSelf()) || mUserInfo.isMain()) {
return false;
}
Context context = getActivity();
if (context == null) {
return false;
}
return !isRemoveUserDisabledByAdmin(context);
}
private boolean isRemoveUserDisabledByAdmin(Context context) {
if (Flags.policyTransparencyRefactorEnabled()) {
PolicyEnforcementInfo info = getEnforcingAdminInfoForRestriction(context,
UserManager.DISALLOW_REMOVE_USER);
if (info.getAllAdmins().isEmpty() || info.isOnlyEnforcedBySystem()) {
return false;
}
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
context,
info.getMostImportantEnforcingAdmin(),
UserManager.DISALLOW_REMOVE_USER);
return true;
} else {
final RestrictedLockUtils.EnforcedAdmin removeDisallowedAdmin =
RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context,
UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
if (removeDisallowedAdmin != null) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context,
removeDisallowedAdmin);
return true;
}
return false;
}
}
@VisibleForTesting
boolean canSwitchUserNow() {
return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK;
}
@VisibleForTesting
void switchUser() {
Trace.beginSection("UserDetailSettings.switchUser");
try {
if (mUserCaps.mIsGuest && mUserCaps.mIsEphemeral) {
int guestUserId = UserHandle.myUserId();
// Using markGuestForDeletion allows us to create a new guest before this one is
// fully removed.
boolean marked = mUserManager.markGuestForDeletion(guestUserId);
if (!marked) {
Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
return;
}
}
ActivityManager.getService().switchUser(mUserInfo.id);
} catch (RemoteException re) {
Log.e(TAG, "Error while switching to other user.");
} finally {
Trace.endSection();
finishFragment();
}
}
private void enableCallsAndSms(boolean enabled) {
mPhonePref.setChecked(enabled);
int[] userProfiles = mUserManager.getProfileIdsWithDisabled(mUserInfo.id);
for (int userId : userProfiles) {
UserHandle user = UserHandle.of(userId);
mUserManager.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, !enabled, user);
mUserManager.setUserRestriction(UserManager.DISALLOW_SMS, !enabled, user);
}
}
/**
* 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);
if (mUserInfo.id == UserHandle.myUserId()) {
// Hides the toggle after an admin self-revokes their admin status, as they can no
// longer modify it.
mGrantAdminPref.setVisible(false);
}
} else if ((mUserInfo.flags & UserInfo.FLAG_ADMIN) == 0) {
mUserManager.setUserAdmin(mUserInfo.id);
}
}
private void removeUserWithAuthCheck() {
if (runUserRemovalKeyguardConfirmation()) {
// User deletion will be handled when the credential authentication result is successful
return;
}
removeUser();
}
private void removeUser() {
Toast.makeText(getPrefContext(), R.string.user_deleted_confirmation_toast,
Toast.LENGTH_LONG).show();
if (mUserInfo.id == UserHandle.myUserId()) {
removeThisUser();
} else {
mUserManager.removeUser(mUserInfo.id);
finishFragment();
}
}
private void removeThisUser() {
if (!canSwitchUserNow()) {
Log.w(TAG, "Cannot remove current user when switching is disabled");
return;
}
try {
mUserManager.removeUserWhenPossible(
UserHandle.of(UserHandle.myUserId()), /* overrideDevicePolicy= */ false);
ActivityManager.getService().switchUser(
mUserManager.getPreviousForegroundUser().getIdentifier());
} catch (RemoteException re) {
Log.e(TAG, "Unable to remove self user");
}
}
/**
* 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) {
removeUser();
}
}
/**
* @param isNewUser indicates if a user was created recently, for new users
* AppRestrictionsFragment should set the default restrictions
*/
private void openAppAndContentAccessScreen(boolean isNewUser) {
Bundle extras = new Bundle();
extras.putInt(AppRestrictionsFragment.EXTRA_USER_ID, mUserInfo.id);
extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, isNewUser);
new SubSettingLauncher(getContext())
.setDestination(AppRestrictionsFragment.class.getName())
.setArguments(extras)
.setTitleRes(R.string.user_restrictions_title)
.setSourceMetricsCategory(getMetricsCategory())
.launch();
}
private void openAppCopyingScreen() {
if (!SHOW_APP_COPYING_PREF) {
return;
}
final Bundle extras = new Bundle();
extras.putInt(AppRestrictionsFragment.EXTRA_USER_ID, mUserInfo.id);
new SubSettingLauncher(getContext())
.setDestination(AppCopyFragment.class.getName())
.setArguments(extras)
.setTitleRes(R.string.user_copy_apps_menu_title)
.setSourceMetricsCategory(getMetricsCategory())
.launch();
}
private boolean isSecondaryUser(UserInfo user) {
return UserManager.USER_TYPE_FULL_SECONDARY.equals(user.userType);
}
private boolean shouldShowSetupPromptDialog() {
// TODO: FLAG_INITIALIZED is set when a user is switched to for the first time,
// but what we would really need here is a flag that shows if the setup process was
// completed. After the user cancels the setup process, mUserInfo.isInitialized() will
// return true so there will be no setup prompt dialog shown to the user anymore.
return isSecondaryUser(mUserInfo) && !mUserInfo.isInitialized();
}
/**
* Determines if changing admin status is restricted.
*
* <p>Admin status change is restricted under the following conditions of current & target user.
*
* <ul>
* <li>The <b>current</b> user is NOT an admin user.</li>
* <li>OR multiple admin support is NOT enabled.</li>
* <li>OR the <b>current</b> user has DISALLOW_GRANT_ADMIN restriction applied</li>
*
* <li>OR the <b>target</b> user ('mUserInfo') is a main user</li>
* <li>OR the <b>target</b> user ('mUserInfo') is not of type
* {@link UserManager#USER_TYPE_FULL_SECONDARY}</li>
* <li>OR the <b>target</b> user ('mUserInfo') has DISALLOW_GRANT_ADMIN restriction.</li>
* </ul>
*
* @return true if changing admin status is restricted, false otherwise
*/
private boolean isChangingAdminStatusRestricted() {
boolean currentUserRestricted = !mUserManager.isAdminUser()
|| !UserManager.isMultipleAdminEnabled()
|| mUserManager.hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN);
boolean targetUserRestricted = mUserInfo.isMain()
|| !(UserManager.USER_TYPE_FULL_SECONDARY.equals(mUserInfo.userType))
|| mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
mUserInfo.getUserHandle());
return currentUserRestricted || targetUserRestricted;
}
private boolean canRemoveSelf() {
boolean isCurrentUser = (UserHandle.myUserId() == mUserInfo.id);
return showUserDetailsSettingsForSelf() && isCurrentUser;
}
private boolean isRemoveUserDisallowed(Context context) {
if (Flags.policyTransparencyRefactorEnabled()) {
PolicyEnforcementInfo info = getEnforcingAdminInfoForRestriction(context,
UserManager.DISALLOW_REMOVE_USER);
return !info.getAllAdmins().isEmpty();
} else {
return RestrictedLockUtilsInternal.hasBaseUserRestriction(context,
UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
}
}
/**
* Gets the policy enforcement info for a user restriction on the current user. This
* information also contains the system restrictions for the current user.
*/
private PolicyEnforcementInfo getEnforcingAdminInfoForRestriction(Context context,
String userRestriction) {
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
return new PolicyEnforcementInfo(Collections.emptyList());
}
return dpm.getEnforcingAdminsForPolicy(
DevicePolicyIdentifiers.getIdentifierForUserRestriction(
userRestriction), UserHandle.myUserId());
}
private boolean isLastAdminUser() {
return mUserManager.getUserRemovability(mUserInfo.id)
== UserManager.REMOVE_RESULT_ERROR_LAST_ADMIN_USER;
}
}
// LINT.ThenChange(UserDetailsSettingsScreenApi.kt)