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