• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getDevicePolicyManager()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      */
LockPatternUtils(Context context)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 
getRequestedMinimumPasswordLength()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      */
getRequestedPasswordQuality()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      */
reportFailedPasswordAttempt()171     public void reportFailedPasswordAttempt() {
172         getDevicePolicyManager().reportFailedPasswordAttempt();
173     }
174 
reportSuccessfulPasswordAttempt()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      */
checkPattern(List<LockPatternView.Cell> pattern)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      */
checkPassword(String password)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      */
savedPatternExists()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      */
savedPasswordExists()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      */
isPatternEverChosen()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      */
getActivePasswordQuality()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      */
clearLock()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      */
saveLockPattern(List<LockPatternView.Cell> pattern)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      */
computePasswordQuality(String password)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      */
saveLockPassword(String password, int quality)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      */
getKeyguardStoredPasswordQuality()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      */
stringToPattern(String string)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      */
patternToString(List<LockPatternView.Cell> pattern)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      */
patternToHash(List<LockPatternView.Cell> pattern)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 
getSalt()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      */
passwordToHash(String password)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 
toHex(byte[] ary)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      */
isLockPasswordEnabled()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      */
isLockPatternEnabled()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      */
setLockPatternEnabled(boolean enabled)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      */
isVisiblePatternEnabled()556     public boolean isVisiblePatternEnabled() {
557         return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE);
558     }
559 
560     /**
561      * Set whether the visible pattern is enabled.
562      */
setVisiblePatternEnabled(boolean enabled)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      */
isTactileFeedbackEnabled()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      */
setTactileFeedbackEnabled(boolean enabled)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      */
setLockoutAttemptDeadline()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      */
getLockoutAttemptDeadline()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      */
isPermanentlyLocked()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      */
setPermanentlyLocked(boolean locked)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      */
getNextAlarm()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 
getBoolean(String secureSettingKey)640     private boolean getBoolean(String secureSettingKey) {
641         return 1 ==
642                 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
643     }
644 
setBoolean(String secureSettingKey, boolean enabled)645     private void setBoolean(String secureSettingKey, boolean enabled) {
646         android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
647                                                 enabled ? 1 : 0);
648     }
649 
getLong(String secureSettingKey, long def)650     private long getLong(String secureSettingKey, long def) {
651         return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
652     }
653 
setLong(String secureSettingKey, long value)654     private void setLong(String secureSettingKey, long value) {
655         android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
656     }
657 
isSecure()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      */
updateEmergencyCallButtonState(Button button)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      */
resumeCall()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