LockPatternUtils.java revision 95bbbdd097fb9ac410ca829df4be4e90992d2b48
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 android.Manifest;
20import android.app.ActivityManagerNative;
21import android.app.admin.DevicePolicyManager;
22import android.app.trust.TrustManager;
23import android.content.ComponentName;
24import android.content.ContentResolver;
25import android.content.Context;
26import android.content.pm.PackageManager;
27import android.content.pm.UserInfo;
28import android.os.AsyncTask;
29import android.os.IBinder;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.SystemClock;
33import android.os.SystemProperties;
34import android.os.UserHandle;
35import android.os.UserManager;
36import android.os.storage.IMountService;
37import android.os.storage.StorageManager;
38import android.provider.Settings;
39import android.text.TextUtils;
40import android.util.Log;
41
42import com.google.android.collect.Lists;
43
44import java.nio.charset.StandardCharsets;
45import java.security.MessageDigest;
46import java.security.NoSuchAlgorithmException;
47import java.security.SecureRandom;
48import java.util.ArrayList;
49import java.util.Collection;
50import java.util.List;
51
52import libcore.util.HexEncoding;
53
54/**
55 * Utilities for the lock pattern and its settings.
56 */
57public class LockPatternUtils {
58
59    private static final String TAG = "LockPatternUtils";
60    private static final boolean DEBUG = false;
61
62    /**
63     * The maximum number of incorrect attempts before the user is prevented
64     * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
65     */
66    public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
67
68    /**
69     * The number of incorrect attempts before which we fall back on an alternative
70     * method of verifying the user, and resetting their lock pattern.
71     */
72    public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
73
74    /**
75     * How long the user is prevented from trying again after entering the
76     * wrong pattern too many times.
77     */
78    public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
79
80    /**
81     * The interval of the countdown for showing progress of the lockout.
82     */
83    public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
84
85
86    /**
87     * This dictates when we start telling the user that continued failed attempts will wipe
88     * their device.
89     */
90    public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5;
91
92    /**
93     * The minimum number of dots in a valid pattern.
94     */
95    public static final int MIN_LOCK_PATTERN_SIZE = 4;
96
97    /**
98     * The minimum size of a valid password.
99     */
100    public static final int MIN_LOCK_PASSWORD_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    @Deprecated
110    public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
111    public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
112    public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
113    public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
114    @Deprecated
115    public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
116    public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
117    public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
118    public final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
119    @Deprecated
120    public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
121            = "lockscreen.biometric_weak_fallback";
122    @Deprecated
123    public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
124            = "lockscreen.biometricweakeverchosen";
125    public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
126            = "lockscreen.power_button_instantly_locks";
127    @Deprecated
128    public final static String LOCKSCREEN_WIDGETS_ENABLED = "lockscreen.widgets_enabled";
129
130    public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
131
132    private static final String LOCK_SCREEN_OWNER_INFO = Settings.Secure.LOCK_SCREEN_OWNER_INFO;
133    private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
134            Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
135
136    private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
137
138    // Maximum allowed number of repeated or ordered characters in a sequence before we'll
139    // consider it a complex PIN/password.
140    public static final int MAX_ALLOWED_SEQUENCE = 3;
141
142    private final Context mContext;
143    private final ContentResolver mContentResolver;
144    private DevicePolicyManager mDevicePolicyManager;
145    private ILockSettings mLockSettingsService;
146
147    private final boolean mMultiUserMode;
148
149    // The current user is set by KeyguardViewMediator and shared by all LockPatternUtils.
150    private static volatile int sCurrentUserId = UserHandle.USER_NULL;
151
152    public DevicePolicyManager getDevicePolicyManager() {
153        if (mDevicePolicyManager == null) {
154            mDevicePolicyManager =
155                (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
156            if (mDevicePolicyManager == null) {
157                Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
158                        new IllegalStateException("Stack trace:"));
159            }
160        }
161        return mDevicePolicyManager;
162    }
163
164    private TrustManager getTrustManager() {
165        TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
166        if (trust == null) {
167            Log.e(TAG, "Can't get TrustManagerService: is it running?",
168                    new IllegalStateException("Stack trace:"));
169        }
170        return trust;
171    }
172
173    public LockPatternUtils(Context context) {
174        mContext = context;
175        mContentResolver = context.getContentResolver();
176
177        // If this is being called by the system or by an application like keyguard that
178        // has permision INTERACT_ACROSS_USERS, then LockPatternUtils will operate in multi-user
179        // mode where calls are for the current user rather than the user of the calling process.
180        mMultiUserMode = context.checkCallingOrSelfPermission(
181            Manifest.permission.INTERACT_ACROSS_USERS_FULL) == PackageManager.PERMISSION_GRANTED;
182    }
183
184    private ILockSettings getLockSettings() {
185        if (mLockSettingsService == null) {
186            ILockSettings service = ILockSettings.Stub.asInterface(
187                    ServiceManager.getService("lock_settings"));
188            mLockSettingsService = service;
189        }
190        return mLockSettingsService;
191    }
192
193    public int getRequestedMinimumPasswordLength() {
194        return getDevicePolicyManager().getPasswordMinimumLength(null, getCurrentOrCallingUserId());
195    }
196
197    /**
198     * Gets the device policy password mode. If the mode is non-specific, returns
199     * MODE_PATTERN which allows the user to choose anything.
200     */
201    public int getRequestedPasswordQuality() {
202        return getDevicePolicyManager().getPasswordQuality(null, getCurrentOrCallingUserId());
203    }
204
205    public int getRequestedPasswordHistoryLength() {
206        return getRequestedPasswordHistoryLength(getCurrentOrCallingUserId());
207    }
208
209    private int getRequestedPasswordHistoryLength(int userId) {
210        return getDevicePolicyManager().getPasswordHistoryLength(null, userId);
211    }
212
213    public int getRequestedPasswordMinimumLetters() {
214        return getDevicePolicyManager().getPasswordMinimumLetters(null,
215                getCurrentOrCallingUserId());
216    }
217
218    public int getRequestedPasswordMinimumUpperCase() {
219        return getDevicePolicyManager().getPasswordMinimumUpperCase(null,
220                getCurrentOrCallingUserId());
221    }
222
223    public int getRequestedPasswordMinimumLowerCase() {
224        return getDevicePolicyManager().getPasswordMinimumLowerCase(null,
225                getCurrentOrCallingUserId());
226    }
227
228    public int getRequestedPasswordMinimumNumeric() {
229        return getDevicePolicyManager().getPasswordMinimumNumeric(null,
230                getCurrentOrCallingUserId());
231    }
232
233    public int getRequestedPasswordMinimumSymbols() {
234        return getDevicePolicyManager().getPasswordMinimumSymbols(null,
235                getCurrentOrCallingUserId());
236    }
237
238    public int getRequestedPasswordMinimumNonLetter() {
239        return getDevicePolicyManager().getPasswordMinimumNonLetter(null,
240                getCurrentOrCallingUserId());
241    }
242
243    public void reportFailedPasswordAttempt() {
244        int userId = getCurrentOrCallingUserId();
245        getDevicePolicyManager().reportFailedPasswordAttempt(userId);
246        getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
247        getTrustManager().reportRequireCredentialEntry(userId);
248    }
249
250    public void reportSuccessfulPasswordAttempt() {
251        getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId());
252        getTrustManager().reportUnlockAttempt(true /* authenticated */,
253                getCurrentOrCallingUserId());
254    }
255
256    public void setCurrentUser(int userId) {
257        sCurrentUserId = userId;
258    }
259
260    public int getCurrentUser() {
261        if (sCurrentUserId != UserHandle.USER_NULL) {
262            // Someone is regularly updating using setCurrentUser() use that value.
263            return sCurrentUserId;
264        }
265        try {
266            return ActivityManagerNative.getDefault().getCurrentUser().id;
267        } catch (RemoteException re) {
268            return UserHandle.USER_OWNER;
269        }
270    }
271
272    private int getCurrentOrCallingUserId() {
273        if (mMultiUserMode) {
274            // TODO: This is a little inefficient. See if all users of this are able to
275            // handle USER_CURRENT and pass that instead.
276            return getCurrentUser();
277        } else {
278            return UserHandle.getCallingUserId();
279        }
280    }
281
282    /**
283     * Check to see if a pattern matches the saved pattern.
284     * If pattern matches, return an opaque attestation that the challenge
285     * was verified.
286     *
287     * @param pattern The pattern to check.
288     * @param challenge The challenge to verify against the pattern
289     * @return the attestation that the challenge was verified, or null.
290     */
291    public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge) {
292        final int userId = getCurrentOrCallingUserId();
293        try {
294            return getLockSettings().verifyPattern(patternToString(pattern), challenge, userId);
295        } catch (RemoteException re) {
296            return null;
297        }
298    }
299
300    /**
301     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
302     * always returns true.
303     * @param pattern The pattern to check.
304     * @return Whether the pattern matches the stored one.
305     */
306    public boolean checkPattern(List<LockPatternView.Cell> pattern) {
307        final int userId = getCurrentOrCallingUserId();
308        try {
309            return getLockSettings().checkPattern(patternToString(pattern), userId);
310        } catch (RemoteException re) {
311            return true;
312        }
313    }
314
315    /**
316     * Check to see if a password matches the saved password.
317     * If password matches, return an opaque attestation that the challenge
318     * was verified.
319     *
320     * @param password The password to check.
321     * @param challenge The challenge to verify against the password
322     * @return the attestation that the challenge was verified, or null.
323     */
324    public byte[] verifyPassword(String password, long challenge) {
325        final int userId = getCurrentOrCallingUserId();
326        try {
327            return getLockSettings().verifyPassword(password, challenge, userId);
328        } catch (RemoteException re) {
329            return null;
330        }
331    }
332
333    /**
334     * Check to see if a password matches the saved password.  If no password exists,
335     * always returns true.
336     * @param password The password to check.
337     * @return Whether the password matches the stored one.
338     */
339    public boolean checkPassword(String password) {
340        final int userId = getCurrentOrCallingUserId();
341        try {
342            return getLockSettings().checkPassword(password, userId);
343        } catch (RemoteException re) {
344            return true;
345        }
346    }
347
348    /**
349     * Check to see if vold already has the password.
350     * Note that this also clears vold's copy of the password.
351     * @return Whether the vold password matches or not.
352     */
353    public boolean checkVoldPassword() {
354        final int userId = getCurrentOrCallingUserId();
355        try {
356            return getLockSettings().checkVoldPassword(userId);
357        } catch (RemoteException re) {
358            return false;
359        }
360    }
361
362    /**
363     * Check to see if a password matches any of the passwords stored in the
364     * password history.
365     *
366     * @param password The password to check.
367     * @return Whether the password matches any in the history.
368     */
369    public boolean checkPasswordHistory(String password) {
370        int userId = getCurrentOrCallingUserId();
371        String passwordHashString = new String(
372                passwordToHash(password, userId), StandardCharsets.UTF_8);
373        String passwordHistory = getString(PASSWORD_HISTORY_KEY, userId);
374        if (passwordHistory == null) {
375            return false;
376        }
377        // Password History may be too long...
378        int passwordHashLength = passwordHashString.length();
379        int passwordHistoryLength = getRequestedPasswordHistoryLength();
380        if(passwordHistoryLength == 0) {
381            return false;
382        }
383        int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
384                + passwordHistoryLength - 1;
385        if (passwordHistory.length() > neededPasswordHistoryLength) {
386            passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
387        }
388        return passwordHistory.contains(passwordHashString);
389    }
390
391    /**
392     * Check to see if the user has stored a lock pattern.
393     * @return Whether a saved pattern exists.
394     */
395    private boolean savedPatternExists(int userId) {
396        try {
397            return getLockSettings().havePattern(userId);
398        } catch (RemoteException re) {
399            return false;
400        }
401    }
402
403    /**
404     * Check to see if the user has stored a lock pattern.
405     * @return Whether a saved pattern exists.
406     */
407    private boolean savedPasswordExists(int userId) {
408        try {
409            return getLockSettings().havePassword(userId);
410        } catch (RemoteException re) {
411            return false;
412        }
413    }
414
415    /**
416     * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
417     * currently cleared.
418     *
419     * @return True if the user has ever chosen a pattern.
420     */
421    public boolean isPatternEverChosen() {
422        return getBoolean(PATTERN_EVER_CHOSEN_KEY, false, getCurrentOrCallingUserId());
423    }
424
425    /**
426     * Used by device policy manager to validate the current password
427     * information it has.
428     */
429    public int getActivePasswordQuality() {
430        return getActivePasswordQuality(getCurrentOrCallingUserId());
431    }
432
433    /**
434     * Used by device policy manager to validate the current password
435     * information it has.
436     */
437    public int getActivePasswordQuality(int userId) {
438        int quality = getKeyguardStoredPasswordQuality(userId);
439
440        if (isLockPasswordEnabled(quality, userId)) {
441            // Quality is a password and a password exists. Return the quality.
442            return quality;
443        }
444
445        if (isLockPatternEnabled(quality, userId)) {
446            // Quality is a pattern and a pattern exists. Return the quality.
447            return quality;
448        }
449
450        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
451    }
452
453    public void clearLock() {
454        clearLock(getCurrentOrCallingUserId());
455    }
456
457    /**
458     * Clear any lock pattern or password.
459     */
460    public void clearLock(int userHandle) {
461        setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
462
463        try {
464            getLockSettings().setLockPassword(null, null, userHandle);
465            getLockSettings().setLockPattern(null, null, userHandle);
466        } catch (RemoteException e) {
467            // well, we tried...
468        }
469
470        if (userHandle == UserHandle.USER_OWNER) {
471            // Set the encryption password to default.
472            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
473        }
474
475        setCredentialRequiredToDecrypt(false);
476
477        getDevicePolicyManager().setActivePasswordState(
478                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
479
480        onAfterChangingPassword(userHandle);
481    }
482
483    /**
484     * Disable showing lock screen at all when the DevicePolicyManager allows it.
485     * This is only meaningful if pattern, pin or password are not set.
486     *
487     * @param disable Disables lock screen when true
488     */
489    public void setLockScreenDisabled(boolean disable) {
490        setLockScreenDisabled(disable, getCurrentOrCallingUserId());
491    }
492
493    /**
494     * Disable showing lock screen at all for a given user.
495     * This is only meaningful if pattern, pin or password are not set.
496     *
497     * @param disable Disables lock screen when true
498     * @param userId User ID of the user this has effect on
499     */
500    public void setLockScreenDisabled(boolean disable, int userId) {
501        setBoolean(DISABLE_LOCKSCREEN_KEY, disable, userId);
502    }
503
504    /**
505     * Determine if LockScreen is disabled for the current user. This is used to decide whether
506     * LockScreen is shown after reboot or after screen timeout / short press on power.
507     *
508     * @return true if lock screen is disabled
509     */
510    public boolean isLockScreenDisabled() {
511        return !isSecure() &&
512                getBoolean(DISABLE_LOCKSCREEN_KEY, false, getCurrentOrCallingUserId());
513    }
514
515    /**
516     * Save a lock pattern.
517     * @param pattern The new pattern to save.
518     * @param savedPattern The previously saved pattern, or null if none
519     */
520    public void saveLockPattern(List<LockPatternView.Cell> pattern,
521            String savedPattern) {
522        this.saveLockPattern(pattern, savedPattern, getCurrentOrCallingUserId());
523    }
524
525    public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) {
526        this.saveLockPattern(pattern, null, userId);
527    }
528    /**
529     * Save a lock pattern.
530     * @param pattern The new pattern to save.
531     * @param savedPattern The previously saved pattern, converted to String format
532     * @param userId the user whose pattern is to be saved.
533     */
534    public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
535        try {
536            if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
537                throw new IllegalArgumentException("pattern must not be null and at least "
538                        + MIN_LOCK_PATTERN_SIZE + " dots long.");
539            }
540
541            getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
542            DevicePolicyManager dpm = getDevicePolicyManager();
543
544            // Update the device encryption password.
545            if (userId == UserHandle.USER_OWNER
546                    && LockPatternUtils.isDeviceEncryptionEnabled()) {
547                final boolean required = isCredentialRequiredToDecrypt(true);
548                if (!required) {
549                    clearEncryptionPassword();
550                } else {
551                    String stringPattern = patternToString(pattern);
552                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
553                }
554            }
555
556            setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
557
558            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
559            dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
560                    pattern.size(), 0, 0, 0, 0, 0, 0, userId);
561            onAfterChangingPassword(userId);
562        } catch (RemoteException re) {
563            Log.e(TAG, "Couldn't save lock pattern " + re);
564        }
565    }
566
567    private void updateCryptoUserInfo(int userId) {
568        if (userId != UserHandle.USER_OWNER) {
569            return;
570        }
571
572        final String ownerInfo = isOwnerInfoEnabled(userId) ? getOwnerInfo(userId) : "";
573
574        IBinder service = ServiceManager.getService("mount");
575        if (service == null) {
576            Log.e(TAG, "Could not find the mount service to update the user info");
577            return;
578        }
579
580        IMountService mountService = IMountService.Stub.asInterface(service);
581        try {
582            Log.d(TAG, "Setting owner info");
583            mountService.setField(StorageManager.OWNER_INFO_KEY, ownerInfo);
584        } catch (RemoteException e) {
585            Log.e(TAG, "Error changing user info", e);
586        }
587    }
588
589    public void setOwnerInfo(String info, int userId) {
590        setString(LOCK_SCREEN_OWNER_INFO, info, userId);
591        updateCryptoUserInfo(userId);
592    }
593
594    public void setOwnerInfoEnabled(boolean enabled) {
595        int userId = getCurrentOrCallingUserId();
596        setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled, userId);
597        updateCryptoUserInfo(userId);
598    }
599
600    public String getOwnerInfo(int userId) {
601        return getString(LOCK_SCREEN_OWNER_INFO, userId);
602    }
603
604    public boolean isOwnerInfoEnabled() {
605        return isOwnerInfoEnabled(getCurrentOrCallingUserId());
606    }
607
608    private boolean isOwnerInfoEnabled(int userId) {
609        return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false, userId);
610    }
611
612    /**
613     * Compute the password quality from the given password string.
614     */
615    static public int computePasswordQuality(String password) {
616        boolean hasDigit = false;
617        boolean hasNonDigit = false;
618        final int len = password.length();
619        for (int i = 0; i < len; i++) {
620            if (Character.isDigit(password.charAt(i))) {
621                hasDigit = true;
622            } else {
623                hasNonDigit = true;
624            }
625        }
626
627        if (hasNonDigit && hasDigit) {
628            return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
629        }
630        if (hasNonDigit) {
631            return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
632        }
633        if (hasDigit) {
634            return maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
635                    ? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
636                    : DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
637        }
638        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
639    }
640
641    private static int categoryChar(char c) {
642        if ('a' <= c && c <= 'z') return 0;
643        if ('A' <= c && c <= 'Z') return 1;
644        if ('0' <= c && c <= '9') return 2;
645        return 3;
646    }
647
648    private static int maxDiffCategory(int category) {
649        if (category == 0 || category == 1) return 1;
650        else if (category == 2) return 10;
651        return 0;
652    }
653
654    /*
655     * Returns the maximum length of a sequential characters.  A sequence is defined as
656     * monotonically increasing characters with a constant interval or the same character repeated.
657     *
658     * For example:
659     * maxLengthSequence("1234") == 4
660     * maxLengthSequence("1234abc") == 4
661     * maxLengthSequence("aabc") == 3
662     * maxLengthSequence("qwertyuio") == 1
663     * maxLengthSequence("@ABC") == 3
664     * maxLengthSequence(";;;;") == 4 (anything that repeats)
665     * maxLengthSequence(":;<=>") == 1  (ordered, but not composed of alphas or digits)
666     *
667     * @param string the pass
668     * @return the number of sequential letters or digits
669     */
670    public static int maxLengthSequence(String string) {
671        if (string.length() == 0) return 0;
672        char previousChar = string.charAt(0);
673        int category = categoryChar(previousChar); //current category of the sequence
674        int diff = 0; //difference between two consecutive characters
675        boolean hasDiff = false; //if we are currently targeting a sequence
676        int maxLength = 0; //maximum length of a sequence already found
677        int startSequence = 0; //where the current sequence started
678        for (int current = 1; current < string.length(); current++) {
679            char currentChar = string.charAt(current);
680            int categoryCurrent = categoryChar(currentChar);
681            int currentDiff = (int) currentChar - (int) previousChar;
682            if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
683                maxLength = Math.max(maxLength, current - startSequence);
684                startSequence = current;
685                hasDiff = false;
686                category = categoryCurrent;
687            }
688            else {
689                if(hasDiff && currentDiff != diff) {
690                    maxLength = Math.max(maxLength, current - startSequence);
691                    startSequence = current - 1;
692                }
693                diff = currentDiff;
694                hasDiff = true;
695            }
696            previousChar = currentChar;
697        }
698        maxLength = Math.max(maxLength, string.length() - startSequence);
699        return maxLength;
700    }
701
702    /** Update the encryption password if it is enabled **/
703    private void updateEncryptionPassword(final int type, final String password) {
704        if (!isDeviceEncryptionEnabled()) {
705            return;
706        }
707        final IBinder service = ServiceManager.getService("mount");
708        if (service == null) {
709            Log.e(TAG, "Could not find the mount service to update the encryption password");
710            return;
711        }
712
713        new AsyncTask<Void, Void, Void>() {
714            @Override
715            protected Void doInBackground(Void... dummy) {
716                IMountService mountService = IMountService.Stub.asInterface(service);
717                try {
718                    mountService.changeEncryptionPassword(type, password);
719                } catch (RemoteException e) {
720                    Log.e(TAG, "Error changing encryption password", e);
721                }
722                return null;
723            }
724        }.execute();
725    }
726
727    /**
728     * Save a lock password.  Does not ensure that the password is as good
729     * as the requested mode, but will adjust the mode to be as good as the
730     * pattern.
731     * @param password The password to save
732     * @param savedPassword The previously saved lock password, or null if none
733     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
734     */
735    public void saveLockPassword(String password, String savedPassword, int quality) {
736        saveLockPassword(password, savedPassword, quality, getCurrentOrCallingUserId());
737    }
738
739    /**
740     * Save a lock password.  Does not ensure that the password is as good
741     * as the requested mode, but will adjust the mode to be as good as the
742     * pattern.
743     * @param password The password to save
744     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
745     * @param userHandle The userId of the user to change the password for
746     */
747    public void saveLockPassword(String password, String savedPassword, int quality,
748            int userHandle) {
749        try {
750            DevicePolicyManager dpm = getDevicePolicyManager();
751            if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
752                throw new IllegalArgumentException("password must not be null and at least "
753                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
754            }
755
756            getLockSettings().setLockPassword(password, savedPassword, userHandle);
757            int computedQuality = computePasswordQuality(password);
758
759            // Update the device encryption password.
760            if (userHandle == UserHandle.USER_OWNER
761                    && LockPatternUtils.isDeviceEncryptionEnabled()) {
762                if (!isCredentialRequiredToDecrypt(true)) {
763                    clearEncryptionPassword();
764                } else {
765                    boolean numeric = computedQuality
766                            == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
767                    boolean numericComplex = computedQuality
768                            == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
769                    int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
770                            : StorageManager.CRYPT_TYPE_PASSWORD;
771                    updateEncryptionPassword(type, password);
772                }
773            }
774
775            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
776            if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
777                int letters = 0;
778                int uppercase = 0;
779                int lowercase = 0;
780                int numbers = 0;
781                int symbols = 0;
782                int nonletter = 0;
783                for (int i = 0; i < password.length(); i++) {
784                    char c = password.charAt(i);
785                    if (c >= 'A' && c <= 'Z') {
786                        letters++;
787                        uppercase++;
788                    } else if (c >= 'a' && c <= 'z') {
789                        letters++;
790                        lowercase++;
791                    } else if (c >= '0' && c <= '9') {
792                        numbers++;
793                        nonletter++;
794                    } else {
795                        symbols++;
796                        nonletter++;
797                    }
798                }
799                dpm.setActivePasswordState(Math.max(quality, computedQuality),
800                        password.length(), letters, uppercase, lowercase,
801                        numbers, symbols, nonletter, userHandle);
802            } else {
803                // The password is not anything.
804                dpm.setActivePasswordState(
805                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
806                        0, 0, 0, 0, 0, 0, 0, userHandle);
807            }
808
809            // Add the password to the password history. We assume all
810            // password hashes have the same length for simplicity of implementation.
811            String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
812            if (passwordHistory == null) {
813                passwordHistory = "";
814            }
815            int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
816            if (passwordHistoryLength == 0) {
817                passwordHistory = "";
818            } else {
819                byte[] hash = passwordToHash(password, userHandle);
820                passwordHistory = new String(hash, StandardCharsets.UTF_8) + "," + passwordHistory;
821                // Cut it to contain passwordHistoryLength hashes
822                // and passwordHistoryLength -1 commas.
823                passwordHistory = passwordHistory.substring(0, Math.min(hash.length
824                        * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
825                        .length()));
826            }
827            setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
828            onAfterChangingPassword(userHandle);
829        } catch (RemoteException re) {
830            // Cant do much
831            Log.e(TAG, "Unable to save lock password " + re);
832        }
833    }
834
835    /**
836     * Gets whether the device is encrypted.
837     *
838     * @return Whether the device is encrypted.
839     */
840    public static boolean isDeviceEncrypted() {
841        IMountService mountService = IMountService.Stub.asInterface(
842                ServiceManager.getService("mount"));
843        try {
844            return mountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE
845                    && mountService.getPasswordType() != StorageManager.CRYPT_TYPE_DEFAULT;
846        } catch (RemoteException re) {
847            Log.e(TAG, "Error getting encryption state", re);
848        }
849        return true;
850    }
851
852    /**
853     * Determine if the device supports encryption, even if it's set to default. This
854     * differs from isDeviceEncrypted() in that it returns true even if the device is
855     * encrypted with the default password.
856     * @return true if device encryption is enabled
857     */
858    public static boolean isDeviceEncryptionEnabled() {
859        final String status = SystemProperties.get("ro.crypto.state", "unsupported");
860        return "encrypted".equalsIgnoreCase(status);
861    }
862
863    /**
864     * Clears the encryption password.
865     */
866    public void clearEncryptionPassword() {
867        updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
868    }
869
870    /**
871     * Retrieves the quality mode we're in.
872     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
873     *
874     * @return stored password quality
875     */
876    public int getKeyguardStoredPasswordQuality() {
877        return getKeyguardStoredPasswordQuality(getCurrentOrCallingUserId());
878    }
879
880    /**
881     * Retrieves the quality mode for {@param userHandle}.
882     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
883     *
884     * @return stored password quality
885     */
886    public int getKeyguardStoredPasswordQuality(int userHandle) {
887        return (int) getLong(PASSWORD_TYPE_KEY,
888                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
889    }
890
891    /**
892     * Deserialize a pattern.
893     * @param string The pattern serialized with {@link #patternToString}
894     * @return The pattern.
895     */
896    public static List<LockPatternView.Cell> stringToPattern(String string) {
897        if (string == null) {
898            return null;
899        }
900
901        List<LockPatternView.Cell> result = Lists.newArrayList();
902
903        final byte[] bytes = string.getBytes();
904        for (int i = 0; i < bytes.length; i++) {
905            byte b = bytes[i];
906            result.add(LockPatternView.Cell.of(b / 3, b % 3));
907        }
908        return result;
909    }
910
911    /**
912     * Serialize a pattern.
913     * @param pattern The pattern.
914     * @return The pattern in string form.
915     */
916    public static String patternToString(List<LockPatternView.Cell> pattern) {
917        if (pattern == null) {
918            return "";
919        }
920        final int patternSize = pattern.size();
921
922        byte[] res = new byte[patternSize];
923        for (int i = 0; i < patternSize; i++) {
924            LockPatternView.Cell cell = pattern.get(i);
925            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
926        }
927        return new String(res);
928    }
929
930    /*
931     * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
932     * at least a second level of protection. First level is that the file
933     * is in a location only readable by the system process.
934     * @param pattern the gesture pattern.
935     * @return the hash of the pattern in a byte array.
936     */
937    public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
938        if (pattern == null) {
939            return null;
940        }
941
942        final int patternSize = pattern.size();
943        byte[] res = new byte[patternSize];
944        for (int i = 0; i < patternSize; i++) {
945            LockPatternView.Cell cell = pattern.get(i);
946            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
947        }
948        try {
949            MessageDigest md = MessageDigest.getInstance("SHA-1");
950            byte[] hash = md.digest(res);
951            return hash;
952        } catch (NoSuchAlgorithmException nsa) {
953            return res;
954        }
955    }
956
957    private String getSalt(int userId) {
958        long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
959        if (salt == 0) {
960            try {
961                salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
962                setLong(LOCK_PASSWORD_SALT_KEY, salt, userId);
963                Log.v(TAG, "Initialized lock password salt for user: " + userId);
964            } catch (NoSuchAlgorithmException e) {
965                // Throw an exception rather than storing a password we'll never be able to recover
966                throw new IllegalStateException("Couldn't get SecureRandom number", e);
967            }
968        }
969        return Long.toHexString(salt);
970    }
971
972    /*
973     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
974     * Not the most secure, but it is at least a second level of protection. First level is that
975     * the file is in a location only readable by the system process.
976     *
977     * @param password the gesture pattern.
978     *
979     * @return the hash of the pattern in a byte array.
980     */
981    public byte[] passwordToHash(String password, int userId) {
982        if (password == null) {
983            return null;
984        }
985
986        try {
987            byte[] saltedPassword = (password + getSalt(userId)).getBytes();
988            byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
989            byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
990
991            byte[] combined = new byte[sha1.length + md5.length];
992            System.arraycopy(sha1, 0, combined, 0, sha1.length);
993            System.arraycopy(md5, 0, combined, sha1.length, md5.length);
994
995            final char[] hexEncoded = HexEncoding.encode(combined);
996            return new String(hexEncoded).getBytes(StandardCharsets.UTF_8);
997        } catch (NoSuchAlgorithmException e) {
998            throw new AssertionError("Missing digest algorithm: ", e);
999        }
1000    }
1001
1002    /**
1003     * @return Whether the lock screen is secured.
1004     */
1005    public boolean isSecure() {
1006        return isSecure(getCurrentOrCallingUserId());
1007    }
1008
1009    /**
1010     * @param userId the user for which to report the value
1011     * @return Whether the lock screen is secured.
1012     */
1013    public boolean isSecure(int userId) {
1014        int mode = getKeyguardStoredPasswordQuality(userId);
1015        return isLockPatternEnabled(mode, userId) || isLockPasswordEnabled(mode, userId);
1016    }
1017
1018    /**
1019     * @return Whether the lock password is enabled
1020     */
1021    public boolean isLockPasswordEnabled() {
1022        return isLockPasswordEnabled(getCurrentOrCallingUserId());
1023    }
1024
1025    public boolean isLockPasswordEnabled(int userId) {
1026        return isLockPasswordEnabled(getKeyguardStoredPasswordQuality(userId), userId);
1027    }
1028
1029    private boolean isLockPasswordEnabled(int mode, int userId) {
1030        final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1031                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1032                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1033                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1034                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1035        return passwordEnabled && savedPasswordExists(userId);
1036    }
1037
1038    /**
1039     * @return Whether the lock pattern is enabled
1040     */
1041    public boolean isLockPatternEnabled() {
1042        return isLockPatternEnabled(getCurrentOrCallingUserId());
1043    }
1044
1045    public boolean isLockPatternEnabled(int userId) {
1046        return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId);
1047    }
1048
1049    private boolean isLockPatternEnabled(int mode, int userId) {
1050        return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
1051                && savedPatternExists(userId);
1052    }
1053
1054    /**
1055     * @return Whether the visible pattern is enabled.
1056     */
1057    public boolean isVisiblePatternEnabled() {
1058        return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false, getCurrentOrCallingUserId());
1059    }
1060
1061    /**
1062     * Set whether the visible pattern is enabled.
1063     */
1064    public void setVisiblePatternEnabled(boolean enabled) {
1065        int userId = getCurrentOrCallingUserId();
1066
1067        setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled, userId);
1068
1069        // Update for crypto if owner
1070        if (userId != UserHandle.USER_OWNER) {
1071            return;
1072        }
1073
1074        IBinder service = ServiceManager.getService("mount");
1075        if (service == null) {
1076            Log.e(TAG, "Could not find the mount service to update the user info");
1077            return;
1078        }
1079
1080        IMountService mountService = IMountService.Stub.asInterface(service);
1081        try {
1082            mountService.setField(StorageManager.PATTERN_VISIBLE_KEY, enabled ? "1" : "0");
1083        } catch (RemoteException e) {
1084            Log.e(TAG, "Error changing pattern visible state", e);
1085        }
1086    }
1087
1088    /**
1089     * @return Whether tactile feedback for the pattern is enabled.
1090     */
1091    public boolean isTactileFeedbackEnabled() {
1092        return Settings.System.getIntForUser(mContentResolver,
1093                Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
1094    }
1095
1096    /**
1097     * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
1098     * pattern until the deadline has passed.
1099     * @return the chosen deadline.
1100     */
1101    public long setLockoutAttemptDeadline() {
1102        final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
1103        setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, getCurrentOrCallingUserId());
1104        return deadline;
1105    }
1106
1107    /**
1108     * @return The elapsed time in millis in the future when the user is allowed to
1109     *   attempt to enter his/her lock pattern, or 0 if the user is welcome to
1110     *   enter a pattern.
1111     */
1112    public long getLockoutAttemptDeadline() {
1113        final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, getCurrentOrCallingUserId());
1114        final long now = SystemClock.elapsedRealtime();
1115        if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
1116            return 0L;
1117        }
1118        return deadline;
1119    }
1120
1121    private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
1122        try {
1123            return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
1124        } catch (RemoteException re) {
1125            return defaultValue;
1126        }
1127    }
1128
1129    private void setBoolean(String secureSettingKey, boolean enabled, int userId) {
1130        try {
1131            getLockSettings().setBoolean(secureSettingKey, enabled, userId);
1132        } catch (RemoteException re) {
1133            // What can we do?
1134            Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re);
1135        }
1136    }
1137
1138    private long getLong(String secureSettingKey, long defaultValue, int userHandle) {
1139        try {
1140            return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
1141        } catch (RemoteException re) {
1142            return defaultValue;
1143        }
1144    }
1145
1146    private void setLong(String secureSettingKey, long value, int userHandle) {
1147        try {
1148            getLockSettings().setLong(secureSettingKey, value, userHandle);
1149        } catch (RemoteException re) {
1150            // What can we do?
1151            Log.e(TAG, "Couldn't write long " + secureSettingKey + re);
1152        }
1153    }
1154
1155    private String getString(String secureSettingKey, int userHandle) {
1156        try {
1157            return getLockSettings().getString(secureSettingKey, null, userHandle);
1158        } catch (RemoteException re) {
1159            return null;
1160        }
1161    }
1162
1163    private void setString(String secureSettingKey, String value, int userHandle) {
1164        try {
1165            getLockSettings().setString(secureSettingKey, value, userHandle);
1166        } catch (RemoteException re) {
1167            // What can we do?
1168            Log.e(TAG, "Couldn't write string " + secureSettingKey + re);
1169        }
1170    }
1171
1172    public void setPowerButtonInstantlyLocks(boolean enabled) {
1173        setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled, getCurrentOrCallingUserId());
1174    }
1175
1176    public boolean getPowerButtonInstantlyLocks() {
1177        return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true,
1178                getCurrentOrCallingUserId());
1179    }
1180
1181    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) {
1182        setEnabledTrustAgents(activeTrustAgents, getCurrentOrCallingUserId());
1183    }
1184
1185    public List<ComponentName> getEnabledTrustAgents() {
1186        return getEnabledTrustAgents(getCurrentOrCallingUserId());
1187    }
1188
1189    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
1190        StringBuilder sb = new StringBuilder();
1191        for (ComponentName cn : activeTrustAgents) {
1192            if (sb.length() > 0) {
1193                sb.append(',');
1194            }
1195            sb.append(cn.flattenToShortString());
1196        }
1197        setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
1198        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
1199    }
1200
1201    public List<ComponentName> getEnabledTrustAgents(int userId) {
1202        String serialized = getString(ENABLED_TRUST_AGENTS, userId);
1203        if (TextUtils.isEmpty(serialized)) {
1204            return null;
1205        }
1206        String[] split = serialized.split(",");
1207        ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
1208        for (String s : split) {
1209            if (!TextUtils.isEmpty(s)) {
1210                activeTrustAgents.add(ComponentName.unflattenFromString(s));
1211            }
1212        }
1213        return activeTrustAgents;
1214    }
1215
1216    /**
1217     * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int)
1218     */
1219    public void requireCredentialEntry(int userId) {
1220        getTrustManager().reportRequireCredentialEntry(userId);
1221    }
1222
1223    private void onAfterChangingPassword(int userHandle) {
1224        getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
1225    }
1226
1227    public boolean isCredentialRequiredToDecrypt(boolean defaultValue) {
1228        final int value = Settings.Global.getInt(mContentResolver,
1229                Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, -1);
1230        return value == -1 ? defaultValue : (value != 0);
1231    }
1232
1233    public void setCredentialRequiredToDecrypt(boolean required) {
1234        if (getCurrentUser() != UserHandle.USER_OWNER) {
1235            Log.w(TAG, "Only device owner may call setCredentialRequiredForDecrypt()");
1236            return;
1237        }
1238        Settings.Global.putInt(mContext.getContentResolver(),
1239                Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
1240    }
1241}
1242