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