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