1 /* 2 * Copyright (C) 2014 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.settings.users; 18 19 import static android.os.UserHandle.USER_NULL; 20 21 import android.app.ActivityManager; 22 import android.app.Dialog; 23 import android.app.settings.SettingsEnums; 24 import android.content.Context; 25 import android.content.pm.UserInfo; 26 import android.os.Bundle; 27 import android.os.RemoteException; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.util.Log; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.preference.Preference; 34 import androidx.preference.SwitchPreference; 35 36 import com.android.settings.R; 37 import com.android.settings.SettingsPreferenceFragment; 38 import com.android.settings.Utils; 39 import com.android.settings.core.SubSettingLauncher; 40 import com.android.settingslib.RestrictedLockUtils; 41 import com.android.settingslib.RestrictedLockUtilsInternal; 42 import com.android.settingslib.RestrictedPreference; 43 44 import java.util.List; 45 46 /** 47 * Settings screen for configuring, deleting or switching to a specific user. 48 * It is shown when you tap on a user in the user management (UserSettings) screen. 49 * 50 * Arguments to this fragment must include the userId of the user (in EXTRA_USER_ID) for whom 51 * to display controls. 52 */ 53 public class UserDetailsSettings extends SettingsPreferenceFragment 54 implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { 55 56 private static final String TAG = UserDetailsSettings.class.getSimpleName(); 57 58 private static final String KEY_SWITCH_USER = "switch_user"; 59 private static final String KEY_ENABLE_TELEPHONY = "enable_calling"; 60 private static final String KEY_REMOVE_USER = "remove_user"; 61 private static final String KEY_APP_AND_CONTENT_ACCESS = "app_and_content_access"; 62 63 /** Integer extra containing the userId to manage */ 64 static final String EXTRA_USER_ID = "user_id"; 65 66 private static final int DIALOG_CONFIRM_REMOVE = 1; 67 private static final int DIALOG_CONFIRM_ENABLE_CALLING = 2; 68 private static final int DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS = 3; 69 private static final int DIALOG_SETUP_USER = 4; 70 71 private UserManager mUserManager; 72 private UserCapabilities mUserCaps; 73 74 @VisibleForTesting 75 RestrictedPreference mSwitchUserPref; 76 private SwitchPreference mPhonePref; 77 @VisibleForTesting 78 Preference mAppAndContentAccessPref; 79 @VisibleForTesting 80 Preference mRemoveUserPref; 81 82 @VisibleForTesting 83 UserInfo mUserInfo; 84 private Bundle mDefaultGuestRestrictions; 85 86 @Override getMetricsCategory()87 public int getMetricsCategory() { 88 return SettingsEnums.USER_DETAILS; 89 } 90 91 @Override onCreate(Bundle icicle)92 public void onCreate(Bundle icicle) { 93 super.onCreate(icicle); 94 95 final Context context = getActivity(); 96 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 97 mUserCaps = UserCapabilities.create(context); 98 addPreferencesFromResource(R.xml.user_details_settings); 99 100 initialize(context, getArguments()); 101 } 102 103 @Override onResume()104 public void onResume() { 105 super.onResume(); 106 mSwitchUserPref.setEnabled(canSwitchUserNow()); 107 } 108 109 @Override onPreferenceClick(Preference preference)110 public boolean onPreferenceClick(Preference preference) { 111 if (preference == mRemoveUserPref) { 112 if (canDeleteUser()) { 113 showDialog(DIALOG_CONFIRM_REMOVE); 114 return true; 115 } 116 } else if (preference == mSwitchUserPref) { 117 if (canSwitchUserNow()) { 118 if (shouldShowSetupPromptDialog()) { 119 showDialog(DIALOG_SETUP_USER); 120 } else { 121 switchUser(); 122 } 123 return true; 124 } 125 } else if (preference == mAppAndContentAccessPref) { 126 openAppAndContentAccessScreen(false); 127 return true; 128 } 129 return false; 130 } 131 132 @Override onPreferenceChange(Preference preference, Object newValue)133 public boolean onPreferenceChange(Preference preference, Object newValue) { 134 if (Boolean.TRUE.equals(newValue)) { 135 showDialog(mUserInfo.isGuest() ? DIALOG_CONFIRM_ENABLE_CALLING 136 : DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS); 137 return false; 138 } 139 enableCallsAndSms(false); 140 return true; 141 } 142 143 @Override getDialogMetricsCategory(int dialogId)144 public int getDialogMetricsCategory(int dialogId) { 145 switch (dialogId) { 146 case DIALOG_CONFIRM_REMOVE: 147 return SettingsEnums.DIALOG_USER_REMOVE; 148 case DIALOG_CONFIRM_ENABLE_CALLING: 149 return SettingsEnums.DIALOG_USER_ENABLE_CALLING; 150 case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS: 151 return SettingsEnums.DIALOG_USER_ENABLE_CALLING_AND_SMS; 152 case DIALOG_SETUP_USER: 153 return SettingsEnums.DIALOG_USER_SETUP; 154 default: 155 return 0; 156 } 157 } 158 159 @Override onCreateDialog(int dialogId)160 public Dialog onCreateDialog(int dialogId) { 161 Context context = getActivity(); 162 if (context == null) { 163 return null; 164 } 165 switch (dialogId) { 166 case DIALOG_CONFIRM_REMOVE: 167 return UserDialogs.createRemoveDialog(getActivity(), mUserInfo.id, 168 (dialog, which) -> removeUser()); 169 case DIALOG_CONFIRM_ENABLE_CALLING: 170 return UserDialogs.createEnablePhoneCallsDialog(getActivity(), 171 (dialog, which) -> enableCallsAndSms(true)); 172 case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS: 173 return UserDialogs.createEnablePhoneCallsAndSmsDialog(getActivity(), 174 (dialog, which) -> enableCallsAndSms(true)); 175 case DIALOG_SETUP_USER: 176 return UserDialogs.createSetupUserDialog(getActivity(), 177 (dialog, which) -> { 178 if (canSwitchUserNow()) { 179 switchUser(); 180 } 181 }); 182 } 183 throw new IllegalArgumentException("Unsupported dialogId " + dialogId); 184 } 185 186 @VisibleForTesting 187 @Override 188 protected void showDialog(int dialogId) { 189 super.showDialog(dialogId); 190 } 191 192 @VisibleForTesting 193 void initialize(Context context, Bundle arguments) { 194 int userId = arguments != null ? arguments.getInt(EXTRA_USER_ID, USER_NULL) : USER_NULL; 195 if (userId == USER_NULL) { 196 throw new IllegalStateException("Arguments to this fragment must contain the user id"); 197 } 198 boolean isNewUser = 199 arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false); 200 mUserInfo = mUserManager.getUserInfo(userId); 201 202 mSwitchUserPref = findPreference(KEY_SWITCH_USER); 203 mPhonePref = findPreference(KEY_ENABLE_TELEPHONY); 204 mRemoveUserPref = findPreference(KEY_REMOVE_USER); 205 mAppAndContentAccessPref = findPreference(KEY_APP_AND_CONTENT_ACCESS); 206 207 mSwitchUserPref.setTitle( 208 context.getString(com.android.settingslib.R.string.user_switch_to_user, 209 mUserInfo.name)); 210 211 if (mUserCaps.mDisallowSwitchUser) { 212 mSwitchUserPref.setDisabledByAdmin(RestrictedLockUtilsInternal.getDeviceOwner(context)); 213 } else { 214 mSwitchUserPref.setDisabledByAdmin(null); 215 mSwitchUserPref.setSelectable(true); 216 mSwitchUserPref.setOnPreferenceClickListener(this); 217 } 218 219 if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls 220 removePreference(KEY_ENABLE_TELEPHONY); 221 removePreference(KEY_REMOVE_USER); 222 removePreference(KEY_APP_AND_CONTENT_ACCESS); 223 } else { 224 if (!Utils.isVoiceCapable(context)) { // no telephony 225 removePreference(KEY_ENABLE_TELEPHONY); 226 } 227 228 if (mUserInfo.isRestricted()) { 229 removePreference(KEY_ENABLE_TELEPHONY); 230 if (isNewUser) { 231 // for newly created restricted users we should open the apps and content access 232 // screen to initialize the default restrictions 233 openAppAndContentAccessScreen(true); 234 } 235 } else { 236 removePreference(KEY_APP_AND_CONTENT_ACCESS); 237 } 238 239 if (mUserInfo.isGuest()) { 240 // These are not for an existing user, just general Guest settings. 241 // Default title is for calling and SMS. Change to calling-only here 242 mPhonePref.setTitle(R.string.user_enable_calling); 243 mDefaultGuestRestrictions = mUserManager.getDefaultGuestRestrictions(); 244 mPhonePref.setChecked( 245 !mDefaultGuestRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); 246 mRemoveUserPref.setTitle(R.string.user_exit_guest_title); 247 } else { 248 mPhonePref.setChecked(!mUserManager.hasUserRestriction( 249 UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId))); 250 mRemoveUserPref.setTitle(R.string.user_remove_user); 251 } 252 if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context, 253 UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId())) { 254 removePreference(KEY_REMOVE_USER); 255 } 256 257 mRemoveUserPref.setOnPreferenceClickListener(this); 258 mPhonePref.setOnPreferenceChangeListener(this); 259 mAppAndContentAccessPref.setOnPreferenceClickListener(this); 260 } 261 } 262 263 @VisibleForTesting 264 boolean canDeleteUser() { 265 if (!mUserManager.isAdminUser()) { 266 return false; 267 } 268 269 Context context = getActivity(); 270 if (context == null) { 271 return false; 272 } 273 274 final RestrictedLockUtils.EnforcedAdmin removeDisallowedAdmin = 275 RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, 276 UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId()); 277 if (removeDisallowedAdmin != null) { 278 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, 279 removeDisallowedAdmin); 280 return false; 281 } 282 return true; 283 } 284 285 @VisibleForTesting 286 boolean canSwitchUserNow() { 287 return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK; 288 } 289 290 @VisibleForTesting 291 void switchUser() { 292 try { 293 ActivityManager.getService().switchUser(mUserInfo.id); 294 } catch (RemoteException re) { 295 Log.e(TAG, "Error while switching to other user."); 296 } finally { 297 finishFragment(); 298 } 299 } 300 301 private void enableCallsAndSms(boolean enabled) { 302 mPhonePref.setChecked(enabled); 303 if (mUserInfo.isGuest()) { 304 mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, !enabled); 305 // SMS is always disabled for guest 306 mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true); 307 mUserManager.setDefaultGuestRestrictions(mDefaultGuestRestrictions); 308 309 // Update the guest's restrictions, if there is a guest 310 // TODO: Maybe setDefaultGuestRestrictions() can internally just set the restrictions 311 // on any existing guest rather than do it here with multiple Binder calls. 312 List<UserInfo> users = mUserManager.getUsers(true); 313 for (UserInfo user : users) { 314 if (user.isGuest()) { 315 UserHandle userHandle = UserHandle.of(user.id); 316 for (String key : mDefaultGuestRestrictions.keySet()) { 317 mUserManager.setUserRestriction( 318 key, mDefaultGuestRestrictions.getBoolean(key), userHandle); 319 } 320 } 321 } 322 } else { 323 UserHandle userHandle = UserHandle.of(mUserInfo.id); 324 mUserManager.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, !enabled, 325 userHandle); 326 mUserManager.setUserRestriction(UserManager.DISALLOW_SMS, !enabled, userHandle); 327 } 328 } 329 330 private void removeUser() { 331 mUserManager.removeUser(mUserInfo.id); 332 finishFragment(); 333 } 334 335 /** 336 * @param isNewUser indicates if a user was created recently, for new users 337 * AppRestrictionsFragment should set the default restrictions 338 */ 339 private void openAppAndContentAccessScreen(boolean isNewUser) { 340 Bundle extras = new Bundle(); 341 extras.putInt(AppRestrictionsFragment.EXTRA_USER_ID, mUserInfo.id); 342 extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, isNewUser); 343 new SubSettingLauncher(getContext()) 344 .setDestination(AppRestrictionsFragment.class.getName()) 345 .setArguments(extras) 346 .setTitleRes(R.string.user_restrictions_title) 347 .setSourceMetricsCategory(getMetricsCategory()) 348 .launch(); 349 } 350 351 private boolean isSecondaryUser(UserInfo user) { 352 return UserManager.USER_TYPE_FULL_SECONDARY.equals(user.userType); 353 } 354 355 private boolean shouldShowSetupPromptDialog() { 356 // TODO: FLAG_INITIALIZED is set when a user is switched to for the first time, 357 // but what we would really need here is a flag that shows if the setup process was 358 // completed. After the user cancels the setup process, mUserInfo.isInitialized() will 359 // return true so there will be no setup prompt dialog shown to the user anymore. 360 return isSecondaryUser(mUserInfo) && !mUserInfo.isInitialized(); 361 } 362 } 363