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