LockPatternUtils.java revision 16e4a1aade2b73edfdaa42aa86a3893fd039fc62
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.widget;
18
19import android.Manifest;
20import android.app.ActivityManagerNative;
21import android.app.AlarmManager;
22import android.app.admin.DevicePolicyManager;
23import android.app.trust.TrustManager;
24import android.appwidget.AppWidgetManager;
25import android.content.ComponentName;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.pm.PackageManager;
30import android.content.pm.UserInfo;
31import android.os.AsyncTask;
32import android.os.IBinder;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.SystemClock;
36import android.os.UserHandle;
37import android.os.UserManager;
38import android.os.storage.IMountService;
39import android.os.storage.StorageManager;
40import android.provider.Settings;
41import android.telecom.TelecomManager;
42import android.text.TextUtils;
43import android.util.Log;
44import android.view.IWindowManager;
45import android.view.View;
46import android.widget.Button;
47
48import com.android.internal.R;
49import com.google.android.collect.Lists;
50
51import java.security.MessageDigest;
52import java.security.NoSuchAlgorithmException;
53import java.security.SecureRandom;
54import java.util.ArrayList;
55import java.util.Collection;
56import java.util.List;
57
58/**
59 * Utilities for the lock pattern and its settings.
60 */
61public class LockPatternUtils {
62
63    private static final String TAG = "LockPatternUtils";
64    private static final boolean DEBUG = false;
65
66    /**
67     * The maximum number of incorrect attempts before the user is prevented
68     * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
69     */
70    public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
71
72    /**
73     * The number of incorrect attempts before which we fall back on an alternative
74     * method of verifying the user, and resetting their lock pattern.
75     */
76    public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
77
78    /**
79     * How long the user is prevented from trying again after entering the
80     * wrong pattern too many times.
81     */
82    public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
83
84    /**
85     * The interval of the countdown for showing progress of the lockout.
86     */
87    public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
88
89
90    /**
91     * This dictates when we start telling the user that continued failed attempts will wipe
92     * their device.
93     */
94    public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5;
95
96    /**
97     * The minimum number of dots in a valid pattern.
98     */
99    public static final int MIN_LOCK_PATTERN_SIZE = 4;
100
101    /**
102     * The minimum number of dots the user must include in a wrong pattern
103     * attempt for it to be counted against the counts that affect
104     * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
105     */
106    public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
107
108    /**
109     * Tells the keyguard to show the user switcher when the keyguard is created.
110     */
111    public static final String KEYGUARD_SHOW_USER_SWITCHER = "showuserswitcher";
112
113    /**
114     * Tells the keyguard to show the security challenge when the keyguard is created.
115     */
116    public static final String KEYGUARD_SHOW_SECURITY_CHALLENGE = "showsecuritychallenge";
117
118    /**
119     * Tells the keyguard to show the widget with the specified id when the keyguard is created.
120     */
121    public static final String KEYGUARD_SHOW_APPWIDGET = "showappwidget";
122
123    /**
124     * The bit in LOCK_BIOMETRIC_WEAK_FLAGS to be used to indicate whether liveliness should
125     * be used
126     */
127    public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
128
129    /**
130     * Pseudo-appwidget id we use to represent the default clock status widget
131     */
132    public static final int ID_DEFAULT_STATUS_WIDGET = -2;
133
134    public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
135    public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
136    public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
137    public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
138    public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
139    public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
140    public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
141    public final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
142    public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
143            = "lockscreen.biometric_weak_fallback";
144    public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
145            = "lockscreen.biometricweakeverchosen";
146    public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
147            = "lockscreen.power_button_instantly_locks";
148    public final static String LOCKSCREEN_WIDGETS_ENABLED = "lockscreen.widgets_enabled";
149
150    public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
151
152    private static final String LOCK_SCREEN_OWNER_INFO = Settings.Secure.LOCK_SCREEN_OWNER_INFO;
153    private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
154            Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
155
156    private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
157
158    // Maximum allowed number of repeated or ordered characters in a sequence before we'll
159    // consider it a complex PIN/password.
160    public static final int MAX_ALLOWED_SEQUENCE = 3;
161
162    private final Context mContext;
163    private final ContentResolver mContentResolver;
164    private DevicePolicyManager mDevicePolicyManager;
165    private ILockSettings mLockSettingsService;
166
167    private final boolean mMultiUserMode;
168
169    // The current user is set by KeyguardViewMediator and shared by all LockPatternUtils.
170    private static volatile int sCurrentUserId = UserHandle.USER_NULL;
171
172    public DevicePolicyManager getDevicePolicyManager() {
173        if (mDevicePolicyManager == null) {
174            mDevicePolicyManager =
175                (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
176            if (mDevicePolicyManager == null) {
177                Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
178                        new IllegalStateException("Stack trace:"));
179            }
180        }
181        return mDevicePolicyManager;
182    }
183
184    private TrustManager getTrustManager() {
185        TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
186        if (trust == null) {
187            Log.e(TAG, "Can't get TrustManagerService: is it running?",
188                    new IllegalStateException("Stack trace:"));
189        }
190        return trust;
191    }
192
193    /**
194     * @param contentResolver Used to look up and save settings.
195     */
196    public LockPatternUtils(Context context) {
197        mContext = context;
198        mContentResolver = context.getContentResolver();
199
200        // If this is being called by the system or by an application like keyguard that
201        // has permision INTERACT_ACROSS_USERS, then LockPatternUtils will operate in multi-user
202        // mode where calls are for the current user rather than the user of the calling process.
203        mMultiUserMode = context.checkCallingOrSelfPermission(
204            Manifest.permission.INTERACT_ACROSS_USERS_FULL) == PackageManager.PERMISSION_GRANTED;
205    }
206
207    private ILockSettings getLockSettings() {
208        if (mLockSettingsService == null) {
209            mLockSettingsService = LockPatternUtilsCache.getInstance(
210                    ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")));
211        }
212        return mLockSettingsService;
213    }
214
215    public int getRequestedMinimumPasswordLength() {
216        return getDevicePolicyManager().getPasswordMinimumLength(null, getCurrentOrCallingUserId());
217    }
218
219    /**
220     * Gets the device policy password mode. If the mode is non-specific, returns
221     * MODE_PATTERN which allows the user to choose anything.
222     */
223    public int getRequestedPasswordQuality() {
224        return getDevicePolicyManager().getPasswordQuality(null, getCurrentOrCallingUserId());
225    }
226
227    public int getRequestedPasswordHistoryLength() {
228        return getDevicePolicyManager().getPasswordHistoryLength(null, getCurrentOrCallingUserId());
229    }
230
231    public int getRequestedPasswordMinimumLetters() {
232        return getDevicePolicyManager().getPasswordMinimumLetters(null,
233                getCurrentOrCallingUserId());
234    }
235
236    public int getRequestedPasswordMinimumUpperCase() {
237        return getDevicePolicyManager().getPasswordMinimumUpperCase(null,
238                getCurrentOrCallingUserId());
239    }
240
241    public int getRequestedPasswordMinimumLowerCase() {
242        return getDevicePolicyManager().getPasswordMinimumLowerCase(null,
243                getCurrentOrCallingUserId());
244    }
245
246    public int getRequestedPasswordMinimumNumeric() {
247        return getDevicePolicyManager().getPasswordMinimumNumeric(null,
248                getCurrentOrCallingUserId());
249    }
250
251    public int getRequestedPasswordMinimumSymbols() {
252        return getDevicePolicyManager().getPasswordMinimumSymbols(null,
253                getCurrentOrCallingUserId());
254    }
255
256    public int getRequestedPasswordMinimumNonLetter() {
257        return getDevicePolicyManager().getPasswordMinimumNonLetter(null,
258                getCurrentOrCallingUserId());
259    }
260
261    public void reportFailedPasswordAttempt() {
262        int userId = getCurrentOrCallingUserId();
263        getDevicePolicyManager().reportFailedPasswordAttempt(userId);
264        getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
265        getTrustManager().reportRequireCredentialEntry(userId);
266    }
267
268    public void reportSuccessfulPasswordAttempt() {
269        getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId());
270        getTrustManager().reportUnlockAttempt(true /* authenticated */,
271                getCurrentOrCallingUserId());
272    }
273
274    public void setCurrentUser(int userId) {
275        sCurrentUserId = userId;
276    }
277
278    public int getCurrentUser() {
279        if (sCurrentUserId != UserHandle.USER_NULL) {
280            // Someone is regularly updating using setCurrentUser() use that value.
281            return sCurrentUserId;
282        }
283        try {
284            return ActivityManagerNative.getDefault().getCurrentUser().id;
285        } catch (RemoteException re) {
286            return UserHandle.USER_OWNER;
287        }
288    }
289
290    public void removeUser(int userId) {
291        try {
292            getLockSettings().removeUser(userId);
293        } catch (RemoteException re) {
294            Log.e(TAG, "Couldn't remove lock settings for user " + userId);
295        }
296    }
297
298    private int getCurrentOrCallingUserId() {
299        if (mMultiUserMode) {
300            // TODO: This is a little inefficient. See if all users of this are able to
301            // handle USER_CURRENT and pass that instead.
302            return getCurrentUser();
303        } else {
304            return UserHandle.getCallingUserId();
305        }
306    }
307
308    /**
309     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
310     * always returns true.
311     * @param pattern The pattern to check.
312     * @return Whether the pattern matches the stored one.
313     */
314    public boolean checkPattern(List<LockPatternView.Cell> pattern) {
315        final int userId = getCurrentOrCallingUserId();
316        try {
317            return getLockSettings().checkPattern(patternToString(pattern), userId);
318        } catch (RemoteException re) {
319            return true;
320        }
321    }
322
323    /**
324     * Check to see if a password matches the saved password.  If no password exists,
325     * always returns true.
326     * @param password The password to check.
327     * @return Whether the password matches the stored one.
328     */
329    public boolean checkPassword(String password) {
330        final int userId = getCurrentOrCallingUserId();
331        try {
332            return getLockSettings().checkPassword(password, userId);
333        } catch (RemoteException re) {
334            return true;
335        }
336    }
337
338    /**
339     * Check to see if vold already has the password.
340     * Note that this also clears vold's copy of the password.
341     * @return Whether the vold password matches or not.
342     */
343    public boolean checkVoldPassword() {
344        final int userId = getCurrentOrCallingUserId();
345        try {
346            return getLockSettings().checkVoldPassword(userId);
347        } catch (RemoteException re) {
348            return false;
349        }
350    }
351
352    /**
353     * Check to see if a password matches any of the passwords stored in the
354     * password history.
355     *
356     * @param password The password to check.
357     * @return Whether the password matches any in the history.
358     */
359    public boolean checkPasswordHistory(String password) {
360        String passwordHashString = new String(
361                passwordToHash(password, getCurrentOrCallingUserId()));
362        String passwordHistory = getString(PASSWORD_HISTORY_KEY);
363        if (passwordHistory == null) {
364            return false;
365        }
366        // Password History may be too long...
367        int passwordHashLength = passwordHashString.length();
368        int passwordHistoryLength = getRequestedPasswordHistoryLength();
369        if(passwordHistoryLength == 0) {
370            return false;
371        }
372        int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
373                + passwordHistoryLength - 1;
374        if (passwordHistory.length() > neededPasswordHistoryLength) {
375            passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
376        }
377        return passwordHistory.contains(passwordHashString);
378    }
379
380    /**
381     * Check to see if the user has stored a lock pattern.
382     * @return Whether a saved pattern exists.
383     */
384    public boolean savedPatternExists() {
385        try {
386            return getLockSettings().havePattern(getCurrentOrCallingUserId());
387        } catch (RemoteException re) {
388            return false;
389        }
390    }
391
392    /**
393     * Check to see if the user has stored a lock pattern.
394     * @return Whether a saved pattern exists.
395     */
396    public boolean savedPasswordExists() {
397        try {
398            return getLockSettings().havePassword(getCurrentOrCallingUserId());
399        } catch (RemoteException re) {
400            return false;
401        }
402    }
403
404    /**
405     * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
406     * currently cleared.
407     *
408     * @return True if the user has ever chosen a pattern.
409     */
410    public boolean isPatternEverChosen() {
411        return getBoolean(PATTERN_EVER_CHOSEN_KEY, false);
412    }
413
414    /**
415     * Return true if the user has ever chosen biometric weak.  This is true even if biometric
416     * weak is not current set.
417     *
418     * @return True if the user has ever chosen biometric weak.
419     */
420    public boolean isBiometricWeakEverChosen() {
421        return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, false);
422    }
423
424    /**
425     * Used by device policy manager to validate the current password
426     * information it has.
427     */
428    public int getActivePasswordQuality() {
429        int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
430        // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to
431        // return biometric_weak if that is being used instead of the backup
432        int quality =
433                (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
434        switch (quality) {
435            case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
436                if (isLockPatternEnabled()) {
437                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
438                }
439                break;
440            case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK:
441                if (isBiometricWeakInstalled()) {
442                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
443                }
444                break;
445            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
446                if (isLockPasswordEnabled()) {
447                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
448                }
449                break;
450            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
451                if (isLockPasswordEnabled()) {
452                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
453                }
454                break;
455            case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
456                if (isLockPasswordEnabled()) {
457                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
458                }
459                break;
460            case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
461                if (isLockPasswordEnabled()) {
462                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
463                }
464                break;
465            case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
466                if (isLockPasswordEnabled()) {
467                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
468                }
469                break;
470        }
471
472        return activePasswordQuality;
473    }
474
475    /**
476     * Clear any lock pattern or password.
477     */
478    public void clearLock(boolean isFallback) {
479        if(!isFallback) deleteGallery();
480        saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
481        setLockPatternEnabled(false);
482        saveLockPattern(null);
483        setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
484        setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
485        onAfterChangingPassword();
486    }
487
488    /**
489     * Disable showing lock screen at all when the DevicePolicyManager allows it.
490     * This is only meaningful if pattern, pin or password are not set.
491     *
492     * @param disable Disables lock screen when true
493     */
494    public void setLockScreenDisabled(boolean disable) {
495        setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0);
496    }
497
498    /**
499     * Determine if LockScreen can be disabled. This is used, for example, to tell if we should
500     * show LockScreen or go straight to the home screen.
501     *
502     * @return true if lock screen is can be disabled
503     */
504    public boolean isLockScreenDisabled() {
505        if (!isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0) {
506            // Check if the number of switchable users forces the lockscreen.
507            final List<UserInfo> users = UserManager.get(mContext).getUsers(true);
508            final int userCount = users.size();
509            int switchableUsers = 0;
510            for (int i = 0; i < userCount; i++) {
511                if (users.get(i).supportsSwitchTo()) {
512                    switchableUsers++;
513                }
514            }
515            return switchableUsers < 2;
516        }
517        return false;
518    }
519
520    /**
521     * Calls back SetupFaceLock to delete the temporary gallery file
522     */
523    public void deleteTempGallery() {
524        Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY");
525        intent.putExtra("deleteTempGallery", true);
526        mContext.sendBroadcast(intent);
527    }
528
529    /**
530     * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
531    */
532    void deleteGallery() {
533        if(usingBiometricWeak()) {
534            Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY");
535            intent.putExtra("deleteGallery", true);
536            mContext.sendBroadcast(intent);
537        }
538    }
539
540    /**
541     * Save a lock pattern.
542     * @param pattern The new pattern to save.
543     */
544    public void saveLockPattern(List<LockPatternView.Cell> pattern) {
545        this.saveLockPattern(pattern, false);
546    }
547
548    /**
549     * Save a lock pattern.
550     * @param pattern The new pattern to save.
551     * @param isFallback Specifies if this is a fallback to biometric weak
552     */
553    public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
554        try {
555            int userId = getCurrentOrCallingUserId();
556            getLockSettings().setLockPattern(patternToString(pattern), userId);
557            DevicePolicyManager dpm = getDevicePolicyManager();
558            if (pattern != null) {
559
560                int userHandle = userId;
561                if (userHandle == UserHandle.USER_OWNER) {
562                    String stringPattern = patternToString(pattern);
563                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
564                }
565
566                setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
567                if (!isFallback) {
568                    deleteGallery();
569                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
570                    dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
571                            pattern.size(), 0, 0, 0, 0, 0, 0, userId);
572                } else {
573                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
574                    setLong(PASSWORD_TYPE_ALTERNATE_KEY,
575                            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
576                    finishBiometricWeak();
577                    dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
578                            0, 0, 0, 0, 0, 0, 0, userId);
579                }
580            } else {
581                dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
582                        0, 0, 0, 0, 0, userId);
583            }
584            onAfterChangingPassword();
585        } catch (RemoteException re) {
586            Log.e(TAG, "Couldn't save lock pattern " + re);
587        }
588    }
589
590    private void updateCryptoUserInfo() {
591        int userId = getCurrentOrCallingUserId();
592        if (userId != UserHandle.USER_OWNER) {
593            return;
594        }
595
596        final String ownerInfo = isOwnerInfoEnabled() ? getOwnerInfo(userId) : "";
597
598        IBinder service = ServiceManager.getService("mount");
599        if (service == null) {
600            Log.e(TAG, "Could not find the mount service to update the user info");
601            return;
602        }
603
604        IMountService mountService = IMountService.Stub.asInterface(service);
605        try {
606            Log.d(TAG, "Setting owner info");
607            mountService.setField(StorageManager.OWNER_INFO_KEY, ownerInfo);
608        } catch (RemoteException e) {
609            Log.e(TAG, "Error changing user info", e);
610        }
611    }
612
613    public void setOwnerInfo(String info, int userId) {
614        setString(LOCK_SCREEN_OWNER_INFO, info, userId);
615        updateCryptoUserInfo();
616    }
617
618    public void setOwnerInfoEnabled(boolean enabled) {
619        setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled);
620        updateCryptoUserInfo();
621    }
622
623    public String getOwnerInfo(int userId) {
624        return getString(LOCK_SCREEN_OWNER_INFO);
625    }
626
627    public boolean isOwnerInfoEnabled() {
628        return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false);
629    }
630
631    /**
632     * Compute the password quality from the given password string.
633     */
634    static public int computePasswordQuality(String password) {
635        boolean hasDigit = false;
636        boolean hasNonDigit = false;
637        final int len = password.length();
638        for (int i = 0; i < len; i++) {
639            if (Character.isDigit(password.charAt(i))) {
640                hasDigit = true;
641            } else {
642                hasNonDigit = true;
643            }
644        }
645
646        if (hasNonDigit && hasDigit) {
647            return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
648        }
649        if (hasNonDigit) {
650            return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
651        }
652        if (hasDigit) {
653            return maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
654                    ? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
655                    : DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
656        }
657        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
658    }
659
660    private static int categoryChar(char c) {
661        if ('a' <= c && c <= 'z') return 0;
662        if ('A' <= c && c <= 'Z') return 1;
663        if ('0' <= c && c <= '9') return 2;
664        return 3;
665    }
666
667    private static int maxDiffCategory(int category) {
668        if (category == 0 || category == 1) return 1;
669        else if (category == 2) return 10;
670        return 0;
671    }
672
673    /*
674     * Returns the maximum length of a sequential characters.  A sequence is defined as
675     * monotonically increasing characters with a constant interval or the same character repeated.
676     *
677     * For example:
678     * maxLengthSequence("1234") == 4
679     * maxLengthSequence("1234abc") == 4
680     * maxLengthSequence("aabc") == 3
681     * maxLengthSequence("qwertyuio") == 1
682     * maxLengthSequence("@ABC") == 3
683     * maxLengthSequence(";;;;") == 4 (anything that repeats)
684     * maxLengthSequence(":;<=>") == 1  (ordered, but not composed of alphas or digits)
685     *
686     * @param string the pass
687     * @return the number of sequential letters or digits
688     */
689    public static int maxLengthSequence(String string) {
690        if (string.length() == 0) return 0;
691        char previousChar = string.charAt(0);
692        int category = categoryChar(previousChar); //current category of the sequence
693        int diff = 0; //difference between two consecutive characters
694        boolean hasDiff = false; //if we are currently targeting a sequence
695        int maxLength = 0; //maximum length of a sequence already found
696        int startSequence = 0; //where the current sequence started
697        for (int current = 1; current < string.length(); current++) {
698            char currentChar = string.charAt(current);
699            int categoryCurrent = categoryChar(currentChar);
700            int currentDiff = (int) currentChar - (int) previousChar;
701            if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
702                maxLength = Math.max(maxLength, current - startSequence);
703                startSequence = current;
704                hasDiff = false;
705                category = categoryCurrent;
706            }
707            else {
708                if(hasDiff && currentDiff != diff) {
709                    maxLength = Math.max(maxLength, current - startSequence);
710                    startSequence = current - 1;
711                }
712                diff = currentDiff;
713                hasDiff = true;
714            }
715            previousChar = currentChar;
716        }
717        maxLength = Math.max(maxLength, string.length() - startSequence);
718        return maxLength;
719    }
720
721    /** Update the encryption password if it is enabled **/
722    private void updateEncryptionPassword(final int type, final String password) {
723        DevicePolicyManager dpm = getDevicePolicyManager();
724        if (dpm.getStorageEncryptionStatus(getCurrentOrCallingUserId())
725                != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
726            return;
727        }
728
729        final IBinder service = ServiceManager.getService("mount");
730        if (service == null) {
731            Log.e(TAG, "Could not find the mount service to update the encryption password");
732            return;
733        }
734
735        new AsyncTask<Void, Void, Void>() {
736            @Override
737            protected Void doInBackground(Void... dummy) {
738                IMountService mountService = IMountService.Stub.asInterface(service);
739                try {
740                    mountService.changeEncryptionPassword(type, password);
741                } catch (RemoteException e) {
742                    Log.e(TAG, "Error changing encryption password", e);
743                }
744                return null;
745            }
746        }.execute();
747    }
748
749    /**
750     * Save a lock password.  Does not ensure that the password is as good
751     * as the requested mode, but will adjust the mode to be as good as the
752     * pattern.
753     * @param password The password to save
754     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
755     */
756    public void saveLockPassword(String password, int quality) {
757        this.saveLockPassword(password, quality, false, getCurrentOrCallingUserId());
758    }
759
760    /**
761     * Save a lock password.  Does not ensure that the password is as good
762     * as the requested mode, but will adjust the mode to be as good as the
763     * pattern.
764     * @param password The password to save
765     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
766     * @param isFallback Specifies if this is a fallback to biometric weak
767     */
768    public void saveLockPassword(String password, int quality, boolean isFallback) {
769        saveLockPassword(password, quality, isFallback, getCurrentOrCallingUserId());
770    }
771
772    /**
773     * Save a lock password.  Does not ensure that the password is as good
774     * as the requested mode, but will adjust the mode to be as good as the
775     * pattern.
776     * @param password The password to save
777     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
778     * @param isFallback Specifies if this is a fallback to biometric weak
779     * @param userHandle The userId of the user to change the password for
780     */
781    public void saveLockPassword(String password, int quality, boolean isFallback, int userHandle) {
782        try {
783            DevicePolicyManager dpm = getDevicePolicyManager();
784            if (!TextUtils.isEmpty(password)) {
785                getLockSettings().setLockPassword(password, userHandle);
786                int computedQuality = computePasswordQuality(password);
787
788                if (userHandle == UserHandle.USER_OWNER) {
789                    // Update the encryption password.
790                    int type = computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
791                        || computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
792                        ? StorageManager.CRYPT_TYPE_PIN
793                        : StorageManager.CRYPT_TYPE_PASSWORD;
794                    updateEncryptionPassword(type, password);
795                }
796
797                if (!isFallback) {
798                    deleteGallery();
799                    setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
800                    if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
801                        int letters = 0;
802                        int uppercase = 0;
803                        int lowercase = 0;
804                        int numbers = 0;
805                        int symbols = 0;
806                        int nonletter = 0;
807                        for (int i = 0; i < password.length(); i++) {
808                            char c = password.charAt(i);
809                            if (c >= 'A' && c <= 'Z') {
810                                letters++;
811                                uppercase++;
812                            } else if (c >= 'a' && c <= 'z') {
813                                letters++;
814                                lowercase++;
815                            } else if (c >= '0' && c <= '9') {
816                                numbers++;
817                                nonletter++;
818                            } else {
819                                symbols++;
820                                nonletter++;
821                            }
822                        }
823                        dpm.setActivePasswordState(Math.max(quality, computedQuality),
824                                password.length(), letters, uppercase, lowercase,
825                                numbers, symbols, nonletter, userHandle);
826                    } else {
827                        // The password is not anything.
828                        dpm.setActivePasswordState(
829                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
830                                0, 0, 0, 0, 0, 0, 0, userHandle);
831                    }
832                } else {
833                    // Case where it's a fallback for biometric weak
834                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
835                            userHandle);
836                    setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality),
837                            userHandle);
838                    finishBiometricWeak();
839                    dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
840                            0, 0, 0, 0, 0, 0, 0, userHandle);
841                }
842                // Add the password to the password history. We assume all
843                // password hashes have the same length for simplicity of implementation.
844                String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
845                if (passwordHistory == null) {
846                    passwordHistory = new String();
847                }
848                int passwordHistoryLength = getRequestedPasswordHistoryLength();
849                if (passwordHistoryLength == 0) {
850                    passwordHistory = "";
851                } else {
852                    byte[] hash = passwordToHash(password, userHandle);
853                    passwordHistory = new String(hash) + "," + passwordHistory;
854                    // Cut it to contain passwordHistoryLength hashes
855                    // and passwordHistoryLength -1 commas.
856                    passwordHistory = passwordHistory.substring(0, Math.min(hash.length
857                            * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
858                            .length()));
859                }
860                setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
861            } else {
862                // Empty password
863                getLockSettings().setLockPassword(null, userHandle);
864                if (userHandle == UserHandle.USER_OWNER) {
865                    // Set the encryption password to default.
866                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
867                }
868
869                dpm.setActivePasswordState(
870                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
871                        userHandle);
872            }
873            onAfterChangingPassword();
874        } catch (RemoteException re) {
875            // Cant do much
876            Log.e(TAG, "Unable to save lock password " + re);
877        }
878    }
879
880    /**
881     * Gets whether the device is encrypted.
882     *
883     * @return Whether the device is encrypted.
884     */
885    public static boolean isDeviceEncrypted() {
886        IMountService mountService = IMountService.Stub.asInterface(
887                ServiceManager.getService("mount"));
888        try {
889            return mountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE
890                    && mountService.getPasswordType() != StorageManager.CRYPT_TYPE_DEFAULT;
891        } catch (RemoteException re) {
892            Log.e(TAG, "Error getting encryption state", re);
893        }
894        return true;
895    }
896
897    /**
898     * Clears the encryption password.
899     */
900    public void clearEncryptionPassword() {
901        updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
902    }
903
904    /**
905     * Retrieves the quality mode we're in.
906     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
907     *
908     * @return stored password quality
909     */
910    public int getKeyguardStoredPasswordQuality() {
911        return getKeyguardStoredPasswordQuality(getCurrentOrCallingUserId());
912    }
913
914    /**
915     * Retrieves the quality mode for {@param userHandle}.
916     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
917     *
918     * @return stored password quality
919     */
920    public int getKeyguardStoredPasswordQuality(int userHandle) {
921        int quality = (int) getLong(PASSWORD_TYPE_KEY,
922                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
923        // If the user has chosen to use weak biometric sensor, then return the backup locking
924        // method and treat biometric as a special case.
925        if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
926            quality = (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
927                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
928        }
929        return quality;
930    }
931
932    /**
933     * @return true if the lockscreen method is set to biometric weak
934     */
935    public boolean usingBiometricWeak() {
936        int quality =
937                (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
938        return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
939    }
940
941    /**
942     * Deserialize a pattern.
943     * @param string The pattern serialized with {@link #patternToString}
944     * @return The pattern.
945     */
946    public static List<LockPatternView.Cell> stringToPattern(String string) {
947        List<LockPatternView.Cell> result = Lists.newArrayList();
948
949        final byte[] bytes = string.getBytes();
950        for (int i = 0; i < bytes.length; i++) {
951            byte b = bytes[i];
952            result.add(LockPatternView.Cell.of(b / 3, b % 3));
953        }
954        return result;
955    }
956
957    /**
958     * Serialize a pattern.
959     * @param pattern The pattern.
960     * @return The pattern in string form.
961     */
962    public static String patternToString(List<LockPatternView.Cell> pattern) {
963        if (pattern == null) {
964            return "";
965        }
966        final int patternSize = pattern.size();
967
968        byte[] res = new byte[patternSize];
969        for (int i = 0; i < patternSize; i++) {
970            LockPatternView.Cell cell = pattern.get(i);
971            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
972        }
973        return new String(res);
974    }
975
976    /*
977     * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
978     * at least a second level of protection. First level is that the file
979     * is in a location only readable by the system process.
980     * @param pattern the gesture pattern.
981     * @return the hash of the pattern in a byte array.
982     */
983    public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
984        if (pattern == null) {
985            return null;
986        }
987
988        final int patternSize = pattern.size();
989        byte[] res = new byte[patternSize];
990        for (int i = 0; i < patternSize; i++) {
991            LockPatternView.Cell cell = pattern.get(i);
992            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
993        }
994        try {
995            MessageDigest md = MessageDigest.getInstance("SHA-1");
996            byte[] hash = md.digest(res);
997            return hash;
998        } catch (NoSuchAlgorithmException nsa) {
999            return res;
1000        }
1001    }
1002
1003    private String getSalt(int userId) {
1004        long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
1005        if (salt == 0) {
1006            try {
1007                salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
1008                setLong(LOCK_PASSWORD_SALT_KEY, salt, userId);
1009                Log.v(TAG, "Initialized lock password salt for user: " + userId);
1010            } catch (NoSuchAlgorithmException e) {
1011                // Throw an exception rather than storing a password we'll never be able to recover
1012                throw new IllegalStateException("Couldn't get SecureRandom number", e);
1013            }
1014        }
1015        return Long.toHexString(salt);
1016    }
1017
1018    /*
1019     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
1020     * Not the most secure, but it is at least a second level of protection. First level is that
1021     * the file is in a location only readable by the system process.
1022     * @param password the gesture pattern.
1023     * @return the hash of the pattern in a byte array.
1024     */
1025    public byte[] passwordToHash(String password, int userId) {
1026        if (password == null) {
1027            return null;
1028        }
1029        String algo = null;
1030        byte[] hashed = null;
1031        try {
1032            byte[] saltedPassword = (password + getSalt(userId)).getBytes();
1033            byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
1034            byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
1035            hashed = (toHex(sha1) + toHex(md5)).getBytes();
1036        } catch (NoSuchAlgorithmException e) {
1037            Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
1038        }
1039        return hashed;
1040    }
1041
1042    private static String toHex(byte[] ary) {
1043        final String hex = "0123456789ABCDEF";
1044        String ret = "";
1045        for (int i = 0; i < ary.length; i++) {
1046            ret += hex.charAt((ary[i] >> 4) & 0xf);
1047            ret += hex.charAt(ary[i] & 0xf);
1048        }
1049        return ret;
1050    }
1051
1052    /**
1053     * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak
1054     */
1055    public boolean isLockPasswordEnabled() {
1056        long mode = getLong(PASSWORD_TYPE_KEY, 0);
1057        long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0);
1058        final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1059                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1060                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1061                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1062                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1063        final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1064                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1065                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1066                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1067                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1068
1069        return savedPasswordExists() && (passwordEnabled ||
1070                (usingBiometricWeak() && backupEnabled));
1071    }
1072
1073    /**
1074     * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
1075     */
1076    public boolean isLockPatternEnabled() {
1077        final boolean backupEnabled =
1078                getLong(PASSWORD_TYPE_ALTERNATE_KEY,
1079                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
1080                                == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1081
1082        return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false)
1083                && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
1084                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
1085                        (usingBiometricWeak() && backupEnabled));
1086    }
1087
1088    /**
1089     * @return Whether biometric weak lock is installed and that the front facing camera exists
1090     */
1091    public boolean isBiometricWeakInstalled() {
1092        // Check that it's installed
1093        PackageManager pm = mContext.getPackageManager();
1094        try {
1095            pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES);
1096        } catch (PackageManager.NameNotFoundException e) {
1097            return false;
1098        }
1099
1100        // Check that the camera is enabled
1101        if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
1102            return false;
1103        }
1104        if (getDevicePolicyManager().getCameraDisabled(null, getCurrentOrCallingUserId())) {
1105            return false;
1106        }
1107
1108        // TODO: If we decide not to proceed with Face Unlock as a trustlet, this must be changed
1109        // back to returning true.  If we become certain that Face Unlock will be a trustlet, this
1110        // entire function and a lot of other code can be removed.
1111        if (DEBUG) Log.d(TAG, "Forcing isBiometricWeakInstalled() to return false to disable it");
1112        return false;
1113    }
1114
1115    /**
1116     * Set whether biometric weak liveliness is enabled.
1117     */
1118    public void setBiometricWeakLivelinessEnabled(boolean enabled) {
1119        long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
1120        long newFlag;
1121        if (enabled) {
1122            newFlag = currentFlag | FLAG_BIOMETRIC_WEAK_LIVELINESS;
1123        } else {
1124            newFlag = currentFlag & ~FLAG_BIOMETRIC_WEAK_LIVELINESS;
1125        }
1126        setLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, newFlag);
1127    }
1128
1129    /**
1130     * @return Whether the biometric weak liveliness is enabled.
1131     */
1132    public boolean isBiometricWeakLivelinessEnabled() {
1133        long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
1134        return ((currentFlag & FLAG_BIOMETRIC_WEAK_LIVELINESS) != 0);
1135    }
1136
1137    /**
1138     * Set whether the lock pattern is enabled.
1139     */
1140    public void setLockPatternEnabled(boolean enabled) {
1141        setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
1142    }
1143
1144    /**
1145     * @return Whether the visible pattern is enabled.
1146     */
1147    public boolean isVisiblePatternEnabled() {
1148        return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false);
1149    }
1150
1151    /**
1152     * Set whether the visible pattern is enabled.
1153     */
1154    public void setVisiblePatternEnabled(boolean enabled) {
1155        setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
1156
1157        // Update for crypto if owner
1158        int userId = getCurrentOrCallingUserId();
1159        if (userId != UserHandle.USER_OWNER) {
1160            return;
1161        }
1162
1163        IBinder service = ServiceManager.getService("mount");
1164        if (service == null) {
1165            Log.e(TAG, "Could not find the mount service to update the user info");
1166            return;
1167        }
1168
1169        IMountService mountService = IMountService.Stub.asInterface(service);
1170        try {
1171            mountService.setField(StorageManager.PATTERN_VISIBLE_KEY, enabled ? "1" : "0");
1172        } catch (RemoteException e) {
1173            Log.e(TAG, "Error changing pattern visible state", e);
1174        }
1175    }
1176
1177    /**
1178     * @return Whether tactile feedback for the pattern is enabled.
1179     */
1180    public boolean isTactileFeedbackEnabled() {
1181        return Settings.System.getIntForUser(mContentResolver,
1182                Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
1183    }
1184
1185    /**
1186     * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
1187     * pattern until the deadline has passed.
1188     * @return the chosen deadline.
1189     */
1190    public long setLockoutAttemptDeadline() {
1191        final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
1192        setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
1193        return deadline;
1194    }
1195
1196    /**
1197     * @return The elapsed time in millis in the future when the user is allowed to
1198     *   attempt to enter his/her lock pattern, or 0 if the user is welcome to
1199     *   enter a pattern.
1200     */
1201    public long getLockoutAttemptDeadline() {
1202        final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
1203        final long now = SystemClock.elapsedRealtime();
1204        if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
1205            return 0L;
1206        }
1207        return deadline;
1208    }
1209
1210    /**
1211     * @return Whether the user is permanently locked out until they verify their
1212     *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
1213     *   attempts.
1214     */
1215    public boolean isPermanentlyLocked() {
1216        return getBoolean(LOCKOUT_PERMANENT_KEY, false);
1217    }
1218
1219    /**
1220     * Set the state of whether the device is permanently locked, meaning the user
1221     * must authenticate via other means.
1222     *
1223     * @param locked Whether the user is permanently locked out until they verify their
1224     *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
1225     *   attempts.
1226     */
1227    public void setPermanentlyLocked(boolean locked) {
1228        setBoolean(LOCKOUT_PERMANENT_KEY, locked);
1229    }
1230
1231    public boolean isEmergencyCallCapable() {
1232        return mContext.getResources().getBoolean(
1233                com.android.internal.R.bool.config_voice_capable);
1234    }
1235
1236    public boolean isPukUnlockScreenEnable() {
1237        return mContext.getResources().getBoolean(
1238                com.android.internal.R.bool.config_enable_puk_unlock_screen);
1239    }
1240
1241    public boolean isEmergencyCallEnabledWhileSimLocked() {
1242        return mContext.getResources().getBoolean(
1243                com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
1244    }
1245
1246    /**
1247     * @return A formatted string of the next alarm (for showing on the lock screen),
1248     *   or null if there is no next alarm.
1249     */
1250    public AlarmManager.AlarmClockInfo getNextAlarm() {
1251        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
1252        return alarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
1253    }
1254
1255    private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
1256        try {
1257            return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
1258        } catch (RemoteException re) {
1259            return defaultValue;
1260        }
1261    }
1262
1263    private boolean getBoolean(String secureSettingKey, boolean defaultValue) {
1264        return getBoolean(secureSettingKey, defaultValue, getCurrentOrCallingUserId());
1265    }
1266
1267    private void setBoolean(String secureSettingKey, boolean enabled, int userId) {
1268        try {
1269            getLockSettings().setBoolean(secureSettingKey, enabled, userId);
1270        } catch (RemoteException re) {
1271            // What can we do?
1272            Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re);
1273        }
1274    }
1275
1276    private void setBoolean(String secureSettingKey, boolean enabled) {
1277        setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId());
1278    }
1279
1280    public int[] getAppWidgets() {
1281        return getAppWidgets(UserHandle.USER_CURRENT);
1282    }
1283
1284    private int[] getAppWidgets(int userId) {
1285        String appWidgetIdString = Settings.Secure.getStringForUser(
1286                mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS, userId);
1287        String delims = ",";
1288        if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
1289            String[] appWidgetStringIds = appWidgetIdString.split(delims);
1290            int[] appWidgetIds = new int[appWidgetStringIds.length];
1291            for (int i = 0; i < appWidgetStringIds.length; i++) {
1292                String appWidget = appWidgetStringIds[i];
1293                try {
1294                    appWidgetIds[i] = Integer.decode(appWidget);
1295                } catch (NumberFormatException e) {
1296                    Log.d(TAG, "Error when parsing widget id " + appWidget);
1297                    return null;
1298                }
1299            }
1300            return appWidgetIds;
1301        }
1302        return new int[0];
1303    }
1304
1305    private static String combineStrings(int[] list, String separator) {
1306        int listLength = list.length;
1307
1308        switch (listLength) {
1309            case 0: {
1310                return "";
1311            }
1312            case 1: {
1313                return Integer.toString(list[0]);
1314            }
1315        }
1316
1317        int strLength = 0;
1318        int separatorLength = separator.length();
1319
1320        String[] stringList = new String[list.length];
1321        for (int i = 0; i < listLength; i++) {
1322            stringList[i] = Integer.toString(list[i]);
1323            strLength += stringList[i].length();
1324            if (i < listLength - 1) {
1325                strLength += separatorLength;
1326            }
1327        }
1328
1329        StringBuilder sb = new StringBuilder(strLength);
1330
1331        for (int i = 0; i < listLength; i++) {
1332            sb.append(list[i]);
1333            if (i < listLength - 1) {
1334                sb.append(separator);
1335            }
1336        }
1337
1338        return sb.toString();
1339    }
1340
1341    // appwidget used when appwidgets are disabled (we make an exception for
1342    // default clock widget)
1343    public void writeFallbackAppWidgetId(int appWidgetId) {
1344        Settings.Secure.putIntForUser(mContentResolver,
1345                Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
1346                appWidgetId,
1347                UserHandle.USER_CURRENT);
1348    }
1349
1350    // appwidget used when appwidgets are disabled (we make an exception for
1351    // default clock widget)
1352    public int getFallbackAppWidgetId() {
1353        return Settings.Secure.getIntForUser(
1354                mContentResolver,
1355                Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
1356                AppWidgetManager.INVALID_APPWIDGET_ID,
1357                UserHandle.USER_CURRENT);
1358    }
1359
1360    private void writeAppWidgets(int[] appWidgetIds) {
1361        Settings.Secure.putStringForUser(mContentResolver,
1362                        Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
1363                        combineStrings(appWidgetIds, ","),
1364                        UserHandle.USER_CURRENT);
1365    }
1366
1367    // TODO: log an error if this returns false
1368    public boolean addAppWidget(int widgetId, int index) {
1369        int[] widgets = getAppWidgets();
1370        if (widgets == null) {
1371            return false;
1372        }
1373        if (index < 0 || index > widgets.length) {
1374            return false;
1375        }
1376        int[] newWidgets = new int[widgets.length + 1];
1377        for (int i = 0, j = 0; i < newWidgets.length; i++) {
1378            if (index == i) {
1379                newWidgets[i] = widgetId;
1380                i++;
1381            }
1382            if (i < newWidgets.length) {
1383                newWidgets[i] = widgets[j];
1384                j++;
1385            }
1386        }
1387        writeAppWidgets(newWidgets);
1388        return true;
1389    }
1390
1391    public boolean removeAppWidget(int widgetId) {
1392        int[] widgets = getAppWidgets();
1393
1394        if (widgets.length == 0) {
1395            return false;
1396        }
1397
1398        int[] newWidgets = new int[widgets.length - 1];
1399        for (int i = 0, j = 0; i < widgets.length; i++) {
1400            if (widgets[i] == widgetId) {
1401                // continue...
1402            } else if (j >= newWidgets.length) {
1403                // we couldn't find the widget
1404                return false;
1405            } else {
1406                newWidgets[j] = widgets[i];
1407                j++;
1408            }
1409        }
1410        writeAppWidgets(newWidgets);
1411        return true;
1412    }
1413
1414    private long getLong(String secureSettingKey, long defaultValue, int userHandle) {
1415        try {
1416            return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
1417        } catch (RemoteException re) {
1418            return defaultValue;
1419        }
1420    }
1421
1422    private long getLong(String secureSettingKey, long defaultValue) {
1423        try {
1424            return getLockSettings().getLong(secureSettingKey, defaultValue,
1425                    getCurrentOrCallingUserId());
1426        } catch (RemoteException re) {
1427            return defaultValue;
1428        }
1429    }
1430
1431    private void setLong(String secureSettingKey, long value) {
1432        setLong(secureSettingKey, value, getCurrentOrCallingUserId());
1433    }
1434
1435    private void setLong(String secureSettingKey, long value, int userHandle) {
1436        try {
1437            getLockSettings().setLong(secureSettingKey, value, userHandle);
1438        } catch (RemoteException re) {
1439            // What can we do?
1440            Log.e(TAG, "Couldn't write long " + secureSettingKey + re);
1441        }
1442    }
1443
1444    private String getString(String secureSettingKey) {
1445        return getString(secureSettingKey, getCurrentOrCallingUserId());
1446    }
1447
1448    private String getString(String secureSettingKey, int userHandle) {
1449        try {
1450            return getLockSettings().getString(secureSettingKey, null, userHandle);
1451        } catch (RemoteException re) {
1452            return null;
1453        }
1454    }
1455
1456    private void setString(String secureSettingKey, String value, int userHandle) {
1457        try {
1458            getLockSettings().setString(secureSettingKey, value, userHandle);
1459        } catch (RemoteException re) {
1460            // What can we do?
1461            Log.e(TAG, "Couldn't write string " + secureSettingKey + re);
1462        }
1463    }
1464
1465    public boolean isSecure() {
1466        long mode = getKeyguardStoredPasswordQuality();
1467        final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1468        final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1469                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1470                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1471                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1472                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1473        final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
1474                || isPassword && savedPasswordExists();
1475        return secure;
1476    }
1477
1478    /**
1479     * Sets the emergency button visibility based on isEmergencyCallCapable().
1480     *
1481     * If the emergency button is visible, sets the text on the emergency button
1482     * to indicate what action will be taken.
1483     *
1484     * If there's currently a call in progress, the button will take them to the call
1485     * @param button The button to update
1486     * @param shown Indicates whether the given screen wants the emergency button to show at all
1487     * @param showIcon Indicates whether to show a phone icon for the button.
1488     */
1489    public void updateEmergencyCallButtonState(Button button, boolean shown, boolean showIcon) {
1490        if (isEmergencyCallCapable() && shown) {
1491            button.setVisibility(View.VISIBLE);
1492        } else {
1493            button.setVisibility(View.GONE);
1494            return;
1495        }
1496
1497        int textId;
1498        if (isInCall()) {
1499            // show "return to call" text and show phone icon
1500            textId = R.string.lockscreen_return_to_call;
1501            int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0;
1502            button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
1503        } else {
1504            textId = R.string.lockscreen_emergency_call;
1505            int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0;
1506            button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
1507        }
1508        button.setText(textId);
1509    }
1510
1511    /**
1512     * Resumes a call in progress. Typically launched from the EmergencyCall button
1513     * on various lockscreens.
1514     */
1515    public void resumeCall() {
1516        getTelecommManager().showInCallScreen(false);
1517    }
1518
1519    /**
1520     * @return {@code true} if there is a call currently in progress, {@code false} otherwise.
1521     */
1522    public boolean isInCall() {
1523        return getTelecommManager().isInCall();
1524    }
1525
1526    private TelecomManager getTelecommManager() {
1527        return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
1528    }
1529
1530    private void finishBiometricWeak() {
1531        setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
1532
1533        // Launch intent to show final screen, this also
1534        // moves the temporary gallery to the actual gallery
1535        Intent intent = new Intent();
1536        intent.setClassName("com.android.facelock",
1537                "com.android.facelock.SetupEndScreen");
1538        mContext.startActivity(intent);
1539    }
1540
1541    public void setPowerButtonInstantlyLocks(boolean enabled) {
1542        setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled);
1543    }
1544
1545    public boolean getPowerButtonInstantlyLocks() {
1546        return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
1547    }
1548
1549    public static boolean isSafeModeEnabled() {
1550        try {
1551            return IWindowManager.Stub.asInterface(
1552                    ServiceManager.getService("window")).isSafeModeEnabled();
1553        } catch (RemoteException e) {
1554            // Shouldn't happen!
1555        }
1556        return false;
1557    }
1558
1559    /**
1560     * Determine whether the user has selected any non-system widgets in keyguard
1561     *
1562     * @return true if widgets have been selected
1563     */
1564    public boolean hasWidgetsEnabledInKeyguard(int userid) {
1565        int widgets[] = getAppWidgets(userid);
1566        for (int i = 0; i < widgets.length; i++) {
1567            if (widgets[i] > 0) {
1568                return true;
1569            }
1570        }
1571        return false;
1572    }
1573
1574    public boolean getWidgetsEnabled() {
1575        return getWidgetsEnabled(getCurrentOrCallingUserId());
1576    }
1577
1578    public boolean getWidgetsEnabled(int userId) {
1579        return getBoolean(LOCKSCREEN_WIDGETS_ENABLED, false, userId);
1580    }
1581
1582    public void setWidgetsEnabled(boolean enabled) {
1583        setWidgetsEnabled(enabled, getCurrentOrCallingUserId());
1584    }
1585
1586    public void setWidgetsEnabled(boolean enabled, int userId) {
1587        setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId);
1588    }
1589
1590    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) {
1591        setEnabledTrustAgents(activeTrustAgents, getCurrentOrCallingUserId());
1592    }
1593
1594    public List<ComponentName> getEnabledTrustAgents() {
1595        return getEnabledTrustAgents(getCurrentOrCallingUserId());
1596    }
1597
1598    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
1599        StringBuilder sb = new StringBuilder();
1600        for (ComponentName cn : activeTrustAgents) {
1601            if (sb.length() > 0) {
1602                sb.append(',');
1603            }
1604            sb.append(cn.flattenToShortString());
1605        }
1606        setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
1607        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
1608    }
1609
1610    public List<ComponentName> getEnabledTrustAgents(int userId) {
1611        String serialized = getString(ENABLED_TRUST_AGENTS, userId);
1612        if (TextUtils.isEmpty(serialized)) {
1613            return null;
1614        }
1615        String[] split = serialized.split(",");
1616        ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
1617        for (String s : split) {
1618            if (!TextUtils.isEmpty(s)) {
1619                activeTrustAgents.add(ComponentName.unflattenFromString(s));
1620            }
1621        }
1622        return activeTrustAgents;
1623    }
1624
1625    /**
1626     * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int)
1627     */
1628    public void requireCredentialEntry(int userId) {
1629        getTrustManager().reportRequireCredentialEntry(userId);
1630    }
1631
1632    private void onAfterChangingPassword() {
1633        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
1634    }
1635}
1636