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