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