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