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