Home | History | Annotate | Download | only in widget
      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 
     17 package com.android.internal.widget;
     18 
     19 import android.app.admin.DevicePolicyManager;
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.os.RemoteException;
     23 import android.os.ServiceManager;
     24 import android.os.SystemClock;
     25 import android.provider.Settings;
     26 import android.security.MessageDigest;
     27 import android.telephony.TelephonyManager;
     28 import android.text.TextUtils;
     29 import android.util.Log;
     30 import android.widget.Button;
     31 
     32 import com.android.internal.R;
     33 import com.android.internal.telephony.ITelephony;
     34 import com.google.android.collect.Lists;
     35 
     36 import java.io.FileNotFoundException;
     37 import java.io.IOException;
     38 import java.io.RandomAccessFile;
     39 import java.security.NoSuchAlgorithmException;
     40 import java.security.SecureRandom;
     41 import java.util.Arrays;
     42 import java.util.List;
     43 
     44 /**
     45  * Utilities for the lock patten and its settings.
     46  */
     47 public class LockPatternUtils {
     48 
     49     private static final String TAG = "LockPatternUtils";
     50 
     51     private static final String LOCK_PATTERN_FILE = "/system/gesture.key";
     52     private static final String LOCK_PASSWORD_FILE = "/system/password.key";
     53 
     54     /**
     55      * The maximum number of incorrect attempts before the user is prevented
     56      * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
     57      */
     58     public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
     59 
     60     /**
     61      * The number of incorrect attempts before which we fall back on an alternative
     62      * method of verifying the user, and resetting their lock pattern.
     63      */
     64     public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
     65 
     66     /**
     67      * How long the user is prevented from trying again after entering the
     68      * wrong pattern too many times.
     69      */
     70     public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
     71 
     72     /**
     73      * The interval of the countdown for showing progress of the lockout.
     74      */
     75     public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
     76 
     77     /**
     78      * The minimum number of dots in a valid pattern.
     79      */
     80     public static final int MIN_LOCK_PATTERN_SIZE = 4;
     81 
     82     /**
     83      * The minimum number of dots the user must include in a wrong pattern
     84      * attempt for it to be counted against the counts that affect
     85      * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
     86      */
     87     public static final int MIN_PATTERN_REGISTER_FAIL = 3;
     88 
     89     private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
     90     private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
     91     private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
     92     public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
     93     private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
     94 
     95     private final Context mContext;
     96     private final ContentResolver mContentResolver;
     97     private DevicePolicyManager mDevicePolicyManager;
     98     private static String sLockPatternFilename;
     99     private static String sLockPasswordFilename;
    100 
    101     public DevicePolicyManager getDevicePolicyManager() {
    102         if (mDevicePolicyManager == null) {
    103             mDevicePolicyManager =
    104                 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
    105             if (mDevicePolicyManager == null) {
    106                 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
    107                         new IllegalStateException("Stack trace:"));
    108             }
    109         }
    110         return mDevicePolicyManager;
    111     }
    112     /**
    113      * @param contentResolver Used to look up and save settings.
    114      */
    115     public LockPatternUtils(Context context) {
    116         mContext = context;
    117         mContentResolver = context.getContentResolver();
    118         // Initialize the location of gesture lock file
    119         if (sLockPatternFilename == null) {
    120             sLockPatternFilename = android.os.Environment.getDataDirectory()
    121                     .getAbsolutePath() + LOCK_PATTERN_FILE;
    122             sLockPasswordFilename = android.os.Environment.getDataDirectory()
    123                     .getAbsolutePath() + LOCK_PASSWORD_FILE;
    124         }
    125 
    126     }
    127 
    128     public int getRequestedMinimumPasswordLength() {
    129         return getDevicePolicyManager().getPasswordMinimumLength(null);
    130     }
    131 
    132 
    133     /**
    134      * Gets the device policy password mode. If the mode is non-specific, returns
    135      * MODE_PATTERN which allows the user to choose anything.
    136      */
    137     public int getRequestedPasswordQuality() {
    138         return getDevicePolicyManager().getPasswordQuality(null);
    139     }
    140 
    141     /**
    142      * Returns the actual password mode, as set by keyguard after updating the password.
    143      *
    144      * @return
    145      */
    146     public void reportFailedPasswordAttempt() {
    147         getDevicePolicyManager().reportFailedPasswordAttempt();
    148     }
    149 
    150     public void reportSuccessfulPasswordAttempt() {
    151         getDevicePolicyManager().reportSuccessfulPasswordAttempt();
    152     }
    153 
    154     /**
    155      * Check to see if a pattern matches the saved pattern.  If no pattern exists,
    156      * always returns true.
    157      * @param pattern The pattern to check.
    158      * @return Whether the pattern matches the stored one.
    159      */
    160     public boolean checkPattern(List<LockPatternView.Cell> pattern) {
    161         try {
    162             // Read all the bytes from the file
    163             RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
    164             final byte[] stored = new byte[(int) raf.length()];
    165             int got = raf.read(stored, 0, stored.length);
    166             raf.close();
    167             if (got <= 0) {
    168                 return true;
    169             }
    170             // Compare the hash from the file with the entered pattern's hash
    171             return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern));
    172         } catch (FileNotFoundException fnfe) {
    173             return true;
    174         } catch (IOException ioe) {
    175             return true;
    176         }
    177     }
    178 
    179     /**
    180      * Check to see if a password matches the saved password.  If no password exists,
    181      * always returns true.
    182      * @param password The password to check.
    183      * @return Whether the password matches the stored one.
    184      */
    185     public boolean checkPassword(String password) {
    186         try {
    187             // Read all the bytes from the file
    188             RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
    189             final byte[] stored = new byte[(int) raf.length()];
    190             int got = raf.read(stored, 0, stored.length);
    191             raf.close();
    192             if (got <= 0) {
    193                 return true;
    194             }
    195             // Compare the hash from the file with the entered password's hash
    196             return Arrays.equals(stored, passwordToHash(password));
    197         } catch (FileNotFoundException fnfe) {
    198             return true;
    199         } catch (IOException ioe) {
    200             return true;
    201         }
    202     }
    203 
    204     /**
    205      * Checks to see if the given file exists and contains any data. Returns true if it does,
    206      * false otherwise.
    207      * @param filename
    208      * @return true if file exists and is non-empty.
    209      */
    210     private boolean nonEmptyFileExists(String filename) {
    211         try {
    212             // Check if we can read a byte from the file
    213             RandomAccessFile raf = new RandomAccessFile(filename, "r");
    214             raf.readByte();
    215             raf.close();
    216             return true;
    217         } catch (FileNotFoundException fnfe) {
    218             return false;
    219         } catch (IOException ioe) {
    220             return false;
    221         }
    222     }
    223 
    224     /**
    225      * Check to see if the user has stored a lock pattern.
    226      * @return Whether a saved pattern exists.
    227      */
    228     public boolean savedPatternExists() {
    229         return nonEmptyFileExists(sLockPatternFilename);
    230     }
    231 
    232     /**
    233      * Check to see if the user has stored a lock pattern.
    234      * @return Whether a saved pattern exists.
    235      */
    236     public boolean savedPasswordExists() {
    237         return nonEmptyFileExists(sLockPasswordFilename);
    238     }
    239 
    240     /**
    241      * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
    242      * currently cleared.
    243      *
    244      * @return True if the user has ever chosen a pattern.
    245      */
    246     public boolean isPatternEverChosen() {
    247         return getBoolean(PATTERN_EVER_CHOSEN_KEY);
    248     }
    249 
    250     /**
    251      * Used by device policy manager to validate the current password
    252      * information it has.
    253      */
    254     public int getActivePasswordQuality() {
    255         int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
    256         switch (getKeyguardStoredPasswordQuality()) {
    257             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
    258                 if (isLockPatternEnabled()) {
    259                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
    260                 }
    261                 break;
    262             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
    263                 if (isLockPasswordEnabled()) {
    264                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
    265                 }
    266                 break;
    267             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
    268                 if (isLockPasswordEnabled()) {
    269                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
    270                 }
    271                 break;
    272             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
    273                 if (isLockPasswordEnabled()) {
    274                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
    275                 }
    276                 break;
    277         }
    278         return activePasswordQuality;
    279     }
    280 
    281     /**
    282      * Clear any lock pattern or password.
    283      */
    284     public void clearLock() {
    285         getDevicePolicyManager().setActivePasswordState(
    286                 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
    287         saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
    288         setLockPatternEnabled(false);
    289         saveLockPattern(null);
    290         setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
    291     }
    292 
    293     /**
    294      * Save a lock pattern.
    295      * @param pattern The new pattern to save.
    296      */
    297     public void saveLockPattern(List<LockPatternView.Cell> pattern) {
    298         // Compute the hash
    299         final byte[] hash  = LockPatternUtils.patternToHash(pattern);
    300         try {
    301             // Write the hash to file
    302             RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw");
    303             // Truncate the file if pattern is null, to clear the lock
    304             if (pattern == null) {
    305                 raf.setLength(0);
    306             } else {
    307                 raf.write(hash, 0, hash.length);
    308             }
    309             raf.close();
    310             DevicePolicyManager dpm = getDevicePolicyManager();
    311             if (pattern != null) {
    312                 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
    313                 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
    314                 dpm.setActivePasswordState(
    315                         DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size());
    316             } else {
    317                 dpm.setActivePasswordState(
    318                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
    319             }
    320         } catch (FileNotFoundException fnfe) {
    321             // Cant do much, unless we want to fail over to using the settings provider
    322             Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
    323         } catch (IOException ioe) {
    324             // Cant do much
    325             Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
    326         }
    327     }
    328 
    329     /**
    330      * Compute the password quality from the given password string.
    331      */
    332     static public int computePasswordQuality(String password) {
    333         boolean hasDigit = false;
    334         boolean hasNonDigit = false;
    335         final int len = password.length();
    336         for (int i = 0; i < len; i++) {
    337             if (Character.isDigit(password.charAt(i))) {
    338                 hasDigit = true;
    339             } else {
    340                 hasNonDigit = true;
    341             }
    342         }
    343 
    344         if (hasNonDigit && hasDigit) {
    345             return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
    346         }
    347         if (hasNonDigit) {
    348             return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
    349         }
    350         if (hasDigit) {
    351             return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
    352         }
    353         return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
    354     }
    355 
    356     /**
    357      * Save a lock password.  Does not ensure that the password is as good
    358      * as the requested mode, but will adjust the mode to be as good as the
    359      * pattern.
    360      * @param password The password to save
    361      * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
    362      */
    363     public void saveLockPassword(String password, int quality) {
    364         // Compute the hash
    365         final byte[] hash = passwordToHash(password);
    366         try {
    367             // Write the hash to file
    368             RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
    369             // Truncate the file if pattern is null, to clear the lock
    370             if (password == null) {
    371                 raf.setLength(0);
    372             } else {
    373                 raf.write(hash, 0, hash.length);
    374             }
    375             raf.close();
    376             DevicePolicyManager dpm = getDevicePolicyManager();
    377             if (password != null) {
    378                 int computedQuality = computePasswordQuality(password);
    379                 setLong(PASSWORD_TYPE_KEY, computedQuality);
    380                 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
    381                     dpm.setActivePasswordState(computedQuality, password.length());
    382                 } else {
    383                     // The password is not anything.
    384                     dpm.setActivePasswordState(
    385                             DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
    386                 }
    387             } else {
    388                 dpm.setActivePasswordState(
    389                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
    390             }
    391         } catch (FileNotFoundException fnfe) {
    392             // Cant do much, unless we want to fail over to using the settings provider
    393             Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
    394         } catch (IOException ioe) {
    395             // Cant do much
    396             Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
    397         }
    398     }
    399 
    400     /**
    401      * Retrieves the quality mode we're in.
    402      * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
    403      *
    404      * @return stored password quality
    405      */
    406     public int getKeyguardStoredPasswordQuality() {
    407         return (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
    408     }
    409 
    410     /**
    411      * Deserialize a pattern.
    412      * @param string The pattern serialized with {@link #patternToString}
    413      * @return The pattern.
    414      */
    415     public static List<LockPatternView.Cell> stringToPattern(String string) {
    416         List<LockPatternView.Cell> result = Lists.newArrayList();
    417 
    418         final byte[] bytes = string.getBytes();
    419         for (int i = 0; i < bytes.length; i++) {
    420             byte b = bytes[i];
    421             result.add(LockPatternView.Cell.of(b / 3, b % 3));
    422         }
    423         return result;
    424     }
    425 
    426     /**
    427      * Serialize a pattern.
    428      * @param pattern The pattern.
    429      * @return The pattern in string form.
    430      */
    431     public static String patternToString(List<LockPatternView.Cell> pattern) {
    432         if (pattern == null) {
    433             return "";
    434         }
    435         final int patternSize = pattern.size();
    436 
    437         byte[] res = new byte[patternSize];
    438         for (int i = 0; i < patternSize; i++) {
    439             LockPatternView.Cell cell = pattern.get(i);
    440             res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
    441         }
    442         return new String(res);
    443     }
    444 
    445     /*
    446      * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
    447      * at least a second level of protection. First level is that the file
    448      * is in a location only readable by the system process.
    449      * @param pattern the gesture pattern.
    450      * @return the hash of the pattern in a byte array.
    451      */
    452     private static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
    453         if (pattern == null) {
    454             return null;
    455         }
    456 
    457         final int patternSize = pattern.size();
    458         byte[] res = new byte[patternSize];
    459         for (int i = 0; i < patternSize; i++) {
    460             LockPatternView.Cell cell = pattern.get(i);
    461             res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
    462         }
    463         try {
    464             MessageDigest md = MessageDigest.getInstance("SHA-1");
    465             byte[] hash = md.digest(res);
    466             return hash;
    467         } catch (NoSuchAlgorithmException nsa) {
    468             return res;
    469         }
    470     }
    471 
    472     private String getSalt() {
    473         long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0);
    474         if (salt == 0) {
    475             try {
    476                 salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
    477                 setLong(LOCK_PASSWORD_SALT_KEY, salt);
    478                 Log.v(TAG, "Initialized lock password salt");
    479             } catch (NoSuchAlgorithmException e) {
    480                 // Throw an exception rather than storing a password we'll never be able to recover
    481                 throw new IllegalStateException("Couldn't get SecureRandom number", e);
    482             }
    483         }
    484         return Long.toHexString(salt);
    485     }
    486 
    487     /*
    488      * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
    489      * Not the most secure, but it is at least a second level of protection. First level is that
    490      * the file is in a location only readable by the system process.
    491      * @param password the gesture pattern.
    492      * @return the hash of the pattern in a byte array.
    493      */
    494      public byte[] passwordToHash(String password) {
    495         if (password == null) {
    496             return null;
    497         }
    498         String algo = null;
    499         byte[] hashed = null;
    500         try {
    501             byte[] saltedPassword = (password + getSalt()).getBytes();
    502             byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
    503             byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
    504             hashed = (toHex(sha1) + toHex(md5)).getBytes();
    505         } catch (NoSuchAlgorithmException e) {
    506             Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
    507         }
    508         return hashed;
    509     }
    510 
    511     private static String toHex(byte[] ary) {
    512         final String hex = "0123456789ABCDEF";
    513         String ret = "";
    514         for (int i = 0; i < ary.length; i++) {
    515             ret += hex.charAt((ary[i] >> 4) & 0xf);
    516             ret += hex.charAt(ary[i] & 0xf);
    517         }
    518         return ret;
    519     }
    520 
    521     /**
    522      * @return Whether the lock password is enabled.
    523      */
    524     public boolean isLockPasswordEnabled() {
    525         long mode = getLong(PASSWORD_TYPE_KEY, 0);
    526         return savedPasswordExists() &&
    527                 (mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
    528                         || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
    529                         || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
    530     }
    531 
    532     /**
    533      * @return Whether the lock pattern is enabled.
    534      */
    535     public boolean isLockPatternEnabled() {
    536         return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED)
    537                 && getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
    538                         == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
    539     }
    540 
    541     /**
    542      * Set whether the lock pattern is enabled.
    543      */
    544     public void setLockPatternEnabled(boolean enabled) {
    545         setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
    546     }
    547 
    548     /**
    549      * @return Whether the visible pattern is enabled.
    550      */
    551     public boolean isVisiblePatternEnabled() {
    552         return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE);
    553     }
    554 
    555     /**
    556      * Set whether the visible pattern is enabled.
    557      */
    558     public void setVisiblePatternEnabled(boolean enabled) {
    559         setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
    560     }
    561 
    562     /**
    563      * @return Whether tactile feedback for the pattern is enabled.
    564      */
    565     public boolean isTactileFeedbackEnabled() {
    566         return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
    567     }
    568 
    569     /**
    570      * Set whether tactile feedback for the pattern is enabled.
    571      */
    572     public void setTactileFeedbackEnabled(boolean enabled) {
    573         setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled);
    574     }
    575 
    576     /**
    577      * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
    578      * pattern until the deadline has passed.
    579      * @return the chosen deadline.
    580      */
    581     public long setLockoutAttemptDeadline() {
    582         final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
    583         setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
    584         return deadline;
    585     }
    586 
    587     /**
    588      * @return The elapsed time in millis in the future when the user is allowed to
    589      *   attempt to enter his/her lock pattern, or 0 if the user is welcome to
    590      *   enter a pattern.
    591      */
    592     public long getLockoutAttemptDeadline() {
    593         final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
    594         final long now = SystemClock.elapsedRealtime();
    595         if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
    596             return 0L;
    597         }
    598         return deadline;
    599     }
    600 
    601     /**
    602      * @return Whether the user is permanently locked out until they verify their
    603      *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
    604      *   attempts.
    605      */
    606     public boolean isPermanentlyLocked() {
    607         return getBoolean(LOCKOUT_PERMANENT_KEY);
    608     }
    609 
    610     /**
    611      * Set the state of whether the device is permanently locked, meaning the user
    612      * must authenticate via other means.
    613      *
    614      * @param locked Whether the user is permanently locked out until they verify their
    615      *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
    616      *   attempts.
    617      */
    618     public void setPermanentlyLocked(boolean locked) {
    619         setBoolean(LOCKOUT_PERMANENT_KEY, locked);
    620     }
    621 
    622     /**
    623      * @return A formatted string of the next alarm (for showing on the lock screen),
    624      *   or null if there is no next alarm.
    625      */
    626     public String getNextAlarm() {
    627         String nextAlarm = Settings.System.getString(mContentResolver,
    628                 Settings.System.NEXT_ALARM_FORMATTED);
    629         if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
    630             return null;
    631         }
    632         return nextAlarm;
    633     }
    634 
    635     private boolean getBoolean(String secureSettingKey) {
    636         return 1 ==
    637                 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
    638     }
    639 
    640     private void setBoolean(String secureSettingKey, boolean enabled) {
    641         android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
    642                                                 enabled ? 1 : 0);
    643     }
    644 
    645     private long getLong(String secureSettingKey, long def) {
    646         return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
    647     }
    648 
    649     private void setLong(String secureSettingKey, long value) {
    650         android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
    651     }
    652 
    653     public boolean isSecure() {
    654         long mode = getKeyguardStoredPasswordQuality();
    655         final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
    656         final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
    657                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
    658                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
    659         final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
    660                 || isPassword && savedPasswordExists();
    661         return secure;
    662     }
    663 
    664     /**
    665      * Sets the text on the emergency button to indicate what action will be taken.
    666      * If there's currently a call in progress, the button will take them to the call
    667      * @param button the button to update
    668      */
    669     public void updateEmergencyCallButtonState(Button button) {
    670         int newState = TelephonyManager.getDefault().getCallState();
    671         int textId;
    672         if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
    673             // show "return to call" text and show phone icon
    674             textId = R.string.lockscreen_return_to_call;
    675             int phoneCallIcon = R.drawable.stat_sys_phone_call;
    676             button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
    677         } else {
    678             textId = R.string.lockscreen_emergency_call;
    679             int emergencyIcon = R.drawable.ic_emergency;
    680             button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
    681         }
    682         button.setText(textId);
    683     }
    684 
    685     /**
    686      * Resumes a call in progress. Typically launched from the EmergencyCall button
    687      * on various lockscreens.
    688      *
    689      * @return true if we were able to tell InCallScreen to show.
    690      */
    691     public boolean resumeCall() {
    692         ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
    693         try {
    694             if (phone != null && phone.showCallScreen()) {
    695                 return true;
    696             }
    697         } catch (RemoteException e) {
    698             // What can we do?
    699         }
    700         return false;
    701     }
    702 }
    703