LockPatternUtils.java revision 20c41d5382b70fa5a24e9ef68f19ce0aedea5234
1/* 2 * Copyright (C) 2007 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.internal.widget; 18 19import com.android.internal.R; 20import com.android.internal.telephony.ITelephony; 21import com.google.android.collect.Lists; 22 23import android.app.ActivityManagerNative; 24import android.app.admin.DevicePolicyManager; 25import android.content.ContentResolver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.pm.PackageManager; 29import android.os.Binder; 30import android.os.IBinder; 31import android.os.Process; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.os.SystemClock; 35import android.os.UserHandle; 36import android.os.storage.IMountService; 37import android.provider.Settings; 38import android.security.KeyStore; 39import android.telephony.TelephonyManager; 40import android.text.TextUtils; 41import android.util.Log; 42import android.view.View; 43import android.widget.Button; 44 45import java.security.MessageDigest; 46import java.security.NoSuchAlgorithmException; 47import java.security.SecureRandom; 48import java.util.List; 49 50/** 51 * Utilities for the lock pattern and its settings. 52 */ 53public class LockPatternUtils { 54 55 private static final String OPTION_ENABLE_FACELOCK = "enable_facelock"; 56 57 private static final String TAG = "LockPatternUtils"; 58 59 /** 60 * The maximum number of incorrect attempts before the user is prevented 61 * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}. 62 */ 63 public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5; 64 65 /** 66 * The number of incorrect attempts before which we fall back on an alternative 67 * method of verifying the user, and resetting their lock pattern. 68 */ 69 public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20; 70 71 /** 72 * How long the user is prevented from trying again after entering the 73 * wrong pattern too many times. 74 */ 75 public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L; 76 77 /** 78 * The interval of the countdown for showing progress of the lockout. 79 */ 80 public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L; 81 82 83 /** 84 * This dictates when we start telling the user that continued failed attempts will wipe 85 * their device. 86 */ 87 public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5; 88 89 /** 90 * The minimum number of dots in a valid pattern. 91 */ 92 public static final int MIN_LOCK_PATTERN_SIZE = 4; 93 94 /** 95 * The minimum number of dots the user must include in a wrong pattern 96 * attempt for it to be counted against the counts that affect 97 * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET} 98 */ 99 public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE; 100 101 /** 102 * The bit in LOCK_BIOMETRIC_WEAK_FLAGS to be used to indicate whether liveliness should 103 * be used 104 */ 105 public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1; 106 107 protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; 108 protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; 109 protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; 110 public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; 111 public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; 112 protected final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; 113 protected final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; 114 protected final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; 115 public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK 116 = "lockscreen.biometric_weak_fallback"; 117 public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY 118 = "lockscreen.biometricweakeverchosen"; 119 public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS 120 = "lockscreen.power_button_instantly_locks"; 121 122 protected final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; 123 124 private final Context mContext; 125 private final ContentResolver mContentResolver; 126 private DevicePolicyManager mDevicePolicyManager; 127 private ILockSettings mLockSettingsService; 128 private int mCurrentUserId = UserHandle.USER_NULL; 129 130 public DevicePolicyManager getDevicePolicyManager() { 131 if (mDevicePolicyManager == null) { 132 mDevicePolicyManager = 133 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 134 if (mDevicePolicyManager == null) { 135 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?", 136 new IllegalStateException("Stack trace:")); 137 } 138 } 139 return mDevicePolicyManager; 140 } 141 142 /** 143 * @param contentResolver Used to look up and save settings. 144 */ 145 public LockPatternUtils(Context context) { 146 mContext = context; 147 mContentResolver = context.getContentResolver(); 148 } 149 150 private ILockSettings getLockSettings() { 151 if (mLockSettingsService == null) { 152 mLockSettingsService = ILockSettings.Stub.asInterface( 153 (IBinder) ServiceManager.getService("lock_settings")); 154 } 155 return mLockSettingsService; 156 } 157 158 public int getRequestedMinimumPasswordLength() { 159 return getDevicePolicyManager().getPasswordMinimumLength(null, getCurrentOrCallingUserId()); 160 } 161 162 /** 163 * Gets the device policy password mode. If the mode is non-specific, returns 164 * MODE_PATTERN which allows the user to choose anything. 165 */ 166 public int getRequestedPasswordQuality() { 167 return getDevicePolicyManager().getPasswordQuality(null, getCurrentOrCallingUserId()); 168 } 169 170 public int getRequestedPasswordHistoryLength() { 171 return getDevicePolicyManager().getPasswordHistoryLength(null, getCurrentOrCallingUserId()); 172 } 173 174 public int getRequestedPasswordMinimumLetters() { 175 return getDevicePolicyManager().getPasswordMinimumLetters(null, 176 getCurrentOrCallingUserId()); 177 } 178 179 public int getRequestedPasswordMinimumUpperCase() { 180 return getDevicePolicyManager().getPasswordMinimumUpperCase(null, 181 getCurrentOrCallingUserId()); 182 } 183 184 public int getRequestedPasswordMinimumLowerCase() { 185 return getDevicePolicyManager().getPasswordMinimumLowerCase(null, 186 getCurrentOrCallingUserId()); 187 } 188 189 public int getRequestedPasswordMinimumNumeric() { 190 return getDevicePolicyManager().getPasswordMinimumNumeric(null, 191 getCurrentOrCallingUserId()); 192 } 193 194 public int getRequestedPasswordMinimumSymbols() { 195 return getDevicePolicyManager().getPasswordMinimumSymbols(null, 196 getCurrentOrCallingUserId()); 197 } 198 199 public int getRequestedPasswordMinimumNonLetter() { 200 return getDevicePolicyManager().getPasswordMinimumNonLetter(null, 201 getCurrentOrCallingUserId()); 202 } 203 204 /** 205 * Returns the actual password mode, as set by keyguard after updating the password. 206 * 207 * @return 208 */ 209 public void reportFailedPasswordAttempt() { 210 getDevicePolicyManager().reportFailedPasswordAttempt(getCurrentOrCallingUserId()); 211 } 212 213 public void reportSuccessfulPasswordAttempt() { 214 getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId()); 215 } 216 217 public void setCurrentUser(int userId) { 218 if (Process.myUid() == Process.SYSTEM_UID) { 219 mCurrentUserId = userId; 220 } else { 221 throw new SecurityException("Only the system process can set the current user"); 222 } 223 } 224 225 public int getCurrentUser() { 226 if (Process.myUid() == Process.SYSTEM_UID) { 227 if (mCurrentUserId != UserHandle.USER_NULL) { 228 // Someone is regularly updating using setCurrentUser() use that value. 229 return mCurrentUserId; 230 } 231 try { 232 return ActivityManagerNative.getDefault().getCurrentUser().id; 233 } catch (RemoteException re) { 234 return UserHandle.USER_OWNER; 235 } 236 } else { 237 throw new SecurityException("Only the system process can get the current user"); 238 } 239 } 240 241 public void removeUser(int userId) { 242 if (Process.myUid() == Process.SYSTEM_UID) { 243 try { 244 getLockSettings().removeUser(userId); 245 } catch (RemoteException re) { 246 Log.e(TAG, "Couldn't remove lock settings for user " + userId); 247 } 248 } 249 } 250 251 private int getCurrentOrCallingUserId() { 252 int callingUid = Binder.getCallingUid(); 253 if (callingUid == android.os.Process.SYSTEM_UID) { 254 // TODO: This is a little inefficient. See if all users of this are able to 255 // handle USER_CURRENT and pass that instead. 256 return getCurrentUser(); 257 } else { 258 return UserHandle.getUserId(callingUid); 259 } 260 } 261 262 /** 263 * Check to see if a pattern matches the saved pattern. If no pattern exists, 264 * always returns true. 265 * @param pattern The pattern to check. 266 * @return Whether the pattern matches the stored one. 267 */ 268 public boolean checkPattern(List<LockPatternView.Cell> pattern) { 269 int userId = getCurrentOrCallingUserId(); 270 try { 271 return getLockSettings().checkPattern(patternToHash(pattern), userId); 272 } catch (RemoteException re) { 273 return true; 274 } 275 } 276 277 /** 278 * Check to see if a password matches the saved password. If no password exists, 279 * always returns true. 280 * @param password The password to check. 281 * @return Whether the password matches the stored one. 282 */ 283 public boolean checkPassword(String password) { 284 int userId = getCurrentOrCallingUserId(); 285 try { 286 return getLockSettings().checkPassword(passwordToHash(password), userId); 287 } catch (RemoteException re) { 288 return true; 289 } 290 } 291 292 /** 293 * Check to see if a password matches any of the passwords stored in the 294 * password history. 295 * 296 * @param password The password to check. 297 * @return Whether the password matches any in the history. 298 */ 299 public boolean checkPasswordHistory(String password) { 300 String passwordHashString = new String(passwordToHash(password)); 301 String passwordHistory = getString(PASSWORD_HISTORY_KEY); 302 if (passwordHistory == null) { 303 return false; 304 } 305 // Password History may be too long... 306 int passwordHashLength = passwordHashString.length(); 307 int passwordHistoryLength = getRequestedPasswordHistoryLength(); 308 if(passwordHistoryLength == 0) { 309 return false; 310 } 311 int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength 312 + passwordHistoryLength - 1; 313 if (passwordHistory.length() > neededPasswordHistoryLength) { 314 passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength); 315 } 316 return passwordHistory.contains(passwordHashString); 317 } 318 319 /** 320 * Check to see if the user has stored a lock pattern. 321 * @return Whether a saved pattern exists. 322 */ 323 public boolean savedPatternExists() { 324 try { 325 return getLockSettings().havePattern(getCurrentOrCallingUserId()); 326 } catch (RemoteException re) { 327 return false; 328 } 329 } 330 331 /** 332 * Check to see if the user has stored a lock pattern. 333 * @return Whether a saved pattern exists. 334 */ 335 public boolean savedPasswordExists() { 336 try { 337 return getLockSettings().havePassword(getCurrentOrCallingUserId()); 338 } catch (RemoteException re) { 339 return false; 340 } 341 } 342 343 /** 344 * Return true if the user has ever chosen a pattern. This is true even if the pattern is 345 * currently cleared. 346 * 347 * @return True if the user has ever chosen a pattern. 348 */ 349 public boolean isPatternEverChosen() { 350 return getBoolean(PATTERN_EVER_CHOSEN_KEY, false); 351 } 352 353 /** 354 * Return true if the user has ever chosen biometric weak. This is true even if biometric 355 * weak is not current set. 356 * 357 * @return True if the user has ever chosen biometric weak. 358 */ 359 public boolean isBiometricWeakEverChosen() { 360 return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, false); 361 } 362 363 /** 364 * Used by device policy manager to validate the current password 365 * information it has. 366 */ 367 public int getActivePasswordQuality() { 368 int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 369 // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to 370 // return biometric_weak if that is being used instead of the backup 371 int quality = 372 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 373 switch (quality) { 374 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 375 if (isLockPatternEnabled()) { 376 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 377 } 378 break; 379 case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: 380 if (isBiometricWeakInstalled()) { 381 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; 382 } 383 break; 384 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 385 if (isLockPasswordEnabled()) { 386 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 387 } 388 break; 389 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 390 if (isLockPasswordEnabled()) { 391 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 392 } 393 break; 394 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 395 if (isLockPasswordEnabled()) { 396 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 397 } 398 break; 399 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 400 if (isLockPasswordEnabled()) { 401 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 402 } 403 break; 404 } 405 406 return activePasswordQuality; 407 } 408 409 /** 410 * Clear any lock pattern or password. 411 */ 412 public void clearLock(boolean isFallback) { 413 if(!isFallback) deleteGallery(); 414 saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 415 setLockPatternEnabled(false); 416 saveLockPattern(null); 417 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); 418 setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); 419 } 420 421 /** 422 * Disable showing lock screen at all when the DevicePolicyManager allows it. 423 * This is only meaningful if pattern, pin or password are not set. 424 * 425 * @param disable Disables lock screen when true 426 */ 427 public void setLockScreenDisabled(boolean disable) { 428 setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0); 429 } 430 431 /** 432 * Determine if LockScreen can be disabled. This is used, for example, to tell if we should 433 * show LockScreen or go straight to the home screen. 434 * 435 * @return true if lock screen is can be disabled 436 */ 437 public boolean isLockScreenDisabled() { 438 return !isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0; 439 } 440 441 /** 442 * Calls back SetupFaceLock to delete the temporary gallery file 443 */ 444 public void deleteTempGallery() { 445 Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY"); 446 intent.putExtra("deleteTempGallery", true); 447 mContext.sendBroadcast(intent); 448 } 449 450 /** 451 * Calls back SetupFaceLock to delete the gallery file when the lock type is changed 452 */ 453 void deleteGallery() { 454 if(usingBiometricWeak()) { 455 Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY"); 456 intent.putExtra("deleteGallery", true); 457 mContext.sendBroadcast(intent); 458 } 459 } 460 461 /** 462 * Save a lock pattern. 463 * @param pattern The new pattern to save. 464 */ 465 public void saveLockPattern(List<LockPatternView.Cell> pattern) { 466 this.saveLockPattern(pattern, false); 467 } 468 469 /** 470 * Save a lock pattern. 471 * @param pattern The new pattern to save. 472 * @param isFallback Specifies if this is a fallback to biometric weak 473 */ 474 public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) { 475 // Compute the hash 476 final byte[] hash = LockPatternUtils.patternToHash(pattern); 477 try { 478 getLockSettings().setLockPattern(hash, getCurrentOrCallingUserId()); 479 DevicePolicyManager dpm = getDevicePolicyManager(); 480 KeyStore keyStore = KeyStore.getInstance(); 481 if (pattern != null) { 482 keyStore.password(patternToString(pattern)); 483 setBoolean(PATTERN_EVER_CHOSEN_KEY, true); 484 if (!isFallback) { 485 deleteGallery(); 486 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 487 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 488 pattern.size(), 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); 489 } else { 490 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); 491 setLong(PASSWORD_TYPE_ALTERNATE_KEY, 492 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 493 finishBiometricWeak(); 494 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, 495 0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); 496 } 497 } else { 498 if (keyStore.isEmpty()) { 499 keyStore.reset(); 500 } 501 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 502 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); 503 } 504 } catch (RemoteException re) { 505 Log.e(TAG, "Couldn't save lock pattern " + re); 506 } 507 } 508 509 /** 510 * Compute the password quality from the given password string. 511 */ 512 static public int computePasswordQuality(String password) { 513 boolean hasDigit = false; 514 boolean hasNonDigit = false; 515 final int len = password.length(); 516 for (int i = 0; i < len; i++) { 517 if (Character.isDigit(password.charAt(i))) { 518 hasDigit = true; 519 } else { 520 hasNonDigit = true; 521 } 522 } 523 524 if (hasNonDigit && hasDigit) { 525 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 526 } 527 if (hasNonDigit) { 528 return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 529 } 530 if (hasDigit) { 531 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 532 } 533 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 534 } 535 536 /** Update the encryption password if it is enabled **/ 537 private void updateEncryptionPassword(String password) { 538 DevicePolicyManager dpm = getDevicePolicyManager(); 539 if (dpm.getStorageEncryptionStatus(getCurrentOrCallingUserId()) 540 != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) { 541 return; 542 } 543 544 IBinder service = ServiceManager.getService("mount"); 545 if (service == null) { 546 Log.e(TAG, "Could not find the mount service to update the encryption password"); 547 return; 548 } 549 550 IMountService mountService = IMountService.Stub.asInterface(service); 551 try { 552 mountService.changeEncryptionPassword(password); 553 } catch (RemoteException e) { 554 Log.e(TAG, "Error changing encryption password", e); 555 } 556 } 557 558 /** 559 * Save a lock password. Does not ensure that the password is as good 560 * as the requested mode, but will adjust the mode to be as good as the 561 * pattern. 562 * @param password The password to save 563 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} 564 */ 565 public void saveLockPassword(String password, int quality) { 566 this.saveLockPassword(password, quality, false, getCurrentOrCallingUserId()); 567 } 568 569 /** 570 * Save a lock password. Does not ensure that the password is as good 571 * as the requested mode, but will adjust the mode to be as good as the 572 * pattern. 573 * @param password The password to save 574 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} 575 * @param isFallback Specifies if this is a fallback to biometric weak 576 */ 577 public void saveLockPassword(String password, int quality, boolean isFallback) { 578 saveLockPassword(password, quality, isFallback, getCurrentOrCallingUserId()); 579 } 580 581 /** 582 * Save a lock password. Does not ensure that the password is as good 583 * as the requested mode, but will adjust the mode to be as good as the 584 * pattern. 585 * @param password The password to save 586 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} 587 * @param isFallback Specifies if this is a fallback to biometric weak 588 * @param userHandle The userId of the user to change the password for 589 */ 590 public void saveLockPassword(String password, int quality, boolean isFallback, int userHandle) { 591 // Compute the hash 592 final byte[] hash = passwordToHash(password); 593 try { 594 if (Process.myUid() != Process.SYSTEM_UID && userHandle != UserHandle.myUserId()) { 595 throw new SecurityException( 596 "Only the system process can save lock password for another user"); 597 } 598 getLockSettings().setLockPassword(hash, userHandle); 599 DevicePolicyManager dpm = getDevicePolicyManager(); 600 KeyStore keyStore = KeyStore.getInstance(); 601 if (password != null) { 602 if (userHandle == UserHandle.USER_OWNER) { 603 // Update the encryption password. 604 updateEncryptionPassword(password); 605 606 // Update the keystore password 607 keyStore.password(password); 608 } 609 610 int computedQuality = computePasswordQuality(password); 611 if (!isFallback) { 612 deleteGallery(); 613 setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle); 614 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { 615 int letters = 0; 616 int uppercase = 0; 617 int lowercase = 0; 618 int numbers = 0; 619 int symbols = 0; 620 int nonletter = 0; 621 for (int i = 0; i < password.length(); i++) { 622 char c = password.charAt(i); 623 if (c >= 'A' && c <= 'Z') { 624 letters++; 625 uppercase++; 626 } else if (c >= 'a' && c <= 'z') { 627 letters++; 628 lowercase++; 629 } else if (c >= '0' && c <= '9') { 630 numbers++; 631 nonletter++; 632 } else { 633 symbols++; 634 nonletter++; 635 } 636 } 637 dpm.setActivePasswordState(Math.max(quality, computedQuality), 638 password.length(), letters, uppercase, lowercase, 639 numbers, symbols, nonletter, userHandle); 640 } else { 641 // The password is not anything. 642 dpm.setActivePasswordState( 643 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 644 0, 0, 0, 0, 0, 0, 0, userHandle); 645 } 646 } else { 647 // Case where it's a fallback for biometric weak 648 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, 649 userHandle); 650 setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality), 651 userHandle); 652 finishBiometricWeak(); 653 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, 654 0, 0, 0, 0, 0, 0, 0, userHandle); 655 } 656 // Add the password to the password history. We assume all 657 // password 658 // hashes have the same length for simplicity of implementation. 659 String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle); 660 if (passwordHistory == null) { 661 passwordHistory = new String(); 662 } 663 int passwordHistoryLength = getRequestedPasswordHistoryLength(); 664 if (passwordHistoryLength == 0) { 665 passwordHistory = ""; 666 } else { 667 passwordHistory = new String(hash) + "," + passwordHistory; 668 // Cut it to contain passwordHistoryLength hashes 669 // and passwordHistoryLength -1 commas. 670 passwordHistory = passwordHistory.substring(0, Math.min(hash.length 671 * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory 672 .length())); 673 } 674 setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle); 675 } else { 676 // Conditionally reset the keystore if empty. If 677 // non-empty, we are just switching key guard type 678 if (keyStore.isEmpty()) { 679 keyStore.reset(); 680 } 681 dpm.setActivePasswordState( 682 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, 683 userHandle); 684 } 685 } catch (RemoteException re) { 686 // Cant do much 687 Log.e(TAG, "Unable to save lock password " + re); 688 } 689 } 690 691 /** 692 * Retrieves the quality mode we're in. 693 * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} 694 * 695 * @return stored password quality 696 */ 697 public int getKeyguardStoredPasswordQuality() { 698 int quality = 699 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 700 // If the user has chosen to use weak biometric sensor, then return the backup locking 701 // method and treat biometric as a special case. 702 if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { 703 quality = 704 (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY, 705 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 706 } 707 return quality; 708 } 709 710 /** 711 * @return true if the lockscreen method is set to biometric weak 712 */ 713 public boolean usingBiometricWeak() { 714 int quality = 715 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 716 return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; 717 } 718 719 /** 720 * Deserialize a pattern. 721 * @param string The pattern serialized with {@link #patternToString} 722 * @return The pattern. 723 */ 724 public static List<LockPatternView.Cell> stringToPattern(String string) { 725 List<LockPatternView.Cell> result = Lists.newArrayList(); 726 727 final byte[] bytes = string.getBytes(); 728 for (int i = 0; i < bytes.length; i++) { 729 byte b = bytes[i]; 730 result.add(LockPatternView.Cell.of(b / 3, b % 3)); 731 } 732 return result; 733 } 734 735 /** 736 * Serialize a pattern. 737 * @param pattern The pattern. 738 * @return The pattern in string form. 739 */ 740 public static String patternToString(List<LockPatternView.Cell> pattern) { 741 if (pattern == null) { 742 return ""; 743 } 744 final int patternSize = pattern.size(); 745 746 byte[] res = new byte[patternSize]; 747 for (int i = 0; i < patternSize; i++) { 748 LockPatternView.Cell cell = pattern.get(i); 749 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); 750 } 751 return new String(res); 752 } 753 754 /* 755 * Generate an SHA-1 hash for the pattern. Not the most secure, but it is 756 * at least a second level of protection. First level is that the file 757 * is in a location only readable by the system process. 758 * @param pattern the gesture pattern. 759 * @return the hash of the pattern in a byte array. 760 */ 761 private static byte[] patternToHash(List<LockPatternView.Cell> pattern) { 762 if (pattern == null) { 763 return null; 764 } 765 766 final int patternSize = pattern.size(); 767 byte[] res = new byte[patternSize]; 768 for (int i = 0; i < patternSize; i++) { 769 LockPatternView.Cell cell = pattern.get(i); 770 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); 771 } 772 try { 773 MessageDigest md = MessageDigest.getInstance("SHA-1"); 774 byte[] hash = md.digest(res); 775 return hash; 776 } catch (NoSuchAlgorithmException nsa) { 777 return res; 778 } 779 } 780 781 private String getSalt() { 782 long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0); 783 if (salt == 0) { 784 try { 785 salt = SecureRandom.getInstance("SHA1PRNG").nextLong(); 786 setLong(LOCK_PASSWORD_SALT_KEY, salt); 787 Log.v(TAG, "Initialized lock password salt"); 788 } catch (NoSuchAlgorithmException e) { 789 // Throw an exception rather than storing a password we'll never be able to recover 790 throw new IllegalStateException("Couldn't get SecureRandom number", e); 791 } 792 } 793 return Long.toHexString(salt); 794 } 795 796 /* 797 * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash. 798 * Not the most secure, but it is at least a second level of protection. First level is that 799 * the file is in a location only readable by the system process. 800 * @param password the gesture pattern. 801 * @return the hash of the pattern in a byte array. 802 */ 803 public byte[] passwordToHash(String password) { 804 if (password == null) { 805 return null; 806 } 807 String algo = null; 808 byte[] hashed = null; 809 try { 810 byte[] saltedPassword = (password + getSalt()).getBytes(); 811 byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword); 812 byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword); 813 hashed = (toHex(sha1) + toHex(md5)).getBytes(); 814 } catch (NoSuchAlgorithmException e) { 815 Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo); 816 } 817 return hashed; 818 } 819 820 private static String toHex(byte[] ary) { 821 final String hex = "0123456789ABCDEF"; 822 String ret = ""; 823 for (int i = 0; i < ary.length; i++) { 824 ret += hex.charAt((ary[i] >> 4) & 0xf); 825 ret += hex.charAt(ary[i] & 0xf); 826 } 827 return ret; 828 } 829 830 /** 831 * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak 832 */ 833 public boolean isLockPasswordEnabled() { 834 long mode = getLong(PASSWORD_TYPE_KEY, 0); 835 long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0); 836 final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC 837 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC 838 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC 839 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 840 final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC 841 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC 842 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC 843 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 844 845 return savedPasswordExists() && (passwordEnabled || 846 (usingBiometricWeak() && backupEnabled)); 847 } 848 849 /** 850 * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak 851 */ 852 public boolean isLockPatternEnabled() { 853 final boolean backupEnabled = 854 getLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) 855 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 856 857 return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false) 858 && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) 859 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING || 860 (usingBiometricWeak() && backupEnabled)); 861 } 862 863 /** 864 * @return Whether biometric weak lock is installed and that the front facing camera exists 865 */ 866 public boolean isBiometricWeakInstalled() { 867 // Check that it's installed 868 PackageManager pm = mContext.getPackageManager(); 869 try { 870 pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES); 871 } catch (PackageManager.NameNotFoundException e) { 872 return false; 873 } 874 875 // Check that the camera is enabled 876 if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) { 877 return false; 878 } 879 if (getDevicePolicyManager().getCameraDisabled(null, getCurrentOrCallingUserId())) { 880 return false; 881 } 882 883 884 return true; 885 } 886 887 /** 888 * Set whether biometric weak liveliness is enabled. 889 */ 890 public void setBiometricWeakLivelinessEnabled(boolean enabled) { 891 long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L); 892 long newFlag; 893 if (enabled) { 894 newFlag = currentFlag | FLAG_BIOMETRIC_WEAK_LIVELINESS; 895 } else { 896 newFlag = currentFlag & ~FLAG_BIOMETRIC_WEAK_LIVELINESS; 897 } 898 setLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, newFlag); 899 } 900 901 /** 902 * @return Whether the biometric weak liveliness is enabled. 903 */ 904 public boolean isBiometricWeakLivelinessEnabled() { 905 long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L); 906 return ((currentFlag & FLAG_BIOMETRIC_WEAK_LIVELINESS) != 0); 907 } 908 909 /** 910 * Set whether the lock pattern is enabled. 911 */ 912 public void setLockPatternEnabled(boolean enabled) { 913 setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled); 914 } 915 916 /** 917 * @return Whether the visible pattern is enabled. 918 */ 919 public boolean isVisiblePatternEnabled() { 920 return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false); 921 } 922 923 /** 924 * Set whether the visible pattern is enabled. 925 */ 926 public void setVisiblePatternEnabled(boolean enabled) { 927 setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled); 928 } 929 930 /** 931 * @return Whether tactile feedback for the pattern is enabled. 932 */ 933 public boolean isTactileFeedbackEnabled() { 934 return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, false); 935 } 936 937 /** 938 * Set whether tactile feedback for the pattern is enabled. 939 */ 940 public void setTactileFeedbackEnabled(boolean enabled) { 941 setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled); 942 } 943 944 /** 945 * Set and store the lockout deadline, meaning the user can't attempt his/her unlock 946 * pattern until the deadline has passed. 947 * @return the chosen deadline. 948 */ 949 public long setLockoutAttemptDeadline() { 950 final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS; 951 setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline); 952 return deadline; 953 } 954 955 /** 956 * @return The elapsed time in millis in the future when the user is allowed to 957 * attempt to enter his/her lock pattern, or 0 if the user is welcome to 958 * enter a pattern. 959 */ 960 public long getLockoutAttemptDeadline() { 961 final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L); 962 final long now = SystemClock.elapsedRealtime(); 963 if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) { 964 return 0L; 965 } 966 return deadline; 967 } 968 969 /** 970 * @return Whether the user is permanently locked out until they verify their 971 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed 972 * attempts. 973 */ 974 public boolean isPermanentlyLocked() { 975 return getBoolean(LOCKOUT_PERMANENT_KEY, false); 976 } 977 978 /** 979 * Set the state of whether the device is permanently locked, meaning the user 980 * must authenticate via other means. 981 * 982 * @param locked Whether the user is permanently locked out until they verify their 983 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed 984 * attempts. 985 */ 986 public void setPermanentlyLocked(boolean locked) { 987 setBoolean(LOCKOUT_PERMANENT_KEY, locked); 988 } 989 990 public boolean isEmergencyCallCapable() { 991 return mContext.getResources().getBoolean( 992 com.android.internal.R.bool.config_voice_capable); 993 } 994 995 public boolean isPukUnlockScreenEnable() { 996 return mContext.getResources().getBoolean( 997 com.android.internal.R.bool.config_enable_puk_unlock_screen); 998 } 999 1000 public boolean isEmergencyCallEnabledWhileSimLocked() { 1001 return mContext.getResources().getBoolean( 1002 com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked); 1003 } 1004 1005 /** 1006 * @return A formatted string of the next alarm (for showing on the lock screen), 1007 * or null if there is no next alarm. 1008 */ 1009 public String getNextAlarm() { 1010 String nextAlarm = Settings.System.getString(mContentResolver, 1011 Settings.System.NEXT_ALARM_FORMATTED); 1012 if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) { 1013 return null; 1014 } 1015 return nextAlarm; 1016 } 1017 1018 private boolean getBoolean(String secureSettingKey, boolean defaultValue) { 1019 try { 1020 return getLockSettings().getBoolean(secureSettingKey, defaultValue, 1021 getCurrentOrCallingUserId()); 1022 } catch (RemoteException re) { 1023 return defaultValue; 1024 } 1025 } 1026 1027 private void setBoolean(String secureSettingKey, boolean enabled) { 1028 try { 1029 getLockSettings().setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId()); 1030 } catch (RemoteException re) { 1031 // What can we do? 1032 Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re); 1033 } 1034 } 1035 1036 public int[] getUserDefinedWidgets() { 1037 int appWidgetId = -1; 1038 String appWidgetIdString = Settings.Secure.getString( 1039 mContentResolver, Settings.Secure.LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID); 1040 if (appWidgetIdString != null) { 1041 appWidgetId = (int) Integer.decode(appWidgetIdString); 1042 } 1043 1044 return new int[] { appWidgetId }; 1045 } 1046 1047 public int getStatusWidget() { 1048 int appWidgetId = -1; 1049 String appWidgetIdString = Settings.Secure.getString( 1050 mContentResolver, Settings.Secure.LOCK_SCREEN_STATUS_APPWIDGET_ID); 1051 if (appWidgetIdString != null) { 1052 appWidgetId = (int) Integer.decode(appWidgetIdString); 1053 } 1054 1055 return appWidgetId; 1056 } 1057 1058 private long getLong(String secureSettingKey, long defaultValue) { 1059 try { 1060 return getLockSettings().getLong(secureSettingKey, defaultValue, 1061 getCurrentOrCallingUserId()); 1062 } catch (RemoteException re) { 1063 return defaultValue; 1064 } 1065 } 1066 1067 private void setLong(String secureSettingKey, long value) { 1068 setLong(secureSettingKey, value, getCurrentOrCallingUserId()); 1069 } 1070 1071 private void setLong(String secureSettingKey, long value, int userHandle) { 1072 try { 1073 getLockSettings().setLong(secureSettingKey, value, getCurrentOrCallingUserId()); 1074 } catch (RemoteException re) { 1075 // What can we do? 1076 Log.e(TAG, "Couldn't write long " + secureSettingKey + re); 1077 } 1078 } 1079 1080 private String getString(String secureSettingKey) { 1081 return getString(secureSettingKey, getCurrentOrCallingUserId()); 1082 } 1083 1084 private String getString(String secureSettingKey, int userHandle) { 1085 try { 1086 return getLockSettings().getString(secureSettingKey, null, userHandle); 1087 } catch (RemoteException re) { 1088 return null; 1089 } 1090 } 1091 1092 private void setString(String secureSettingKey, String value, int userHandle) { 1093 try { 1094 getLockSettings().setString(secureSettingKey, value, userHandle); 1095 } catch (RemoteException re) { 1096 // What can we do? 1097 Log.e(TAG, "Couldn't write string " + secureSettingKey + re); 1098 } 1099 } 1100 1101 public boolean isSecure() { 1102 long mode = getKeyguardStoredPasswordQuality(); 1103 final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 1104 final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC 1105 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC 1106 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC 1107 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 1108 final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() 1109 || isPassword && savedPasswordExists(); 1110 return secure; 1111 } 1112 1113 /** 1114 * Sets the emergency button visibility based on isEmergencyCallCapable(). 1115 * 1116 * If the emergency button is visible, sets the text on the emergency button 1117 * to indicate what action will be taken. 1118 * 1119 * If there's currently a call in progress, the button will take them to the call 1120 * @param button the button to update 1121 * @param the phone state: 1122 * {@link TelephonyManager#CALL_STATE_IDLE} 1123 * {@link TelephonyManager#CALL_STATE_RINGING} 1124 * {@link TelephonyManager#CALL_STATE_OFFHOOK} 1125 * @param shown indicates whether the given screen wants the emergency button to show at all 1126 * @param button 1127 * @param phoneState 1128 * @param shown shown if true; hidden if false 1129 * @param upperCase if true, converts button label string to upper case 1130 */ 1131 public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown, 1132 boolean upperCase, boolean showIcon) { 1133 if (isEmergencyCallCapable() && shown) { 1134 button.setVisibility(View.VISIBLE); 1135 } else { 1136 button.setVisibility(View.GONE); 1137 return; 1138 } 1139 1140 int textId; 1141 if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) { 1142 // show "return to call" text and show phone icon 1143 textId = R.string.lockscreen_return_to_call; 1144 int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0; 1145 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0); 1146 } else { 1147 textId = R.string.lockscreen_emergency_call; 1148 int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0; 1149 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0); 1150 } 1151 if (upperCase) { 1152 CharSequence original = mContext.getResources().getText(textId); 1153 String upper = original != null ? original.toString().toUpperCase() : null; 1154 button.setText(upper); 1155 } else { 1156 button.setText(textId); 1157 } 1158 } 1159 1160 /** 1161 * @deprecated 1162 * @param button 1163 * @param phoneState 1164 * @param shown 1165 */ 1166 public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) { 1167 updateEmergencyCallButtonState(button, phoneState, shown, false, true); 1168 } 1169 1170 /** 1171 * Resumes a call in progress. Typically launched from the EmergencyCall button 1172 * on various lockscreens. 1173 * 1174 * @return true if we were able to tell InCallScreen to show. 1175 */ 1176 public boolean resumeCall() { 1177 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 1178 try { 1179 if (phone != null && phone.showCallScreen()) { 1180 return true; 1181 } 1182 } catch (RemoteException e) { 1183 // What can we do? 1184 } 1185 return false; 1186 } 1187 1188 private void finishBiometricWeak() { 1189 setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true); 1190 1191 // Launch intent to show final screen, this also 1192 // moves the temporary gallery to the actual gallery 1193 Intent intent = new Intent(); 1194 intent.setClassName("com.android.facelock", 1195 "com.android.facelock.SetupEndScreen"); 1196 mContext.startActivity(intent); 1197 } 1198 1199 public void setPowerButtonInstantlyLocks(boolean enabled) { 1200 setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled); 1201 } 1202 1203 public boolean getPowerButtonInstantlyLocks() { 1204 return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true); 1205 } 1206 1207} 1208