2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.widget;
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.app.admin.DevicePolicyManager;
22 import android.app.trust.IStrongAuthTracker;
23 import android.app.trust.TrustManager;
24 import android.content.ComponentName;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.pm.UserInfo;
28 import android.os.AsyncTask;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.ServiceManager;
35 import android.os.SystemClock;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.os.storage.IMountService;
39 import android.os.storage.StorageManager;
40 import android.provider.Settings;
41 import android.text.TextUtils;
42 import android.util.Log;
43 import android.util.SparseIntArray;
45 import com.google.android.collect.Lists;
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.nio.charset.StandardCharsets;
50 import java.security.MessageDigest;
51 import java.security.NoSuchAlgorithmException;
52 import java.security.SecureRandom;
53 import java.util.ArrayList;
54 import java.util.Collection;
55 import java.util.List;
57 import libcore.util.HexEncoding;
60 * Utilities for the lock pattern and its settings.
62 public class LockPatternUtils {
64 private static final String TAG = "LockPatternUtils";
65 private static final boolean DEBUG = false;
68 * The key to identify when the lock pattern enabled flag is being acccessed for legacy reasons.
70 public static final String LEGACY_LOCK_PATTERN_ENABLED = "legacy_lock_pattern_enabled";
73 * The number of incorrect attempts before which we fall back on an alternative
74 * method of verifying the user, and resetting their lock pattern.
76 public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
79 * The interval of the countdown for showing progress of the lockout.
81 public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
85 * This dictates when we start telling the user that continued failed attempts will wipe
88 public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5;
91 * The minimum number of dots in a valid pattern.
93 public static final int MIN_LOCK_PATTERN_SIZE = 4;
96 * The minimum size of a valid password.
98 public static final int MIN_LOCK_PASSWORD_SIZE = 4;
101 * The minimum number of dots the user must include in a wrong pattern
102 * attempt for it to be counted against the counts that affect
103 * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
105 public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
108 public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
109 public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
110 public final static String LOCKOUT_ATTEMPT_TIMEOUT_MS = "lockscreen.lockoutattempttimeoutmss";
111 public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
112 public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
114 public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
115 public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
116 public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
117 public final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
119 public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
120 = "lockscreen.biometric_weak_fallback";
122 public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
123 = "lockscreen.biometricweakeverchosen";
124 public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
125 = "lockscreen.power_button_instantly_locks";
127 public final static String LOCKSCREEN_WIDGETS_ENABLED = "lockscreen.widgets_enabled";
129 public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
131 private static final String LOCK_SCREEN_OWNER_INFO = Settings.Secure.LOCK_SCREEN_OWNER_INFO;
132 private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
133 Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
135 private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info";
137 private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
138 private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
140 // Maximum allowed number of repeated or ordered characters in a sequence before we'll
141 // consider it a complex PIN/password.
142 public static final int MAX_ALLOWED_SEQUENCE = 3;
144 public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
145 public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
147 private final Context mContext;
148 private final ContentResolver mContentResolver;
149 private DevicePolicyManager mDevicePolicyManager;
150 private ILockSettings mLockSettingsService;
151 private UserManager mUserManager;
152 private final Handler mHandler;
155 * Use {@link TrustManager#isTrustUsuallyManaged(int)}.
157 * This returns the lazily-peristed value and should only be used by TrustManagerService.
159 public boolean isTrustUsuallyManaged(int userId) {
160 if (!(mLockSettingsService instanceof ILockSettings.Stub)) {
161 throw new IllegalStateException("May only be called by TrustManagerService. "
162 + "Use TrustManager.isTrustUsuallyManaged()");
165 return getLockSettings().getBoolean(IS_TRUST_USUALLY_MANAGED, false, userId);
166 } catch (RemoteException e) {
171 public void setTrustUsuallyManaged(boolean managed, int userId) {
173 getLockSettings().setBoolean(IS_TRUST_USUALLY_MANAGED, managed, userId);
174 } catch (RemoteException e) {
179 public void userPresent(int userId) {
181 getLockSettings().userPresent(userId);
182 } catch (RemoteException e) {
183 throw e.rethrowFromSystemServer();
187 public static final class RequestThrottledException extends Exception {
188 private int mTimeoutMs;
189 public RequestThrottledException(int timeoutMs) {
190 mTimeoutMs = timeoutMs;
194 * @return The amount of time in ms before another request may
197 public int getTimeoutMs() {
203 public DevicePolicyManager getDevicePolicyManager() {
204 if (mDevicePolicyManager == null) {
205 mDevicePolicyManager =
206 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
207 if (mDevicePolicyManager == null) {
208 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
209 new IllegalStateException("Stack trace:"));
212 return mDevicePolicyManager;
215 private UserManager getUserManager() {
216 if (mUserManager == null) {
217 mUserManager = UserManager.get(mContext);
222 private TrustManager getTrustManager() {
223 TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
225 Log.e(TAG, "Can't get TrustManagerService: is it running?",
226 new IllegalStateException("Stack trace:"));
231 public LockPatternUtils(Context context) {
233 mContentResolver = context.getContentResolver();
235 Looper looper = Looper.myLooper();
236 mHandler = looper != null ? new Handler(looper) : null;
239 private ILockSettings getLockSettings() {
240 if (mLockSettingsService == null) {
241 ILockSettings service = ILockSettings.Stub.asInterface(
242 ServiceManager.getService("lock_settings"));
243 mLockSettingsService = service;
245 return mLockSettingsService;
248 public int getRequestedMinimumPasswordLength(int userId) {
249 return getDevicePolicyManager().getPasswordMinimumLength(null, userId);
253 * Gets the device policy password mode. If the mode is non-specific, returns
254 * MODE_PATTERN which allows the user to choose anything.
256 public int getRequestedPasswordQuality(int userId) {
257 return getDevicePolicyManager().getPasswordQuality(null, userId);
260 private int getRequestedPasswordHistoryLength(int userId) {
261 return getDevicePolicyManager().getPasswordHistoryLength(null, userId);
264 public int getRequestedPasswordMinimumLetters(int userId) {
265 return getDevicePolicyManager().getPasswordMinimumLetters(null, userId);
268 public int getRequestedPasswordMinimumUpperCase(int userId) {
269 return getDevicePolicyManager().getPasswordMinimumUpperCase(null, userId);
272 public int getRequestedPasswordMinimumLowerCase(int userId) {
273 return getDevicePolicyManager().getPasswordMinimumLowerCase(null, userId);
276 public int getRequestedPasswordMinimumNumeric(int userId) {
277 return getDevicePolicyManager().getPasswordMinimumNumeric(null, userId);
280 public int getRequestedPasswordMinimumSymbols(int userId) {
281 return getDevicePolicyManager().getPasswordMinimumSymbols(null, userId);
284 public int getRequestedPasswordMinimumNonLetter(int userId) {
285 return getDevicePolicyManager().getPasswordMinimumNonLetter(null, userId);
288 public void reportFailedPasswordAttempt(int userId) {
289 getDevicePolicyManager().reportFailedPasswordAttempt(userId);
290 getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
293 public void reportSuccessfulPasswordAttempt(int userId) {
294 getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
295 getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
298 public int getCurrentFailedPasswordAttempts(int userId) {
299 return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
302 public int getMaximumFailedPasswordsForWipe(int userId) {
303 return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
304 null /* componentName */, userId);
308 * Check to see if a pattern matches the saved pattern.
309 * If pattern matches, return an opaque attestation that the challenge
312 * @param pattern The pattern to check.
313 * @param challenge The challenge to verify against the pattern
314 * @return the attestation that the challenge was verified, or null.
316 public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
317 throws RequestThrottledException {
318 throwIfCalledOnMainThread();
320 VerifyCredentialResponse response =
321 getLockSettings().verifyPattern(patternToString(pattern), challenge, userId);
322 if (response == null) {
327 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
328 return response.getPayload();
329 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
330 throw new RequestThrottledException(response.getTimeout());
334 } catch (RemoteException re) {
340 * Check to see if a pattern matches the saved pattern. If no pattern exists,
341 * always returns true.
342 * @param pattern The pattern to check.
343 * @return Whether the pattern matches the stored one.
345 public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
346 throws RequestThrottledException {
347 return checkPattern(pattern, userId, null /* progressCallback */);
351 * Check to see if a pattern matches the saved pattern. If no pattern exists,
352 * always returns true.
353 * @param pattern The pattern to check.
354 * @return Whether the pattern matches the stored one.
356 public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId,
357 @Nullable CheckCredentialProgressCallback progressCallback)
358 throws RequestThrottledException {
359 throwIfCalledOnMainThread();
361 VerifyCredentialResponse response =
362 getLockSettings().checkPattern(patternToString(pattern), userId,
363 wrapCallback(progressCallback));
365 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
367 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
368 throw new RequestThrottledException(response.getTimeout());
372 } catch (RemoteException re) {
378 * Check to see if a password matches the saved password.
379 * If password matches, return an opaque attestation that the challenge
382 * @param password The password to check.
383 * @param challenge The challenge to verify against the password
384 * @return the attestation that the challenge was verified, or null.
386 public byte[] verifyPassword(String password, long challenge, int userId)
387 throws RequestThrottledException {
388 throwIfCalledOnMainThread();
390 VerifyCredentialResponse response =
391 getLockSettings().verifyPassword(password, challenge, userId);
393 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
394 return response.getPayload();
395 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
396 throw new RequestThrottledException(response.getTimeout());
400 } catch (RemoteException re) {
407 * Check to see if a password matches the saved password.
408 * If password matches, return an opaque attestation that the challenge
411 * @param password The password to check.
412 * @param challenge The challenge to verify against the password
413 * @return the attestation that the challenge was verified, or null.
415 public byte[] verifyTiedProfileChallenge(String password, boolean isPattern, long challenge,
416 int userId) throws RequestThrottledException {
417 throwIfCalledOnMainThread();
419 VerifyCredentialResponse response =
420 getLockSettings().verifyTiedProfileChallenge(password, isPattern, challenge,
423 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
424 return response.getPayload();
425 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
426 throw new RequestThrottledException(response.getTimeout());
430 } catch (RemoteException re) {
436 * Check to see if a password matches the saved password. If no password exists,
437 * always returns true.
438 * @param password The password to check.
439 * @return Whether the password matches the stored one.
441 public boolean checkPassword(String password, int userId) throws RequestThrottledException {
442 return checkPassword(password, userId, null /* progressCallback */);
446 * Check to see if a password matches the saved password. If no password exists,
447 * always returns true.
448 * @param password The password to check.
449 * @return Whether the password matches the stored one.
451 public boolean checkPassword(String password, int userId,
452 @Nullable CheckCredentialProgressCallback progressCallback)
453 throws RequestThrottledException {
454 throwIfCalledOnMainThread();
456 VerifyCredentialResponse response =
457 getLockSettings().checkPassword(password, userId, wrapCallback(progressCallback));
458 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
460 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
461 throw new RequestThrottledException(response.getTimeout());
465 } catch (RemoteException re) {
471 * Check to see if vold already has the password.
472 * Note that this also clears vold's copy of the password.
473 * @return Whether the vold password matches or not.
475 public boolean checkVoldPassword(int userId) {
477 return getLockSettings().checkVoldPassword(userId);
478 } catch (RemoteException re) {
484 * Check to see if a password matches any of the passwords stored in the
487 * @param password The password to check.
488 * @return Whether the password matches any in the history.
490 public boolean checkPasswordHistory(String password, int userId) {
491 String passwordHashString = new String(
492 passwordToHash(password, userId), StandardCharsets.UTF_8);
493 String passwordHistory = getString(PASSWORD_HISTORY_KEY, userId);
494 if (passwordHistory == null) {
497 // Password History may be too long...
498 int passwordHashLength = passwordHashString.length();
499 int passwordHistoryLength = getRequestedPasswordHistoryLength(userId);
500 if(passwordHistoryLength == 0) {
503 int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
504 + passwordHistoryLength - 1;
505 if (passwordHistory.length() > neededPasswordHistoryLength) {
506 passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
508 return passwordHistory.contains(passwordHashString);
512 * Check to see if the user has stored a lock pattern.
513 * @return Whether a saved pattern exists.
515 private boolean savedPatternExists(int userId) {
517 return getLockSettings().havePattern(userId);
518 } catch (RemoteException re) {
524 * Check to see if the user has stored a lock pattern.
525 * @return Whether a saved pattern exists.
527 private boolean savedPasswordExists(int userId) {
529 return getLockSettings().havePassword(userId);
530 } catch (RemoteException re) {
536 * Return true if the user has ever chosen a pattern. This is true even if the pattern is
539 * @return True if the user has ever chosen a pattern.
541 public boolean isPatternEverChosen(int userId) {
542 return getBoolean(PATTERN_EVER_CHOSEN_KEY, false, userId);
546 * Used by device policy manager to validate the current password
547 * information it has.
549 public int getActivePasswordQuality(int userId) {
550 int quality = getKeyguardStoredPasswordQuality(userId);
552 if (isLockPasswordEnabled(quality, userId)) {
553 // Quality is a password and a password exists. Return the quality.
557 if (isLockPatternEnabled(quality, userId)) {
558 // Quality is a pattern and a pattern exists. Return the quality.
562 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
566 * Use it to reset keystore without wiping work profile
568 public void resetKeyStore(int userId) {
570 getLockSettings().resetKeyStore(userId);
571 } catch (RemoteException e) {
572 // It should not happen
573 Log.e(TAG, "Couldn't reset keystore " + e);
578 * Clear any lock pattern or password.
580 public void clearLock(int userHandle) {
581 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
584 getLockSettings().setLockPassword(null, null, userHandle);
585 getLockSettings().setLockPattern(null, null, userHandle);
586 } catch (RemoteException e) {
590 if (userHandle == UserHandle.USER_SYSTEM) {
591 // Set the encryption password to default.
592 updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
593 setCredentialRequiredToDecrypt(false);
596 onAfterChangingPassword(userHandle);
600 * Disable showing lock screen at all for a given user.
601 * This is only meaningful if pattern, pin or password are not set.
603 * @param disable Disables lock screen when true
604 * @param userId User ID of the user this has effect on
606 public void setLockScreenDisabled(boolean disable, int userId) {
607 setBoolean(DISABLE_LOCKSCREEN_KEY, disable, userId);
611 * Determine if LockScreen is disabled for the current user. This is used to decide whether
612 * LockScreen is shown after reboot or after screen timeout / short press on power.
614 * @return true if lock screen is disabled
616 public boolean isLockScreenDisabled(int userId) {
617 return !isSecure(userId) &&
618 getBoolean(DISABLE_LOCKSCREEN_KEY, false, userId);
622 * Save a lock pattern.
623 * @param pattern The new pattern to save.
624 * @param userId the user whose pattern is to be saved.
626 public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) {
627 this.saveLockPattern(pattern, null, userId);
630 * Save a lock pattern.
631 * @param pattern The new pattern to save.
632 * @param savedPattern The previously saved pattern, converted to String format
633 * @param userId the user whose pattern is to be saved.
635 public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
637 if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
638 throw new IllegalArgumentException("pattern must not be null and at least "
639 + MIN_LOCK_PATTERN_SIZE + " dots long.");
642 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
643 getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
644 DevicePolicyManager dpm = getDevicePolicyManager();
646 // Update the device encryption password.
647 if (userId == UserHandle.USER_SYSTEM
648 && LockPatternUtils.isDeviceEncryptionEnabled()) {
649 if (!shouldEncryptWithCredentials(true)) {
650 clearEncryptionPassword();
652 String stringPattern = patternToString(pattern);
653 updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
657 setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
659 onAfterChangingPassword(userId);
660 } catch (RemoteException re) {
661 Log.e(TAG, "Couldn't save lock pattern " + re);
665 private void updateCryptoUserInfo(int userId) {
666 if (userId != UserHandle.USER_SYSTEM) {
670 final String ownerInfo = isOwnerInfoEnabled(userId) ? getOwnerInfo(userId) : "";
672 IBinder service = ServiceManager.getService("mount");
673 if (service == null) {
674 Log.e(TAG, "Could not find the mount service to update the user info");
678 IMountService mountService = IMountService.Stub.asInterface(service);
680 Log.d(TAG, "Setting owner info");
681 mountService.setField(StorageManager.OWNER_INFO_KEY, ownerInfo);
682 } catch (RemoteException e) {
683 Log.e(TAG, "Error changing user info", e);
687 public void setOwnerInfo(String info, int userId) {
688 setString(LOCK_SCREEN_OWNER_INFO, info, userId);
689 updateCryptoUserInfo(userId);
692 public void setOwnerInfoEnabled(boolean enabled, int userId) {
693 setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled, userId);
694 updateCryptoUserInfo(userId);
697 public String getOwnerInfo(int userId) {
698 return getString(LOCK_SCREEN_OWNER_INFO, userId);
701 public boolean isOwnerInfoEnabled(int userId) {
702 return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false, userId);
706 * Sets the device owner information. If the information is {@code null} or empty then the
707 * device owner info is cleared.
709 * @param info Device owner information which will be displayed instead of the user
712 public void setDeviceOwnerInfo(String info) {
713 if (info != null && info.isEmpty()) {
717 setString(LOCK_SCREEN_DEVICE_OWNER_INFO, info, UserHandle.USER_SYSTEM);
720 public String getDeviceOwnerInfo() {
721 return getString(LOCK_SCREEN_DEVICE_OWNER_INFO, UserHandle.USER_SYSTEM);
724 public boolean isDeviceOwnerInfoEnabled() {
725 return getDeviceOwnerInfo() != null;
729 * Compute the password quality from the given password string.
731 static public int computePasswordQuality(String password) {
732 boolean hasDigit = false;
733 boolean hasNonDigit = false;
734 final int len = password.length();
735 for (int i = 0; i < len; i++) {
736 if (Character.isDigit(password.charAt(i))) {
743 if (hasNonDigit && hasDigit) {
744 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
747 return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
750 return maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
751 ? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
752 : DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
754 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
757 private static int categoryChar(char c) {
758 if ('a' <= c && c <= 'z') return 0;
759 if ('A' <= c && c <= 'Z') return 1;
760 if ('0' <= c && c <= '9') return 2;
764 private static int maxDiffCategory(int category) {
765 if (category == 0 || category == 1) return 1;
766 else if (category == 2) return 10;
771 * Returns the maximum length of a sequential characters. A sequence is defined as
772 * monotonically increasing characters with a constant interval or the same character repeated.
775 * maxLengthSequence("1234") == 4
776 * maxLengthSequence("1234abc") == 4
777 * maxLengthSequence("aabc") == 3
778 * maxLengthSequence("qwertyuio") == 1
779 * maxLengthSequence("@ABC") == 3
780 * maxLengthSequence(";;;;") == 4 (anything that repeats)
781 * maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits)
783 * @param string the pass
784 * @return the number of sequential letters or digits
786 public static int maxLengthSequence(String string) {
787 if (string.length() == 0) return 0;
788 char previousChar = string.charAt(0);
789 int category = categoryChar(previousChar); //current category of the sequence
790 int diff = 0; //difference between two consecutive characters
791 boolean hasDiff = false; //if we are currently targeting a sequence
792 int maxLength = 0; //maximum length of a sequence already found
793 int startSequence = 0; //where the current sequence started
794 for (int current = 1; current < string.length(); current++) {
795 char currentChar = string.charAt(current);
796 int categoryCurrent = categoryChar(currentChar);
797 int currentDiff = (int) currentChar - (int) previousChar;
798 if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
799 maxLength = Math.max(maxLength, current - startSequence);
800 startSequence = current;
802 category = categoryCurrent;
805 if(hasDiff && currentDiff != diff) {
806 maxLength = Math.max(maxLength, current - startSequence);
807 startSequence = current - 1;
812 previousChar = currentChar;
814 maxLength = Math.max(maxLength, string.length() - startSequence);
818 /** Update the encryption password if it is enabled **/
819 private void updateEncryptionPassword(final int type, final String password) {
820 if (!isDeviceEncryptionEnabled()) {
823 final IBinder service = ServiceManager.getService("mount");
824 if (service == null) {
825 Log.e(TAG, "Could not find the mount service to update the encryption password");
829 new AsyncTask<Void, Void, Void>() {
831 protected Void doInBackground(Void... dummy) {
832 IMountService mountService = IMountService.Stub.asInterface(service);
834 mountService.changeEncryptionPassword(type, password);
835 } catch (RemoteException e) {
836 Log.e(TAG, "Error changing encryption password", e);
844 * Save a lock password. Does not ensure that the password is as good
845 * as the requested mode, but will adjust the mode to be as good as the
847 * @param password The password to save
848 * @param savedPassword The previously saved lock password, or null if none
849 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
850 * @param userHandle The userId of the user to change the password for
852 public void saveLockPassword(String password, String savedPassword, int quality,
855 DevicePolicyManager dpm = getDevicePolicyManager();
856 if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
857 throw new IllegalArgumentException("password must not be null and at least "
858 + "of length " + MIN_LOCK_PASSWORD_SIZE);
861 final int computedQuality = computePasswordQuality(password);
862 setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
863 getLockSettings().setLockPassword(password, savedPassword, userHandle);
865 // Update the device encryption password.
866 if (userHandle == UserHandle.USER_SYSTEM
867 && LockPatternUtils.isDeviceEncryptionEnabled()) {
868 if (!shouldEncryptWithCredentials(true)) {
869 clearEncryptionPassword();
871 boolean numeric = computedQuality
872 == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
873 boolean numericComplex = computedQuality
874 == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
875 int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
876 : StorageManager.CRYPT_TYPE_PASSWORD;
877 updateEncryptionPassword(type, password);
881 // Add the password to the password history. We assume all
882 // password hashes have the same length for simplicity of implementation.
883 String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
884 if (passwordHistory == null) {
885 passwordHistory = "";
887 int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
888 if (passwordHistoryLength == 0) {
889 passwordHistory = "";
891 byte[] hash = passwordToHash(password, userHandle);
892 passwordHistory = new String(hash, StandardCharsets.UTF_8) + "," + passwordHistory;
893 // Cut it to contain passwordHistoryLength hashes
894 // and passwordHistoryLength -1 commas.
895 passwordHistory = passwordHistory.substring(0, Math.min(hash.length
896 * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
899 setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
900 onAfterChangingPassword(userHandle);
901 } catch (RemoteException re) {
903 Log.e(TAG, "Unable to save lock password " + re);
908 * Determine if the device supports encryption, even if it's set to default. This
909 * differs from isDeviceEncrypted() in that it returns true even if the device is
910 * encrypted with the default password.
911 * @return true if device encryption is enabled
913 public static boolean isDeviceEncryptionEnabled() {
914 return StorageManager.isEncrypted();
918 * Determine if the device is file encrypted
919 * @return true if device is file encrypted
921 public static boolean isFileEncryptionEnabled() {
922 return StorageManager.isFileEncryptedNativeOrEmulated();
926 * Clears the encryption password.
928 public void clearEncryptionPassword() {
929 updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
933 * Retrieves the quality mode for {@param userHandle}.
934 * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
936 * @return stored password quality
938 public int getKeyguardStoredPasswordQuality(int userHandle) {
939 return (int) getLong(PASSWORD_TYPE_KEY,
940 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
944 * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
945 * for user handles that do not belong to a managed profile.
947 * @param userHandle Managed profile user id
948 * @param enabled True if separate challenge is enabled
949 * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is
952 public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
953 String managedUserPassword) {
954 UserInfo info = getUserManager().getUserInfo(userHandle);
955 if (info.isManagedProfile()) {
957 getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
958 managedUserPassword);
959 onAfterChangingPassword(userHandle);
960 } catch (RemoteException e) {
961 Log.e(TAG, "Couldn't update work profile challenge enabled");
967 * Retrieves whether the Separate Profile Challenge is enabled for this {@param userHandle}.
969 public boolean isSeparateProfileChallengeEnabled(int userHandle) {
970 UserInfo info = getUserManager().getUserInfo(userHandle);
971 if (info == null || !info.isManagedProfile()) {
975 return getLockSettings().getSeparateProfileChallengeEnabled(userHandle);
976 } catch (RemoteException e) {
977 Log.e(TAG, "Couldn't get separate profile challenge enabled");
978 // Default value is false
984 * Retrieves whether the current DPM allows use of the Profile Challenge.
986 public boolean isSeparateProfileChallengeAllowed(int userHandle) {
987 UserInfo info = getUserManager().getUserInfo(userHandle);
988 if (info == null || !info.isManagedProfile()) {
991 return getDevicePolicyManager().isSeparateProfileChallengeAllowed(userHandle);
995 * Retrieves whether the current profile and device locks can be unified.
997 public boolean isSeparateProfileChallengeAllowedToUnify(int userHandle) {
998 return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle);
1002 * Deserialize a pattern.
1003 * @param string The pattern serialized with {@link #patternToString}
1004 * @return The pattern.
1006 public static List<LockPatternView.Cell> stringToPattern(String string) {
1007 if (string == null) {
1011 List<LockPatternView.Cell> result = Lists.newArrayList();
1013 final byte[] bytes = string.getBytes();
1014 for (int i = 0; i < bytes.length; i++) {
1015 byte b = (byte) (bytes[i] - '1');
1016 result.add(LockPatternView.Cell.of(b / 3, b % 3));
1022 * Serialize a pattern.
1023 * @param pattern The pattern.
1024 * @return The pattern in string form.
1026 public static String patternToString(List<LockPatternView.Cell> pattern) {
1027 if (pattern == null) {
1030 final int patternSize = pattern.size();
1032 byte[] res = new byte[patternSize];
1033 for (int i = 0; i < patternSize; i++) {
1034 LockPatternView.Cell cell = pattern.get(i);
1035 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
1037 return new String(res);
1040 public static String patternStringToBaseZero(String pattern) {
1041 if (pattern == null) {
1044 final int patternSize = pattern.length();
1046 byte[] res = new byte[patternSize];
1047 final byte[] bytes = pattern.getBytes();
1048 for (int i = 0; i < patternSize; i++) {
1049 res[i] = (byte) (bytes[i] - '1');
1051 return new String(res);
1055 * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
1056 * at least a second level of protection. First level is that the file
1057 * is in a location only readable by the system process.
1058 * @param pattern the gesture pattern.
1059 * @return the hash of the pattern in a byte array.
1061 public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
1062 if (pattern == null) {
1066 final int patternSize = pattern.size();
1067 byte[] res = new byte[patternSize];
1068 for (int i = 0; i < patternSize; i++) {
1069 LockPatternView.Cell cell = pattern.get(i);
1070 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
1073 MessageDigest md = MessageDigest.getInstance("SHA-1");
1074 byte[] hash = md.digest(res);
1076 } catch (NoSuchAlgorithmException nsa) {
1081 private String getSalt(int userId) {
1082 long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
1085 salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
1086 setLong(LOCK_PASSWORD_SALT_KEY, salt, userId);
1087 Log.v(TAG, "Initialized lock password salt for user: " + userId);
1088 } catch (NoSuchAlgorithmException e) {
1089 // Throw an exception rather than storing a password we'll never be able to recover
1090 throw new IllegalStateException("Couldn't get SecureRandom number", e);
1093 return Long.toHexString(salt);
1097 * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
1098 * Not the most secure, but it is at least a second level of protection. First level is that
1099 * the file is in a location only readable by the system process.
1101 * @param password the gesture pattern.
1103 * @return the hash of the pattern in a byte array.
1105 public byte[] passwordToHash(String password, int userId) {
1106 if (password == null) {
1111 byte[] saltedPassword = (password + getSalt(userId)).getBytes();
1112 byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
1113 byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
1115 byte[] combined = new byte[sha1.length + md5.length];
1116 System.arraycopy(sha1, 0, combined, 0, sha1.length);
1117 System.arraycopy(md5, 0, combined, sha1.length, md5.length);
1119 final char[] hexEncoded = HexEncoding.encode(combined);
1120 return new String(hexEncoded).getBytes(StandardCharsets.UTF_8);
1121 } catch (NoSuchAlgorithmException e) {
1122 throw new AssertionError("Missing digest algorithm: ", e);
1127 * @param userId the user for which to report the value
1128 * @return Whether the lock screen is secured.
1130 public boolean isSecure(int userId) {
1131 int mode = getKeyguardStoredPasswordQuality(userId);
1132 return isLockPatternEnabled(mode, userId) || isLockPasswordEnabled(mode, userId);
1135 public boolean isLockPasswordEnabled(int userId) {
1136 return isLockPasswordEnabled(getKeyguardStoredPasswordQuality(userId), userId);
1139 private boolean isLockPasswordEnabled(int mode, int userId) {
1140 final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1141 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1142 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1143 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1144 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
1145 || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
1146 return passwordEnabled && savedPasswordExists(userId);
1150 * @return Whether the lock pattern is enabled
1152 public boolean isLockPatternEnabled(int userId) {
1153 return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId);
1157 public boolean isLegacyLockPatternEnabled(int userId) {
1158 // Note: this value should default to {@code true} to avoid any reset that might result.
1159 // We must use a special key to read this value, since it will by default return the value
1160 // based on the new logic.
1161 return getBoolean(LEGACY_LOCK_PATTERN_ENABLED, true, userId);
1165 public void setLegacyLockPatternEnabled(int userId) {
1166 setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, true, userId);
1169 private boolean isLockPatternEnabled(int mode, int userId) {
1170 return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
1171 && savedPatternExists(userId);
1175 * @return Whether the visible pattern is enabled.
1177 public boolean isVisiblePatternEnabled(int userId) {
1178 return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false, userId);
1182 * Set whether the visible pattern is enabled.
1184 public void setVisiblePatternEnabled(boolean enabled, int userId) {
1185 setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled, userId);
1187 // Update for crypto if owner
1188 if (userId != UserHandle.USER_SYSTEM) {
1192 IBinder service = ServiceManager.getService("mount");
1193 if (service == null) {
1194 Log.e(TAG, "Could not find the mount service to update the user info");
1198 IMountService mountService = IMountService.Stub.asInterface(service);
1200 mountService.setField(StorageManager.PATTERN_VISIBLE_KEY, enabled ? "1" : "0");
1201 } catch (RemoteException e) {
1202 Log.e(TAG, "Error changing pattern visible state", e);
1207 * Set whether the visible password is enabled for cryptkeeper screen.
1209 public void setVisiblePasswordEnabled(boolean enabled, int userId) {
1210 // Update for crypto if owner
1211 if (userId != UserHandle.USER_SYSTEM) {
1215 IBinder service = ServiceManager.getService("mount");
1216 if (service == null) {
1217 Log.e(TAG, "Could not find the mount service to update the user info");
1221 IMountService mountService = IMountService.Stub.asInterface(service);
1223 mountService.setField(StorageManager.PASSWORD_VISIBLE_KEY, enabled ? "1" : "0");
1224 } catch (RemoteException e) {
1225 Log.e(TAG, "Error changing password visible state", e);
1230 * @return Whether tactile feedback for the pattern is enabled.
1232 public boolean isTactileFeedbackEnabled() {
1233 return Settings.System.getIntForUser(mContentResolver,
1234 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
1238 * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
1239 * pattern until the deadline has passed.
1240 * @return the chosen deadline.
1242 public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
1243 final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
1244 setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
1245 setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, timeoutMs, userId);
1250 * @return The elapsed time in millis in the future when the user is allowed to
1251 * attempt to enter his/her lock pattern, or 0 if the user is welcome to
1254 public long getLockoutAttemptDeadline(int userId) {
1255 long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
1256 final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
1257 final long now = SystemClock.elapsedRealtime();
1258 if (deadline < now && deadline != 0) {
1260 setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId);
1261 setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId);
1265 if (deadline > (now + timeoutMs)) {
1266 // device was rebooted, set new deadline
1267 deadline = now + timeoutMs;
1268 setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
1274 private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
1276 return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
1277 } catch (RemoteException re) {
1278 return defaultValue;
1282 private void setBoolean(String secureSettingKey, boolean enabled, int userId) {
1284 getLockSettings().setBoolean(secureSettingKey, enabled, userId);
1285 } catch (RemoteException re) {
1287 Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re);
1291 private long getLong(String secureSettingKey, long defaultValue, int userHandle) {
1293 return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
1294 } catch (RemoteException re) {
1295 return defaultValue;
1299 private void setLong(String secureSettingKey, long value, int userHandle) {
1301 getLockSettings().setLong(secureSettingKey, value, userHandle);
1302 } catch (RemoteException re) {
1304 Log.e(TAG, "Couldn't write long " + secureSettingKey + re);
1308 private String getString(String secureSettingKey, int userHandle) {
1310 return getLockSettings().getString(secureSettingKey, null, userHandle);
1311 } catch (RemoteException re) {
1316 private void setString(String secureSettingKey, String value, int userHandle) {
1318 getLockSettings().setString(secureSettingKey, value, userHandle);
1319 } catch (RemoteException re) {
1321 Log.e(TAG, "Couldn't write string " + secureSettingKey + re);
1325 public void setPowerButtonInstantlyLocks(boolean enabled, int userId) {
1326 setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled, userId);
1329 public boolean getPowerButtonInstantlyLocks(int userId) {
1330 return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true, userId);
1333 public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
1334 StringBuilder sb = new StringBuilder();
1335 for (ComponentName cn : activeTrustAgents) {
1336 if (sb.length() > 0) {
1339 sb.append(cn.flattenToShortString());
1341 setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
1342 getTrustManager().reportEnabledTrustAgentsChanged(userId);
1345 public List<ComponentName> getEnabledTrustAgents(int userId) {
1346 String serialized = getString(ENABLED_TRUST_AGENTS, userId);
1347 if (TextUtils.isEmpty(serialized)) {
1350 String[] split = serialized.split(",");
1351 ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
1352 for (String s : split) {
1353 if (!TextUtils.isEmpty(s)) {
1354 activeTrustAgents.add(ComponentName.unflattenFromString(s));
1357 return activeTrustAgents;
1361 * Disable trust until credentials have been entered for user {@param userId}.
1363 * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
1365 * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
1367 public void requireCredentialEntry(int userId) {
1368 requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
1372 * Requests strong authentication for user {@param userId}.
1374 * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
1376 * @param strongAuthReason a combination of {@link StrongAuthTracker.StrongAuthFlags} indicating
1377 * the reason for and the strength of the requested authentication.
1378 * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
1380 public void requireStrongAuth(@StrongAuthTracker.StrongAuthFlags int strongAuthReason,
1383 getLockSettings().requireStrongAuth(strongAuthReason, userId);
1384 } catch (RemoteException e) {
1385 Log.e(TAG, "Error while requesting strong auth: " + e);
1389 private void onAfterChangingPassword(int userHandle) {
1390 getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
1393 public boolean isCredentialRequiredToDecrypt(boolean defaultValue) {
1394 final int value = Settings.Global.getInt(mContentResolver,
1395 Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, -1);
1396 return value == -1 ? defaultValue : (value != 0);
1399 public void setCredentialRequiredToDecrypt(boolean required) {
1400 if (!(getUserManager().isSystemUser() || getUserManager().isPrimaryUser())) {
1401 throw new IllegalStateException(
1402 "Only the system or primary user may call setCredentialRequiredForDecrypt()");
1405 if (isDeviceEncryptionEnabled()){
1406 Settings.Global.putInt(mContext.getContentResolver(),
1407 Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
1411 private boolean isDoNotAskCredentialsOnBootSet() {
1412 return mDevicePolicyManager.getDoNotAskCredentialsOnBoot();
1415 private boolean shouldEncryptWithCredentials(boolean defaultValue) {
1416 return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet();
1419 private void throwIfCalledOnMainThread() {
1420 if (Looper.getMainLooper().isCurrentThread()) {
1421 throw new IllegalStateException("should not be called from the main thread.");
1425 public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
1427 getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub);
1428 } catch (RemoteException e) {
1429 throw new RuntimeException("Could not register StrongAuthTracker");
1433 public void unregisterStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
1435 getLockSettings().unregisterStrongAuthTracker(strongAuthTracker.mStub);
1436 } catch (RemoteException e) {
1437 Log.e(TAG, "Could not unregister StrongAuthTracker", e);
1442 * @see StrongAuthTracker#getStrongAuthForUser
1444 public int getStrongAuthForUser(int userId) {
1446 return getLockSettings().getStrongAuthForUser(userId);
1447 } catch (RemoteException e) {
1448 Log.e(TAG, "Could not get StrongAuth", e);
1449 return StrongAuthTracker.getDefaultFlags(mContext);
1454 * @see StrongAuthTracker#isTrustAllowedForUser
1456 public boolean isTrustAllowedForUser(int userId) {
1457 return getStrongAuthForUser(userId) == StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
1461 * @see StrongAuthTracker#isFingerprintAllowedForUser
1463 public boolean isFingerprintAllowedForUser(int userId) {
1464 return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0;
1467 private ICheckCredentialProgressCallback wrapCallback(
1468 final CheckCredentialProgressCallback callback) {
1469 if (callback == null) {
1472 if (mHandler == null) {
1473 throw new IllegalStateException("Must construct LockPatternUtils on a looper thread"
1474 + " to use progress callbacks.");
1476 return new ICheckCredentialProgressCallback.Stub() {
1479 public void onCredentialVerified() throws RemoteException {
1480 mHandler.post(callback::onEarlyMatched);
1487 * Callback to be notified about progress when checking credentials.
1489 public interface CheckCredentialProgressCallback {
1492 * Called as soon as possible when we know that the credentials match but the user hasn't
1493 * been fully unlocked.
1495 void onEarlyMatched();
1499 * Tracks the global strong authentication state.
1501 public static class StrongAuthTracker {
1503 @IntDef(flag = true,
1504 value = { STRONG_AUTH_NOT_REQUIRED,
1505 STRONG_AUTH_REQUIRED_AFTER_BOOT,
1506 STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
1507 SOME_AUTH_REQUIRED_AFTER_USER_REQUEST,
1508 STRONG_AUTH_REQUIRED_AFTER_LOCKOUT})
1509 @Retention(RetentionPolicy.SOURCE)
1510 public @interface StrongAuthFlags {}
1513 * Strong authentication is not required.
1515 public static final int STRONG_AUTH_NOT_REQUIRED = 0x0;
1518 * Strong authentication is required because the user has not authenticated since boot.
1520 public static final int STRONG_AUTH_REQUIRED_AFTER_BOOT = 0x1;
1523 * Strong authentication is required because a device admin has requested it.
1525 public static final int STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW = 0x2;
1528 * Some authentication is required because the user has temporarily disabled trust.
1530 public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4;
1533 * Strong authentication is required because the user has been locked out after too many
1536 public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
1539 * Strong auth flags that do not prevent fingerprint from being accepted as auth.
1541 * If any other flags are set, fingerprint is disabled.
1543 private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
1544 | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
1546 private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
1547 private final H mHandler;
1548 private final int mDefaultStrongAuthFlags;
1550 public StrongAuthTracker(Context context) {
1551 this(context, Looper.myLooper());
1555 * @param looper the looper on whose thread calls to {@link #onStrongAuthRequiredChanged}
1556 * will be scheduled.
1557 * @param context the current {@link Context}
1559 public StrongAuthTracker(Context context, Looper looper) {
1560 mHandler = new H(looper);
1561 mDefaultStrongAuthFlags = getDefaultFlags(context);
1564 public static @StrongAuthFlags int getDefaultFlags(Context context) {
1565 boolean strongAuthRequired = context.getResources().getBoolean(
1566 com.android.internal.R.bool.config_strongAuthRequiredOnBoot);
1567 return strongAuthRequired ? STRONG_AUTH_REQUIRED_AFTER_BOOT : STRONG_AUTH_NOT_REQUIRED;
1571 * Returns {@link #STRONG_AUTH_NOT_REQUIRED} if strong authentication is not required,
1572 * otherwise returns a combination of {@link StrongAuthFlags} indicating why strong
1573 * authentication is required.
1575 * @param userId the user for whom the state is queried.
1577 public @StrongAuthFlags int getStrongAuthForUser(int userId) {
1578 return mStrongAuthRequiredForUser.get(userId, mDefaultStrongAuthFlags);
1582 * @return true if unlocking with trust alone is allowed for {@param userId} by the current
1583 * strong authentication requirements.
1585 public boolean isTrustAllowedForUser(int userId) {
1586 return getStrongAuthForUser(userId) == STRONG_AUTH_NOT_REQUIRED;
1590 * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
1591 * current strong authentication requirements.
1593 public boolean isFingerprintAllowedForUser(int userId) {
1594 return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
1598 * Called when the strong authentication requirements for {@param userId} changed.
1600 public void onStrongAuthRequiredChanged(int userId) {
1603 protected void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
1605 int oldValue = getStrongAuthForUser(userId);
1606 if (strongAuthFlags != oldValue) {
1607 if (strongAuthFlags == mDefaultStrongAuthFlags) {
1608 mStrongAuthRequiredForUser.delete(userId);
1610 mStrongAuthRequiredForUser.put(userId, strongAuthFlags);
1612 onStrongAuthRequiredChanged(userId);
1617 protected final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() {
1619 public void onStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
1621 mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED,
1622 strongAuthFlags, userId).sendToTarget();
1626 private class H extends Handler {
1627 static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1;
1629 public H(Looper looper) {
1634 public void handleMessage(Message msg) {
1636 case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED:
1637 handleStrongAuthRequiredChanged(msg.arg1, msg.arg2);