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