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