1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.profiles; 18 19 import static android.os.UserManager.DISALLOW_REMOVE_USER; 20 21 import static com.android.car.settings.common.PreferenceController.AVAILABLE; 22 import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING; 23 import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE; 24 import static com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment.DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG; 25 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm; 26 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByUm; 27 28 import android.content.Context; 29 import android.content.pm.UserInfo; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.widget.Toast; 33 34 import androidx.annotation.VisibleForTesting; 35 36 import com.android.car.settings.R; 37 import com.android.car.settings.common.ConfirmationDialogFragment; 38 import com.android.car.settings.common.ErrorDialog; 39 import com.android.car.settings.common.FragmentController; 40 import com.android.car.settings.enterprise.EnterpriseUtils; 41 42 /** 43 * Consolidates profile removal logic into one handler so we can have consistent logic across 44 * various parts of the Settings app. 45 */ 46 public class RemoveProfileHandler { 47 @VisibleForTesting 48 static final String REMOVE_PROFILE_DIALOG_TAG = "RemoveProfileDialogFragment"; 49 50 private final Context mContext; 51 private final ProfileHelper mProfileHelper; 52 private final UserManager mUserManager; 53 private final FragmentController mFragmentController; 54 55 private UserInfo mUserInfo; 56 57 @VisibleForTesting 58 ConfirmationDialogFragment.ConfirmListener mRemoveConfirmListener; 59 RemoveProfileHandler(Context context, ProfileHelper profileHelper, UserManager userManager, FragmentController fragmentController)60 public RemoveProfileHandler(Context context, ProfileHelper profileHelper, 61 UserManager userManager, FragmentController fragmentController) { 62 mContext = context; 63 mProfileHelper = profileHelper; 64 mUserManager = userManager; 65 mFragmentController = fragmentController; 66 } 67 68 /** 69 * Sets the profile info to be handled for removal 70 * @param userInfo UserInfo of the profile to remove 71 */ setUserInfo(UserInfo userInfo)72 public void setUserInfo(UserInfo userInfo) { 73 mUserInfo = userInfo; 74 75 mRemoveConfirmListener = arguments -> { 76 String profileType = arguments.getString(ProfilesDialogProvider.KEY_PROFILE_TYPE); 77 if (profileType.equals(ProfilesDialogProvider.LAST_ADMIN)) { 78 mFragmentController.launchFragment( 79 ChooseNewAdminFragment.newInstance(userInfo)); 80 } else { 81 int removeProfileResult = mProfileHelper.removeProfile(mContext, mUserInfo); 82 if (removeProfileResult == ProfileHelper.REMOVE_PROFILE_RESULT_SUCCESS) { 83 mFragmentController.goBack(); 84 } else { 85 // If failed, need to show error dialog for users. 86 mFragmentController.showDialog( 87 ErrorDialog.newInstance(mProfileHelper.getErrorMessageForProfileResult( 88 removeProfileResult)), null); 89 } 90 } 91 }; 92 } 93 94 /** 95 * Resets listeners as they can get unregistered with certain configuration changes. 96 */ resetListeners()97 public void resetListeners() { 98 ConfirmationDialogFragment removeProfileDialog = 99 (ConfirmationDialogFragment) mFragmentController.findDialogByTag( 100 REMOVE_PROFILE_DIALOG_TAG); 101 102 ConfirmationDialogFragment.resetListeners( 103 removeProfileDialog, 104 mRemoveConfirmListener, 105 /* rejectListener= */ null, 106 /* neutralListener= */ null); 107 } 108 109 /** 110 * Checks to see if the current active profile can delete the requested profile. 111 * @param userInfo UserInfo of the profile to delete 112 * @return True if the profile can be deleted by the current active profile. False otherwise. 113 */ canRemoveProfile(UserInfo userInfo)114 public boolean canRemoveProfile(UserInfo userInfo) { 115 return !mUserManager.hasUserRestriction(DISALLOW_REMOVE_USER) 116 && !isSystemOrDemoUser(userInfo); 117 } 118 119 /** 120 * Checks to see if the current active profile is {@code USER_SYSTEM} or has user type 121 * {@code USER_TYPE_FULL_DEMO} 122 */ isSystemOrDemoUser(UserInfo userInfo)123 public boolean isSystemOrDemoUser(UserInfo userInfo) { 124 return userInfo.id == UserHandle.USER_SYSTEM 125 || mUserManager.isDemoUser(); 126 } 127 128 /** 129 * Returns {@code PreferenceController.AVAILABLE} when preference should be available, 130 * {@code PreferenceController.DISABLED_FOR_PROFILE} when preference should be unavailable, 131 * {@code PreferenceController.AVAILABLE_FOR_VIEWING} when preference is visible but 132 * disabled. 133 * 134 * @param context to get user restriction 135 * @param userInfo target user to check 136 * @param availableForCurrentProcessUser when true, only available for current user process. 137 * When false, disabled for current user process. 138 */ getAvailabilityStatus(Context context, UserInfo userInfo, boolean availableForCurrentProcessUser)139 public int getAvailabilityStatus(Context context, UserInfo userInfo, 140 boolean availableForCurrentProcessUser) { 141 boolean allowCurrentProcess = 142 !(availableForCurrentProcessUser ^ mProfileHelper.isCurrentProcessUser(userInfo)); 143 if (canRemoveProfile(userInfo) && allowCurrentProcess) { 144 return AVAILABLE; 145 } 146 if (!allowCurrentProcess || isSystemOrDemoUser(userInfo) 147 || hasUserRestrictionByUm(context, DISALLOW_REMOVE_USER)) { 148 return DISABLED_FOR_PROFILE; 149 } 150 return AVAILABLE_FOR_VIEWING; 151 } 152 153 /** 154 * Show the remove profile confirmation dialog. This will handle edge cases such as removing 155 * the last profile or removing the last admin profile. 156 */ showConfirmRemoveProfileDialog()157 public void showConfirmRemoveProfileDialog() { 158 boolean isLastProfile = mProfileHelper.getAllPersistentProfiles().size() == 1; 159 boolean isLastAdmin = mUserInfo.isAdmin() 160 && mProfileHelper.getAllAdminProfiles().size() == 1; 161 162 ConfirmationDialogFragment dialogFragment; 163 164 if (isLastProfile) { 165 dialogFragment = ProfilesDialogProvider.getConfirmRemoveLastProfileDialogFragment( 166 mContext, mRemoveConfirmListener, /* rejectListener= */ null); 167 } else if (isLastAdmin) { 168 dialogFragment = ProfilesDialogProvider.getConfirmRemoveLastAdminDialogFragment( 169 mContext, mRemoveConfirmListener, /* rejectListener= */ null); 170 } else { 171 dialogFragment = ProfilesDialogProvider.getConfirmRemoveProfileDialogFragment(mContext, 172 mRemoveConfirmListener, /* rejectListener= */ null); 173 } 174 mFragmentController.showDialog(dialogFragment, REMOVE_PROFILE_DIALOG_TAG); 175 } 176 177 /** 178 * When the Preference is disabled while still visible, {@code ActionDisabledByAdminDialog} 179 * should be shown when the action is disallowed by a device owner or a profile owner. 180 * Otherwise, a {@code Toast} will be shown to inform the user that the action is disabled. 181 */ runClickableWhileDisabled()182 public void runClickableWhileDisabled() { 183 if (hasUserRestrictionByDpm(mContext, DISALLOW_REMOVE_USER)) { 184 showActionDisabledByAdminDialog(); 185 } else { 186 Toast.makeText(mContext, mContext.getString(R.string.action_unavailable), 187 Toast.LENGTH_LONG).show(); 188 } 189 } 190 showActionDisabledByAdminDialog()191 private void showActionDisabledByAdminDialog() { 192 mFragmentController.showDialog( 193 EnterpriseUtils.getActionDisabledByAdminDialog(mContext, 194 DISALLOW_REMOVE_USER), 195 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); 196 } 197 } 198