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