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