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