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