LockPatternUtils.java revision 0eb1e402c7e612887e38dc5516f11506b11fd835
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                        ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
771                    updateEncryptionPassword(type, password);
772                }
773
774                if (!isFallback) {
775                    deleteGallery();
776                    setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
777                    if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
778                        int letters = 0;
779                        int uppercase = 0;
780                        int lowercase = 0;
781                        int numbers = 0;
782                        int symbols = 0;
783                        int nonletter = 0;
784                        for (int i = 0; i < password.length(); i++) {
785                            char c = password.charAt(i);
786                            if (c >= 'A' && c <= 'Z') {
787                                letters++;
788                                uppercase++;
789                            } else if (c >= 'a' && c <= 'z') {
790                                letters++;
791                                lowercase++;
792                            } else if (c >= '0' && c <= '9') {
793                                numbers++;
794                                nonletter++;
795                            } else {
796                                symbols++;
797                                nonletter++;
798                            }
799                        }
800                        dpm.setActivePasswordState(Math.max(quality, computedQuality),
801                                password.length(), letters, uppercase, lowercase,
802                                numbers, symbols, nonletter, userHandle);
803                    } else {
804                        // The password is not anything.
805                        dpm.setActivePasswordState(
806                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
807                                0, 0, 0, 0, 0, 0, 0, userHandle);
808                    }
809                } else {
810                    // Case where it's a fallback for biometric weak
811                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
812                            userHandle);
813                    setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality),
814                            userHandle);
815                    finishBiometricWeak();
816                    dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
817                            0, 0, 0, 0, 0, 0, 0, userHandle);
818                }
819                // Add the password to the password history. We assume all
820                // password hashes have the same length for simplicity of implementation.
821                String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
822                if (passwordHistory == null) {
823                    passwordHistory = new String();
824                }
825                int passwordHistoryLength = getRequestedPasswordHistoryLength();
826                if (passwordHistoryLength == 0) {
827                    passwordHistory = "";
828                } else {
829                    byte[] hash = passwordToHash(password, userHandle);
830                    passwordHistory = new String(hash) + "," + passwordHistory;
831                    // Cut it to contain passwordHistoryLength hashes
832                    // and passwordHistoryLength -1 commas.
833                    passwordHistory = passwordHistory.substring(0, Math.min(hash.length
834                            * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
835                            .length()));
836                }
837                setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
838            } else {
839                // Empty password
840                if (userHandle == UserHandle.USER_OWNER) {
841                    // Set the encryption password to default.
842                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
843                }
844
845                dpm.setActivePasswordState(
846                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
847                        userHandle);
848            }
849            onAfterChangingPassword();
850        } catch (RemoteException re) {
851            // Cant do much
852            Log.e(TAG, "Unable to save lock password " + re);
853        }
854    }
855
856    /**
857     * Retrieves the quality mode we're in.
858     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
859     *
860     * @return stored password quality
861     */
862    public int getKeyguardStoredPasswordQuality() {
863        int quality =
864                (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
865        // If the user has chosen to use weak biometric sensor, then return the backup locking
866        // method and treat biometric as a special case.
867        if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
868            quality =
869                (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
870                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
871        }
872        return quality;
873    }
874
875    /**
876     * @return true if the lockscreen method is set to biometric weak
877     */
878    public boolean usingBiometricWeak() {
879        int quality =
880                (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
881        return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
882    }
883
884    /**
885     * Deserialize a pattern.
886     * @param string The pattern serialized with {@link #patternToString}
887     * @return The pattern.
888     */
889    public static List<LockPatternView.Cell> stringToPattern(String string) {
890        List<LockPatternView.Cell> result = Lists.newArrayList();
891
892        final byte[] bytes = string.getBytes();
893        for (int i = 0; i < bytes.length; i++) {
894            byte b = bytes[i];
895            result.add(LockPatternView.Cell.of(b / 3, b % 3));
896        }
897        return result;
898    }
899
900    /**
901     * Serialize a pattern.
902     * @param pattern The pattern.
903     * @return The pattern in string form.
904     */
905    public static String patternToString(List<LockPatternView.Cell> pattern) {
906        if (pattern == null) {
907            return "";
908        }
909        final int patternSize = pattern.size();
910
911        byte[] res = new byte[patternSize];
912        for (int i = 0; i < patternSize; i++) {
913            LockPatternView.Cell cell = pattern.get(i);
914            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
915        }
916        return new String(res);
917    }
918
919    /*
920     * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
921     * at least a second level of protection. First level is that the file
922     * is in a location only readable by the system process.
923     * @param pattern the gesture pattern.
924     * @return the hash of the pattern in a byte array.
925     */
926    public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
927        if (pattern == null) {
928            return null;
929        }
930
931        final int patternSize = pattern.size();
932        byte[] res = new byte[patternSize];
933        for (int i = 0; i < patternSize; i++) {
934            LockPatternView.Cell cell = pattern.get(i);
935            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
936        }
937        try {
938            MessageDigest md = MessageDigest.getInstance("SHA-1");
939            byte[] hash = md.digest(res);
940            return hash;
941        } catch (NoSuchAlgorithmException nsa) {
942            return res;
943        }
944    }
945
946    private String getSalt(int userId) {
947        long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
948        if (salt == 0) {
949            try {
950                salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
951                setLong(LOCK_PASSWORD_SALT_KEY, salt, userId);
952                Log.v(TAG, "Initialized lock password salt for user: " + userId);
953            } catch (NoSuchAlgorithmException e) {
954                // Throw an exception rather than storing a password we'll never be able to recover
955                throw new IllegalStateException("Couldn't get SecureRandom number", e);
956            }
957        }
958        return Long.toHexString(salt);
959    }
960
961    /*
962     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
963     * Not the most secure, but it is at least a second level of protection. First level is that
964     * the file is in a location only readable by the system process.
965     * @param password the gesture pattern.
966     * @return the hash of the pattern in a byte array.
967     */
968    public byte[] passwordToHash(String password, int userId) {
969        if (password == null) {
970            return null;
971        }
972        String algo = null;
973        byte[] hashed = null;
974        try {
975            byte[] saltedPassword = (password + getSalt(userId)).getBytes();
976            byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
977            byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
978            hashed = (toHex(sha1) + toHex(md5)).getBytes();
979        } catch (NoSuchAlgorithmException e) {
980            Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
981        }
982        return hashed;
983    }
984
985    private static String toHex(byte[] ary) {
986        final String hex = "0123456789ABCDEF";
987        String ret = "";
988        for (int i = 0; i < ary.length; i++) {
989            ret += hex.charAt((ary[i] >> 4) & 0xf);
990            ret += hex.charAt(ary[i] & 0xf);
991        }
992        return ret;
993    }
994
995    /**
996     * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak
997     */
998    public boolean isLockPasswordEnabled() {
999        long mode = getLong(PASSWORD_TYPE_KEY, 0);
1000        long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0);
1001        final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1002                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1003                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1004                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1005                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1006        final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1007                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1008                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1009                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1010                || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1011
1012        return savedPasswordExists() && (passwordEnabled ||
1013                (usingBiometricWeak() && backupEnabled));
1014    }
1015
1016    /**
1017     * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
1018     */
1019    public boolean isLockPatternEnabled() {
1020        final boolean backupEnabled =
1021                getLong(PASSWORD_TYPE_ALTERNATE_KEY,
1022                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
1023                                == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1024
1025        return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false)
1026                && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
1027                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
1028                        (usingBiometricWeak() && backupEnabled));
1029    }
1030
1031    /**
1032     * @return Whether biometric weak lock is installed and that the front facing camera exists
1033     */
1034    public boolean isBiometricWeakInstalled() {
1035        // Check that it's installed
1036        PackageManager pm = mContext.getPackageManager();
1037        try {
1038            pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES);
1039        } catch (PackageManager.NameNotFoundException e) {
1040            return false;
1041        }
1042
1043        // Check that the camera is enabled
1044        if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
1045            return false;
1046        }
1047        if (getDevicePolicyManager().getCameraDisabled(null, getCurrentOrCallingUserId())) {
1048            return false;
1049        }
1050
1051        // TODO: If we decide not to proceed with Face Unlock as a trustlet, this must be changed
1052        // back to returning true.  If we become certain that Face Unlock will be a trustlet, this
1053        // entire function and a lot of other code can be removed.
1054        if (DEBUG) Log.d(TAG, "Forcing isBiometricWeakInstalled() to return false to disable it");
1055        return false;
1056    }
1057
1058    /**
1059     * Set whether biometric weak liveliness is enabled.
1060     */
1061    public void setBiometricWeakLivelinessEnabled(boolean enabled) {
1062        long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
1063        long newFlag;
1064        if (enabled) {
1065            newFlag = currentFlag | FLAG_BIOMETRIC_WEAK_LIVELINESS;
1066        } else {
1067            newFlag = currentFlag & ~FLAG_BIOMETRIC_WEAK_LIVELINESS;
1068        }
1069        setLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, newFlag);
1070    }
1071
1072    /**
1073     * @return Whether the biometric weak liveliness is enabled.
1074     */
1075    public boolean isBiometricWeakLivelinessEnabled() {
1076        long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L);
1077        return ((currentFlag & FLAG_BIOMETRIC_WEAK_LIVELINESS) != 0);
1078    }
1079
1080    /**
1081     * Set whether the lock pattern is enabled.
1082     */
1083    public void setLockPatternEnabled(boolean enabled) {
1084        setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
1085    }
1086
1087    /**
1088     * @return Whether the visible pattern is enabled.
1089     */
1090    public boolean isVisiblePatternEnabled() {
1091        return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false);
1092    }
1093
1094    /**
1095     * Set whether the visible pattern is enabled.
1096     */
1097    public void setVisiblePatternEnabled(boolean enabled) {
1098        setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
1099
1100        // Update for crypto if owner
1101        int userId = getCurrentOrCallingUserId();
1102        if (userId != UserHandle.USER_OWNER) {
1103            return;
1104        }
1105
1106        IBinder service = ServiceManager.getService("mount");
1107        if (service == null) {
1108            Log.e(TAG, "Could not find the mount service to update the user info");
1109            return;
1110        }
1111
1112        IMountService mountService = IMountService.Stub.asInterface(service);
1113        try {
1114            mountService.setField("PatternVisible", enabled ? "1" : "0");
1115        } catch (RemoteException e) {
1116            Log.e(TAG, "Error changing pattern visible state", e);
1117        }
1118    }
1119
1120    /**
1121     * @return Whether tactile feedback for the pattern is enabled.
1122     */
1123    public boolean isTactileFeedbackEnabled() {
1124        return Settings.System.getIntForUser(mContentResolver,
1125                Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
1126    }
1127
1128    /**
1129     * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
1130     * pattern until the deadline has passed.
1131     * @return the chosen deadline.
1132     */
1133    public long setLockoutAttemptDeadline() {
1134        final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
1135        setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
1136        return deadline;
1137    }
1138
1139    /**
1140     * @return The elapsed time in millis in the future when the user is allowed to
1141     *   attempt to enter his/her lock pattern, or 0 if the user is welcome to
1142     *   enter a pattern.
1143     */
1144    public long getLockoutAttemptDeadline() {
1145        final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
1146        final long now = SystemClock.elapsedRealtime();
1147        if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
1148            return 0L;
1149        }
1150        return deadline;
1151    }
1152
1153    /**
1154     * @return Whether the user is permanently locked out until they verify their
1155     *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
1156     *   attempts.
1157     */
1158    public boolean isPermanentlyLocked() {
1159        return getBoolean(LOCKOUT_PERMANENT_KEY, false);
1160    }
1161
1162    /**
1163     * Set the state of whether the device is permanently locked, meaning the user
1164     * must authenticate via other means.
1165     *
1166     * @param locked Whether the user is permanently locked out until they verify their
1167     *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
1168     *   attempts.
1169     */
1170    public void setPermanentlyLocked(boolean locked) {
1171        setBoolean(LOCKOUT_PERMANENT_KEY, locked);
1172    }
1173
1174    public boolean isEmergencyCallCapable() {
1175        return mContext.getResources().getBoolean(
1176                com.android.internal.R.bool.config_voice_capable);
1177    }
1178
1179    public boolean isPukUnlockScreenEnable() {
1180        return mContext.getResources().getBoolean(
1181                com.android.internal.R.bool.config_enable_puk_unlock_screen);
1182    }
1183
1184    public boolean isEmergencyCallEnabledWhileSimLocked() {
1185        return mContext.getResources().getBoolean(
1186                com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
1187    }
1188
1189    /**
1190     * @return A formatted string of the next alarm (for showing on the lock screen),
1191     *   or null if there is no next alarm.
1192     */
1193    public AlarmManager.AlarmClockInfo getNextAlarm() {
1194        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
1195        return alarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
1196    }
1197
1198    private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
1199        try {
1200            return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
1201        } catch (RemoteException re) {
1202            return defaultValue;
1203        }
1204    }
1205
1206    private boolean getBoolean(String secureSettingKey, boolean defaultValue) {
1207        return getBoolean(secureSettingKey, defaultValue, getCurrentOrCallingUserId());
1208    }
1209
1210    private void setBoolean(String secureSettingKey, boolean enabled, int userId) {
1211        try {
1212            getLockSettings().setBoolean(secureSettingKey, enabled, userId);
1213        } catch (RemoteException re) {
1214            // What can we do?
1215            Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re);
1216        }
1217    }
1218
1219    private void setBoolean(String secureSettingKey, boolean enabled) {
1220        setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId());
1221    }
1222
1223    public int[] getAppWidgets() {
1224        return getAppWidgets(UserHandle.USER_CURRENT);
1225    }
1226
1227    private int[] getAppWidgets(int userId) {
1228        String appWidgetIdString = Settings.Secure.getStringForUser(
1229                mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS, userId);
1230        String delims = ",";
1231        if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
1232            String[] appWidgetStringIds = appWidgetIdString.split(delims);
1233            int[] appWidgetIds = new int[appWidgetStringIds.length];
1234            for (int i = 0; i < appWidgetStringIds.length; i++) {
1235                String appWidget = appWidgetStringIds[i];
1236                try {
1237                    appWidgetIds[i] = Integer.decode(appWidget);
1238                } catch (NumberFormatException e) {
1239                    Log.d(TAG, "Error when parsing widget id " + appWidget);
1240                    return null;
1241                }
1242            }
1243            return appWidgetIds;
1244        }
1245        return new int[0];
1246    }
1247
1248    private static String combineStrings(int[] list, String separator) {
1249        int listLength = list.length;
1250
1251        switch (listLength) {
1252            case 0: {
1253                return "";
1254            }
1255            case 1: {
1256                return Integer.toString(list[0]);
1257            }
1258        }
1259
1260        int strLength = 0;
1261        int separatorLength = separator.length();
1262
1263        String[] stringList = new String[list.length];
1264        for (int i = 0; i < listLength; i++) {
1265            stringList[i] = Integer.toString(list[i]);
1266            strLength += stringList[i].length();
1267            if (i < listLength - 1) {
1268                strLength += separatorLength;
1269            }
1270        }
1271
1272        StringBuilder sb = new StringBuilder(strLength);
1273
1274        for (int i = 0; i < listLength; i++) {
1275            sb.append(list[i]);
1276            if (i < listLength - 1) {
1277                sb.append(separator);
1278            }
1279        }
1280
1281        return sb.toString();
1282    }
1283
1284    // appwidget used when appwidgets are disabled (we make an exception for
1285    // default clock widget)
1286    public void writeFallbackAppWidgetId(int appWidgetId) {
1287        Settings.Secure.putIntForUser(mContentResolver,
1288                Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
1289                appWidgetId,
1290                UserHandle.USER_CURRENT);
1291    }
1292
1293    // appwidget used when appwidgets are disabled (we make an exception for
1294    // default clock widget)
1295    public int getFallbackAppWidgetId() {
1296        return Settings.Secure.getIntForUser(
1297                mContentResolver,
1298                Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
1299                AppWidgetManager.INVALID_APPWIDGET_ID,
1300                UserHandle.USER_CURRENT);
1301    }
1302
1303    private void writeAppWidgets(int[] appWidgetIds) {
1304        Settings.Secure.putStringForUser(mContentResolver,
1305                        Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
1306                        combineStrings(appWidgetIds, ","),
1307                        UserHandle.USER_CURRENT);
1308    }
1309
1310    // TODO: log an error if this returns false
1311    public boolean addAppWidget(int widgetId, int index) {
1312        int[] widgets = getAppWidgets();
1313        if (widgets == null) {
1314            return false;
1315        }
1316        if (index < 0 || index > widgets.length) {
1317            return false;
1318        }
1319        int[] newWidgets = new int[widgets.length + 1];
1320        for (int i = 0, j = 0; i < newWidgets.length; i++) {
1321            if (index == i) {
1322                newWidgets[i] = widgetId;
1323                i++;
1324            }
1325            if (i < newWidgets.length) {
1326                newWidgets[i] = widgets[j];
1327                j++;
1328            }
1329        }
1330        writeAppWidgets(newWidgets);
1331        return true;
1332    }
1333
1334    public boolean removeAppWidget(int widgetId) {
1335        int[] widgets = getAppWidgets();
1336
1337        if (widgets.length == 0) {
1338            return false;
1339        }
1340
1341        int[] newWidgets = new int[widgets.length - 1];
1342        for (int i = 0, j = 0; i < widgets.length; i++) {
1343            if (widgets[i] == widgetId) {
1344                // continue...
1345            } else if (j >= newWidgets.length) {
1346                // we couldn't find the widget
1347                return false;
1348            } else {
1349                newWidgets[j] = widgets[i];
1350                j++;
1351            }
1352        }
1353        writeAppWidgets(newWidgets);
1354        return true;
1355    }
1356
1357    private long getLong(String secureSettingKey, long defaultValue, int userHandle) {
1358        try {
1359            return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle);
1360        } catch (RemoteException re) {
1361            return defaultValue;
1362        }
1363    }
1364
1365    private long getLong(String secureSettingKey, long defaultValue) {
1366        try {
1367            return getLockSettings().getLong(secureSettingKey, defaultValue,
1368                    getCurrentOrCallingUserId());
1369        } catch (RemoteException re) {
1370            return defaultValue;
1371        }
1372    }
1373
1374    private void setLong(String secureSettingKey, long value) {
1375        setLong(secureSettingKey, value, getCurrentOrCallingUserId());
1376    }
1377
1378    private void setLong(String secureSettingKey, long value, int userHandle) {
1379        try {
1380            getLockSettings().setLong(secureSettingKey, value, userHandle);
1381        } catch (RemoteException re) {
1382            // What can we do?
1383            Log.e(TAG, "Couldn't write long " + secureSettingKey + re);
1384        }
1385    }
1386
1387    private String getString(String secureSettingKey) {
1388        return getString(secureSettingKey, getCurrentOrCallingUserId());
1389    }
1390
1391    private String getString(String secureSettingKey, int userHandle) {
1392        try {
1393            return getLockSettings().getString(secureSettingKey, null, userHandle);
1394        } catch (RemoteException re) {
1395            return null;
1396        }
1397    }
1398
1399    private void setString(String secureSettingKey, String value, int userHandle) {
1400        try {
1401            getLockSettings().setString(secureSettingKey, value, userHandle);
1402        } catch (RemoteException re) {
1403            // What can we do?
1404            Log.e(TAG, "Couldn't write string " + secureSettingKey + re);
1405        }
1406    }
1407
1408    public boolean isSecure() {
1409        long mode = getKeyguardStoredPasswordQuality();
1410        final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1411        final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1412                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
1413                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1414                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1415                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1416        final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
1417                || isPassword && savedPasswordExists();
1418        return secure;
1419    }
1420
1421    /**
1422     * Sets the emergency button visibility based on isEmergencyCallCapable().
1423     *
1424     * If the emergency button is visible, sets the text on the emergency button
1425     * to indicate what action will be taken.
1426     *
1427     * If there's currently a call in progress, the button will take them to the call
1428     * @param button The button to update
1429     * @param shown Indicates whether the given screen wants the emergency button to show at all
1430     * @param showIcon Indicates whether to show a phone icon for the button.
1431     */
1432    public void updateEmergencyCallButtonState(Button button, boolean shown, boolean showIcon) {
1433        if (isEmergencyCallCapable() && shown) {
1434            button.setVisibility(View.VISIBLE);
1435        } else {
1436            button.setVisibility(View.GONE);
1437            return;
1438        }
1439
1440        int textId;
1441        if (getTelecommManager().isInCall()) {
1442            // show "return to call" text and show phone icon
1443            textId = R.string.lockscreen_return_to_call;
1444            int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0;
1445            button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
1446        } else {
1447            textId = R.string.lockscreen_emergency_call;
1448            int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0;
1449            button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
1450        }
1451        button.setText(textId);
1452    }
1453
1454    /**
1455     * Resumes a call in progress. Typically launched from the EmergencyCall button
1456     * on various lockscreens.
1457     */
1458    public void resumeCall() {
1459        getTelecommManager().showInCallScreen(false);
1460    }
1461
1462    private TelecommManager getTelecommManager() {
1463        return (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
1464    }
1465
1466    private void finishBiometricWeak() {
1467        setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
1468
1469        // Launch intent to show final screen, this also
1470        // moves the temporary gallery to the actual gallery
1471        Intent intent = new Intent();
1472        intent.setClassName("com.android.facelock",
1473                "com.android.facelock.SetupEndScreen");
1474        mContext.startActivity(intent);
1475    }
1476
1477    public void setPowerButtonInstantlyLocks(boolean enabled) {
1478        setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled);
1479    }
1480
1481    public boolean getPowerButtonInstantlyLocks() {
1482        return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
1483    }
1484
1485    public static boolean isSafeModeEnabled() {
1486        try {
1487            return IWindowManager.Stub.asInterface(
1488                    ServiceManager.getService("window")).isSafeModeEnabled();
1489        } catch (RemoteException e) {
1490            // Shouldn't happen!
1491        }
1492        return false;
1493    }
1494
1495    /**
1496     * Determine whether the user has selected any non-system widgets in keyguard
1497     *
1498     * @return true if widgets have been selected
1499     */
1500    public boolean hasWidgetsEnabledInKeyguard(int userid) {
1501        int widgets[] = getAppWidgets(userid);
1502        for (int i = 0; i < widgets.length; i++) {
1503            if (widgets[i] > 0) {
1504                return true;
1505            }
1506        }
1507        return false;
1508    }
1509
1510    public boolean getWidgetsEnabled() {
1511        return getWidgetsEnabled(getCurrentOrCallingUserId());
1512    }
1513
1514    public boolean getWidgetsEnabled(int userId) {
1515        return getBoolean(LOCKSCREEN_WIDGETS_ENABLED, false, userId);
1516    }
1517
1518    public void setWidgetsEnabled(boolean enabled) {
1519        setWidgetsEnabled(enabled, getCurrentOrCallingUserId());
1520    }
1521
1522    public void setWidgetsEnabled(boolean enabled, int userId) {
1523        setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId);
1524    }
1525
1526    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) {
1527        setEnabledTrustAgents(activeTrustAgents, getCurrentOrCallingUserId());
1528    }
1529
1530    public List<ComponentName> getEnabledTrustAgents() {
1531        return getEnabledTrustAgents(getCurrentOrCallingUserId());
1532    }
1533
1534    public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
1535        StringBuilder sb = new StringBuilder();
1536        for (ComponentName cn : activeTrustAgents) {
1537            if (sb.length() > 0) {
1538                sb.append(',');
1539            }
1540            sb.append(cn.flattenToShortString());
1541        }
1542        setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
1543        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
1544    }
1545
1546    public List<ComponentName> getEnabledTrustAgents(int userId) {
1547        String serialized = getString(ENABLED_TRUST_AGENTS, userId);
1548        if (TextUtils.isEmpty(serialized)) {
1549            return null;
1550        }
1551        String[] split = serialized.split(",");
1552        ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
1553        for (String s : split) {
1554            if (!TextUtils.isEmpty(s)) {
1555                activeTrustAgents.add(ComponentName.unflattenFromString(s));
1556            }
1557        }
1558        return activeTrustAgents;
1559    }
1560
1561    /**
1562     * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int)
1563     */
1564    public void requireCredentialEntry(int userId) {
1565        getTrustManager().reportRequireCredentialEntry(userId);
1566    }
1567
1568    private void onAfterChangingPassword() {
1569        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
1570    }
1571}
1572