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