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