1 /* 2 * Copyright (C) 2010 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; 18 19 import android.app.Activity; 20 import android.app.Fragment; 21 import android.app.PendingIntent; 22 import android.app.admin.DevicePolicyManager; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.UserInfo; 26 import android.os.Bundle; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.preference.Preference; 31 import android.preference.PreferenceActivity; 32 import android.preference.PreferenceScreen; 33 import android.security.KeyStore; 34 import android.util.EventLog; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.widget.ListView; 39 40 import com.android.internal.widget.LockPatternUtils; 41 import com.android.settings.ConfirmLockPattern.ConfirmLockPatternFragment; 42 43 import java.util.List; 44 45 import libcore.util.MutableBoolean; 46 47 public class ChooseLockGeneric extends PreferenceActivity { 48 49 @Override 50 public Intent getIntent() { 51 Intent modIntent = new Intent(super.getIntent()); 52 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockGenericFragment.class.getName()); 53 modIntent.putExtra(EXTRA_NO_HEADERS, true); 54 return modIntent; 55 } 56 57 @Override 58 protected boolean isValidFragment(String fragmentName) { 59 if (ChooseLockGenericFragment.class.getName().equals(fragmentName)) return true; 60 return false; 61 } 62 63 public static class InternalActivity extends ChooseLockGeneric { 64 } 65 66 public static class ChooseLockGenericFragment extends SettingsPreferenceFragment { 67 private static final int MIN_PASSWORD_LENGTH = 4; 68 private static final String KEY_UNLOCK_BACKUP_INFO = "unlock_backup_info"; 69 private static final String KEY_UNLOCK_SET_OFF = "unlock_set_off"; 70 private static final String KEY_UNLOCK_SET_NONE = "unlock_set_none"; 71 private static final String KEY_UNLOCK_SET_BIOMETRIC_WEAK = "unlock_set_biometric_weak"; 72 private static final String KEY_UNLOCK_SET_PIN = "unlock_set_pin"; 73 private static final String KEY_UNLOCK_SET_PASSWORD = "unlock_set_password"; 74 private static final String KEY_UNLOCK_SET_PATTERN = "unlock_set_pattern"; 75 private static final int CONFIRM_EXISTING_REQUEST = 100; 76 private static final int FALLBACK_REQUEST = 101; 77 private static final String PASSWORD_CONFIRMED = "password_confirmed"; 78 private static final String CONFIRM_CREDENTIALS = "confirm_credentials"; 79 private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation"; 80 private static final String FINISH_PENDING = "finish_pending"; 81 public static final String MINIMUM_QUALITY_KEY = "minimum_quality"; 82 83 private static final boolean ALWAY_SHOW_TUTORIAL = true; 84 85 private ChooseLockSettingsHelper mChooseLockSettingsHelper; 86 private DevicePolicyManager mDPM; 87 private KeyStore mKeyStore; 88 private boolean mPasswordConfirmed = false; 89 private boolean mWaitingForConfirmation = false; 90 private boolean mFinishPending = false; 91 92 @Override 93 public void onCreate(Bundle savedInstanceState) { 94 super.onCreate(savedInstanceState); 95 96 mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 97 mKeyStore = KeyStore.getInstance(); 98 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity()); 99 100 // Defaults to needing to confirm credentials 101 final boolean confirmCredentials = getActivity().getIntent() 102 .getBooleanExtra(CONFIRM_CREDENTIALS, true); 103 if (getActivity() instanceof ChooseLockGeneric.InternalActivity) { 104 mPasswordConfirmed = !confirmCredentials; 105 } 106 107 if (savedInstanceState != null) { 108 mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); 109 mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION); 110 mFinishPending = savedInstanceState.getBoolean(FINISH_PENDING); 111 } 112 113 if (mPasswordConfirmed) { 114 updatePreferencesOrFinish(); 115 } else if (!mWaitingForConfirmation) { 116 ChooseLockSettingsHelper helper = 117 new ChooseLockSettingsHelper(this.getActivity(), this); 118 if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) { 119 mPasswordConfirmed = true; // no password set, so no need to confirm 120 updatePreferencesOrFinish(); 121 } else { 122 mWaitingForConfirmation = true; 123 } 124 } 125 } 126 127 @Override 128 public void onResume() { 129 super.onResume(); 130 if (mFinishPending) { 131 mFinishPending = false; 132 finish(); 133 } 134 } 135 136 @Override 137 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, 138 Preference preference) { 139 final String key = preference.getKey(); 140 boolean handled = true; 141 142 EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, key); 143 144 if (KEY_UNLOCK_SET_OFF.equals(key)) { 145 updateUnlockMethodAndFinish( 146 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true); 147 } else if (KEY_UNLOCK_SET_NONE.equals(key)) { 148 updateUnlockMethodAndFinish( 149 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false); 150 } else if (KEY_UNLOCK_SET_BIOMETRIC_WEAK.equals(key)) { 151 updateUnlockMethodAndFinish( 152 DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, false); 153 }else if (KEY_UNLOCK_SET_PATTERN.equals(key)) { 154 updateUnlockMethodAndFinish( 155 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false); 156 } else if (KEY_UNLOCK_SET_PIN.equals(key)) { 157 updateUnlockMethodAndFinish( 158 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false); 159 } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) { 160 updateUnlockMethodAndFinish( 161 DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false); 162 } else { 163 handled = false; 164 } 165 return handled; 166 } 167 168 @Override 169 public View onCreateView(LayoutInflater inflater, ViewGroup container, 170 Bundle savedInstanceState) { 171 View v = super.onCreateView(inflater, container, savedInstanceState); 172 final boolean onlyShowFallback = getActivity().getIntent() 173 .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false); 174 if (onlyShowFallback) { 175 View header = v.inflate(getActivity(), 176 R.layout.weak_biometric_fallback_header, null); 177 ((ListView) v.findViewById(android.R.id.list)).addHeaderView(header, null, false); 178 } 179 180 return v; 181 } 182 183 @Override 184 public void onActivityResult(int requestCode, int resultCode, Intent data) { 185 super.onActivityResult(requestCode, resultCode, data); 186 mWaitingForConfirmation = false; 187 if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) { 188 mPasswordConfirmed = true; 189 updatePreferencesOrFinish(); 190 } else if(requestCode == FALLBACK_REQUEST) { 191 mChooseLockSettingsHelper.utils().deleteTempGallery(); 192 getActivity().setResult(resultCode); 193 finish(); 194 } else { 195 getActivity().setResult(Activity.RESULT_CANCELED); 196 finish(); 197 } 198 } 199 200 @Override 201 public void onSaveInstanceState(Bundle outState) { 202 super.onSaveInstanceState(outState); 203 // Saved so we don't force user to re-enter their password if configuration changes 204 outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed); 205 outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation); 206 outState.putBoolean(FINISH_PENDING, mFinishPending); 207 } 208 209 private void updatePreferencesOrFinish() { 210 Intent intent = getActivity().getIntent(); 211 int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); 212 if (quality == -1) { 213 // If caller didn't specify password quality, show UI and allow the user to choose. 214 quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1); 215 MutableBoolean allowBiometric = new MutableBoolean(false); 216 quality = upgradeQuality(quality, allowBiometric); 217 final PreferenceScreen prefScreen = getPreferenceScreen(); 218 if (prefScreen != null) { 219 prefScreen.removeAll(); 220 } 221 addPreferencesFromResource(R.xml.security_settings_picker); 222 disableUnusablePreferences(quality, allowBiometric); 223 } else { 224 updateUnlockMethodAndFinish(quality, false); 225 } 226 } 227 228 /** increases the quality if necessary, and returns whether biometric is allowed */ 229 private int upgradeQuality(int quality, MutableBoolean allowBiometric) { 230 quality = upgradeQualityForDPM(quality); 231 quality = upgradeQualityForKeyStore(quality); 232 int encryptionQuality = upgradeQualityForEncryption(quality); 233 if (encryptionQuality > quality) { 234 //The first case checks whether biometric is allowed, prior to the user making 235 //their selection from the list 236 if (allowBiometric != null) { 237 allowBiometric.value = quality <= 238 DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; 239 } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { 240 //When the user has selected biometric we shouldn't change that due to 241 //encryption 242 return quality; 243 } 244 } 245 return encryptionQuality; 246 } 247 248 private int upgradeQualityForDPM(int quality) { 249 // Compare min allowed password quality 250 int minQuality = mDPM.getPasswordQuality(null); 251 if (quality < minQuality) { 252 quality = minQuality; 253 } 254 return quality; 255 } 256 257 /** 258 * Mix in "encryption minimums" to any given quality value. This prevents users 259 * from downgrading the pattern/pin/password to a level below the minimums. 260 * 261 * ASSUMPTION: Setting quality is sufficient (e.g. minimum lengths will be set 262 * appropriately.) 263 */ 264 private int upgradeQualityForEncryption(int quality) { 265 // Don't upgrade quality for secondary users. Encryption requirements don't apply. 266 if (!Process.myUserHandle().equals(UserHandle.OWNER)) return quality; 267 int encryptionStatus = mDPM.getStorageEncryptionStatus(); 268 boolean encrypted = (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) 269 || (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING); 270 if (encrypted) { 271 if (quality < CryptKeeperSettings.MIN_PASSWORD_QUALITY) { 272 quality = CryptKeeperSettings.MIN_PASSWORD_QUALITY; 273 } 274 } 275 return quality; 276 } 277 278 private int upgradeQualityForKeyStore(int quality) { 279 if (!mKeyStore.isEmpty()) { 280 if (quality < CredentialStorage.MIN_PASSWORD_QUALITY) { 281 quality = CredentialStorage.MIN_PASSWORD_QUALITY; 282 } 283 } 284 return quality; 285 } 286 287 /*** 288 * Disables preferences that are less secure than required quality. 289 * 290 * @param quality the requested quality. 291 */ 292 private void disableUnusablePreferences(final int quality, MutableBoolean allowBiometric) { 293 final PreferenceScreen entries = getPreferenceScreen(); 294 final boolean onlyShowFallback = getActivity().getIntent() 295 .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false); 296 final boolean weakBiometricAvailable = 297 mChooseLockSettingsHelper.utils().isBiometricWeakInstalled(); 298 299 // if there are multiple users, disable "None" setting 300 UserManager mUm = (UserManager) getSystemService(Context.USER_SERVICE); 301 List<UserInfo> users = mUm.getUsers(true); 302 final boolean singleUser = users.size() == 1; 303 304 for (int i = entries.getPreferenceCount() - 1; i >= 0; --i) { 305 Preference pref = entries.getPreference(i); 306 if (pref instanceof PreferenceScreen) { 307 final String key = ((PreferenceScreen) pref).getKey(); 308 boolean enabled = true; 309 boolean visible = true; 310 if (KEY_UNLOCK_SET_OFF.equals(key)) { 311 enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 312 visible = singleUser; // don't show when there's more than 1 user 313 } else if (KEY_UNLOCK_SET_NONE.equals(key)) { 314 enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 315 } else if (KEY_UNLOCK_SET_BIOMETRIC_WEAK.equals(key)) { 316 enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK || 317 allowBiometric.value; 318 visible = weakBiometricAvailable; // If not available, then don't show it. 319 } else if (KEY_UNLOCK_SET_PATTERN.equals(key)) { 320 enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 321 } else if (KEY_UNLOCK_SET_PIN.equals(key)) { 322 enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 323 } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) { 324 enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 325 } 326 if (!visible || (onlyShowFallback && !allowedForFallback(key))) { 327 entries.removePreference(pref); 328 } else if (!enabled) { 329 pref.setSummary(R.string.unlock_set_unlock_disabled_summary); 330 pref.setEnabled(false); 331 } 332 } 333 } 334 } 335 336 /** 337 * Check whether the key is allowed for fallback (e.g. bio sensor). Returns true if it's 338 * supported as a backup. 339 * 340 * @param key 341 * @return true if allowed 342 */ 343 private boolean allowedForFallback(String key) { 344 return KEY_UNLOCK_BACKUP_INFO.equals(key) || 345 KEY_UNLOCK_SET_PATTERN.equals(key) || KEY_UNLOCK_SET_PIN.equals(key); 346 } 347 348 private Intent getBiometricSensorIntent() { 349 Intent fallBackIntent = new Intent().setClass(getActivity(), 350 ChooseLockGeneric.InternalActivity.class); 351 fallBackIntent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, true); 352 fallBackIntent.putExtra(CONFIRM_CREDENTIALS, false); 353 fallBackIntent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, 354 R.string.backup_lock_settings_picker_title); 355 356 boolean showTutorial = ALWAY_SHOW_TUTORIAL || 357 !mChooseLockSettingsHelper.utils().isBiometricWeakEverChosen(); 358 Intent intent = new Intent(); 359 intent.setClassName("com.android.facelock", "com.android.facelock.SetupIntro"); 360 intent.putExtra("showTutorial", showTutorial); 361 PendingIntent pending = PendingIntent.getActivity(getActivity(), 0, fallBackIntent, 0); 362 intent.putExtra("PendingIntent", pending); 363 return intent; 364 } 365 366 /** 367 * Invokes an activity to change the user's pattern, password or PIN based on given quality 368 * and minimum quality specified by DevicePolicyManager. If quality is 369 * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared. 370 * 371 * @param quality the desired quality. Ignored if DevicePolicyManager requires more security 372 * @param disabled whether or not to show LockScreen at all. Only meaningful when quality is 373 * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED} 374 */ 375 void updateUnlockMethodAndFinish(int quality, boolean disabled) { 376 // Sanity check. We should never get here without confirming user's existing password. 377 if (!mPasswordConfirmed) { 378 throw new IllegalStateException("Tried to update password without confirming it"); 379 } 380 381 final boolean isFallback = getActivity().getIntent() 382 .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false); 383 384 quality = upgradeQuality(quality, null); 385 386 if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { 387 int minLength = mDPM.getPasswordMinimumLength(null); 388 if (minLength < MIN_PASSWORD_LENGTH) { 389 minLength = MIN_PASSWORD_LENGTH; 390 } 391 final int maxLength = mDPM.getPasswordMaximumLength(quality); 392 Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class); 393 intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality); 394 intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength); 395 intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength); 396 intent.putExtra(CONFIRM_CREDENTIALS, false); 397 intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, 398 isFallback); 399 if (isFallback) { 400 startActivityForResult(intent, FALLBACK_REQUEST); 401 return; 402 } else { 403 mFinishPending = true; 404 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 405 startActivity(intent); 406 } 407 } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { 408 Intent intent = new Intent(getActivity(), ChooseLockPattern.class); 409 intent.putExtra("key_lock_method", "pattern"); 410 intent.putExtra(CONFIRM_CREDENTIALS, false); 411 intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, 412 isFallback); 413 if (isFallback) { 414 startActivityForResult(intent, FALLBACK_REQUEST); 415 return; 416 } else { 417 mFinishPending = true; 418 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 419 startActivity(intent); 420 } 421 } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { 422 Intent intent = getBiometricSensorIntent(); 423 mFinishPending = true; 424 startActivity(intent); 425 } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { 426 mChooseLockSettingsHelper.utils().clearLock(false); 427 mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled); 428 getActivity().setResult(Activity.RESULT_OK); 429 finish(); 430 } else { 431 finish(); 432 } 433 } 434 435 @Override 436 protected int getHelpResource() { 437 return R.string.help_url_choose_lockscreen; 438 } 439 440 } 441 } 442