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