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