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