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