• 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 com.android.internal.R;
20 import com.android.internal.telephony.ITelephony;
21 import com.google.android.collect.Lists;
22 
23 import android.app.admin.DevicePolicyManager;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.hardware.Camera;
29 import android.hardware.Camera.CameraInfo;
30 import android.os.FileObserver;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.SystemClock;
35 import android.os.SystemProperties;
36 import android.os.storage.IMountService;
37 import android.provider.Settings;
38 import android.security.KeyStore;
39 import android.telephony.TelephonyManager;
40 import android.text.TextUtils;
41 import android.util.Log;
42 import android.view.View;
43 import android.widget.Button;
44 import android.widget.TextView;
45 
46 import java.io.File;
47 import java.io.FileNotFoundException;
48 import java.io.IOException;
49 import java.io.RandomAccessFile;
50 import java.security.MessageDigest;
51 import java.security.NoSuchAlgorithmException;
52 import java.security.SecureRandom;
53 import java.util.Arrays;
54 import java.util.List;
55 import java.util.concurrent.atomic.AtomicBoolean;
56 
57 /**
58  * Utilities for the lock pattern and its settings.
59  */
60 public class LockPatternUtils {
61 
62     private static final String OPTION_ENABLE_FACELOCK = "enable_facelock";
63 
64     private static final String TAG = "LockPatternUtils";
65 
66     private static final String SYSTEM_DIRECTORY = "/system/";
67     private static final String LOCK_PATTERN_FILE = "gesture.key";
68     private static final String LOCK_PASSWORD_FILE = "password.key";
69 
70     /**
71      * The maximum number of incorrect attempts before the user is prevented
72      * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
73      */
74     public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
75 
76     /**
77      * The number of incorrect attempts before which we fall back on an alternative
78      * method of verifying the user, and resetting their lock pattern.
79      */
80     public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
81 
82     /**
83      * How long the user is prevented from trying again after entering the
84      * wrong pattern too many times.
85      */
86     public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
87 
88     /**
89      * The interval of the countdown for showing progress of the lockout.
90      */
91     public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
92 
93 
94     /**
95      * This dictates when we start telling the user that continued failed attempts will wipe
96      * their device.
97      */
98     public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5;
99 
100     /**
101      * The minimum number of dots in a valid pattern.
102      */
103     public static final int MIN_LOCK_PATTERN_SIZE = 4;
104 
105     /**
106      * The minimum number of dots the user must include in a wrong pattern
107      * attempt for it to be counted against the counts that affect
108      * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
109      */
110     public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
111 
112     private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
113     private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
114     private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
115     public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
116     public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
117     private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
118     private final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
119     private final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
120     public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
121             = "lockscreen.biometric_weak_fallback";
122     public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
123             = "lockscreen.biometricweakeverchosen";
124 
125     private final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
126 
127     private final Context mContext;
128     private final ContentResolver mContentResolver;
129     private DevicePolicyManager mDevicePolicyManager;
130     private static String sLockPatternFilename;
131     private static String sLockPasswordFilename;
132 
133     private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false);
134     private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false);
135 
136     private static FileObserver sPasswordObserver;
137 
138     private static class PasswordFileObserver extends FileObserver {
PasswordFileObserver(String path, int mask)139         public PasswordFileObserver(String path, int mask) {
140             super(path, mask);
141         }
142 
143         @Override
onEvent(int event, String path)144         public void onEvent(int event, String path) {
145             if (LOCK_PATTERN_FILE.equals(path)) {
146                 Log.d(TAG, "lock pattern file changed");
147                 sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
148             } else if (LOCK_PASSWORD_FILE.equals(path)) {
149                 Log.d(TAG, "lock password file changed");
150                 sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
151             }
152         }
153     }
154 
getDevicePolicyManager()155     public DevicePolicyManager getDevicePolicyManager() {
156         if (mDevicePolicyManager == null) {
157             mDevicePolicyManager =
158                 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
159             if (mDevicePolicyManager == null) {
160                 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
161                         new IllegalStateException("Stack trace:"));
162             }
163         }
164         return mDevicePolicyManager;
165     }
166     /**
167      * @param contentResolver Used to look up and save settings.
168      */
LockPatternUtils(Context context)169     public LockPatternUtils(Context context) {
170         mContext = context;
171         mContentResolver = context.getContentResolver();
172 
173         // Initialize the location of gesture & PIN lock files
174         if (sLockPatternFilename == null) {
175             String dataSystemDirectory =
176                     android.os.Environment.getDataDirectory().getAbsolutePath() +
177                     SYSTEM_DIRECTORY;
178             sLockPatternFilename =  dataSystemDirectory + LOCK_PATTERN_FILE;
179             sLockPasswordFilename = dataSystemDirectory + LOCK_PASSWORD_FILE;
180             sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
181             sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
182             int fileObserverMask = FileObserver.CLOSE_WRITE | FileObserver.DELETE |
183                     FileObserver.MOVED_TO | FileObserver.CREATE;
184             sPasswordObserver = new PasswordFileObserver(dataSystemDirectory, fileObserverMask);
185             sPasswordObserver.startWatching();
186         }
187     }
188 
getRequestedMinimumPasswordLength()189     public int getRequestedMinimumPasswordLength() {
190         return getDevicePolicyManager().getPasswordMinimumLength(null);
191     }
192 
193 
194     /**
195      * Gets the device policy password mode. If the mode is non-specific, returns
196      * MODE_PATTERN which allows the user to choose anything.
197      */
getRequestedPasswordQuality()198     public int getRequestedPasswordQuality() {
199         return getDevicePolicyManager().getPasswordQuality(null);
200     }
201 
getRequestedPasswordHistoryLength()202     public int getRequestedPasswordHistoryLength() {
203         return getDevicePolicyManager().getPasswordHistoryLength(null);
204     }
205 
getRequestedPasswordMinimumLetters()206     public int getRequestedPasswordMinimumLetters() {
207         return getDevicePolicyManager().getPasswordMinimumLetters(null);
208     }
209 
getRequestedPasswordMinimumUpperCase()210     public int getRequestedPasswordMinimumUpperCase() {
211         return getDevicePolicyManager().getPasswordMinimumUpperCase(null);
212     }
213 
getRequestedPasswordMinimumLowerCase()214     public int getRequestedPasswordMinimumLowerCase() {
215         return getDevicePolicyManager().getPasswordMinimumLowerCase(null);
216     }
217 
getRequestedPasswordMinimumNumeric()218     public int getRequestedPasswordMinimumNumeric() {
219         return getDevicePolicyManager().getPasswordMinimumNumeric(null);
220     }
221 
getRequestedPasswordMinimumSymbols()222     public int getRequestedPasswordMinimumSymbols() {
223         return getDevicePolicyManager().getPasswordMinimumSymbols(null);
224     }
225 
getRequestedPasswordMinimumNonLetter()226     public int getRequestedPasswordMinimumNonLetter() {
227         return getDevicePolicyManager().getPasswordMinimumNonLetter(null);
228     }
229     /**
230      * Returns the actual password mode, as set by keyguard after updating the password.
231      *
232      * @return
233      */
reportFailedPasswordAttempt()234     public void reportFailedPasswordAttempt() {
235         getDevicePolicyManager().reportFailedPasswordAttempt();
236     }
237 
reportSuccessfulPasswordAttempt()238     public void reportSuccessfulPasswordAttempt() {
239         getDevicePolicyManager().reportSuccessfulPasswordAttempt();
240     }
241 
242     /**
243      * Check to see if a pattern matches the saved pattern.  If no pattern exists,
244      * always returns true.
245      * @param pattern The pattern to check.
246      * @return Whether the pattern matches the stored one.
247      */
checkPattern(List<LockPatternView.Cell> pattern)248     public boolean checkPattern(List<LockPatternView.Cell> pattern) {
249         try {
250             // Read all the bytes from the file
251             RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
252             final byte[] stored = new byte[(int) raf.length()];
253             int got = raf.read(stored, 0, stored.length);
254             raf.close();
255             if (got <= 0) {
256                 return true;
257             }
258             // Compare the hash from the file with the entered pattern's hash
259             return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern));
260         } catch (FileNotFoundException fnfe) {
261             return true;
262         } catch (IOException ioe) {
263             return true;
264         }
265     }
266 
267     /**
268      * Check to see if a password matches the saved password.  If no password exists,
269      * always returns true.
270      * @param password The password to check.
271      * @return Whether the password matches the stored one.
272      */
checkPassword(String password)273     public boolean checkPassword(String password) {
274         try {
275             // Read all the bytes from the file
276             RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
277             final byte[] stored = new byte[(int) raf.length()];
278             int got = raf.read(stored, 0, stored.length);
279             raf.close();
280             if (got <= 0) {
281                 return true;
282             }
283             // Compare the hash from the file with the entered password's hash
284             return Arrays.equals(stored, passwordToHash(password));
285         } catch (FileNotFoundException fnfe) {
286             return true;
287         } catch (IOException ioe) {
288             return true;
289         }
290     }
291 
292     /**
293      * Check to see if a password matches any of the passwords stored in the
294      * password history.
295      *
296      * @param password The password to check.
297      * @return Whether the password matches any in the history.
298      */
checkPasswordHistory(String password)299     public boolean checkPasswordHistory(String password) {
300         String passwordHashString = new String(passwordToHash(password));
301         String passwordHistory = getString(PASSWORD_HISTORY_KEY);
302         if (passwordHistory == null) {
303             return false;
304         }
305         // Password History may be too long...
306         int passwordHashLength = passwordHashString.length();
307         int passwordHistoryLength = getRequestedPasswordHistoryLength();
308         if(passwordHistoryLength == 0) {
309             return false;
310         }
311         int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
312                 + passwordHistoryLength - 1;
313         if (passwordHistory.length() > neededPasswordHistoryLength) {
314             passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
315         }
316         return passwordHistory.contains(passwordHashString);
317     }
318 
319     /**
320      * Check to see if the user has stored a lock pattern.
321      * @return Whether a saved pattern exists.
322      */
savedPatternExists()323     public boolean savedPatternExists() {
324         return sHaveNonZeroPatternFile.get();
325     }
326 
327     /**
328      * Check to see if the user has stored a lock pattern.
329      * @return Whether a saved pattern exists.
330      */
savedPasswordExists()331     public boolean savedPasswordExists() {
332         return sHaveNonZeroPasswordFile.get();
333     }
334 
335     /**
336      * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
337      * currently cleared.
338      *
339      * @return True if the user has ever chosen a pattern.
340      */
isPatternEverChosen()341     public boolean isPatternEverChosen() {
342         return getBoolean(PATTERN_EVER_CHOSEN_KEY);
343     }
344 
345     /**
346      * Return true if the user has ever chosen biometric weak.  This is true even if biometric
347      * weak is not current set.
348      *
349      * @return True if the user has ever chosen biometric weak.
350      */
isBiometricWeakEverChosen()351     public boolean isBiometricWeakEverChosen() {
352         return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY);
353     }
354 
355     /**
356      * Used by device policy manager to validate the current password
357      * information it has.
358      */
getActivePasswordQuality()359     public int getActivePasswordQuality() {
360         int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
361         // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to
362         // return biometric_weak if that is being used instead of the backup
363         int quality =
364                 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
365         switch (quality) {
366             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
367                 if (isLockPatternEnabled()) {
368                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
369                 }
370                 break;
371             case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK:
372                 if (isBiometricWeakInstalled()) {
373                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
374                 }
375                 break;
376             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
377                 if (isLockPasswordEnabled()) {
378                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
379                 }
380                 break;
381             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
382                 if (isLockPasswordEnabled()) {
383                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
384                 }
385                 break;
386             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
387                 if (isLockPasswordEnabled()) {
388                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
389                 }
390                 break;
391             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
392                 if (isLockPasswordEnabled()) {
393                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
394                 }
395                 break;
396         }
397 
398         return activePasswordQuality;
399     }
400 
401     /**
402      * Clear any lock pattern or password.
403      */
clearLock(boolean isFallback)404     public void clearLock(boolean isFallback) {
405         if(!isFallback) deleteGallery();
406         saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
407         setLockPatternEnabled(false);
408         saveLockPattern(null);
409         setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
410         setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
411     }
412 
413     /**
414      * Disable showing lock screen at all when the DevicePolicyManager allows it.
415      * This is only meaningful if pattern, pin or password are not set.
416      *
417      * @param disable Disables lock screen when true
418      */
setLockScreenDisabled(boolean disable)419     public void setLockScreenDisabled(boolean disable) {
420         setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0);
421     }
422 
423     /**
424      * Determine if LockScreen can be disabled. This is used, for example, to tell if we should
425      * show LockScreen or go straight to the home screen.
426      *
427      * @return true if lock screen is can be disabled
428      */
isLockScreenDisabled()429     public boolean isLockScreenDisabled() {
430         return !isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0;
431     }
432 
433     /**
434      * Save a lock pattern.
435      * @param pattern The new pattern to save.
436      */
saveLockPattern(List<LockPatternView.Cell> pattern)437     public void saveLockPattern(List<LockPatternView.Cell> pattern) {
438         this.saveLockPattern(pattern, false);
439     }
440 
441     /**
442      * Calls back SetupFaceLock to save the temporary gallery file if this is the backup lock.
443      * This doesn't have to verify that biometric is enabled because it's only called in that case
444     */
moveTempGallery()445     void moveTempGallery() {
446         Intent intent = new Intent().setClassName("com.android.facelock",
447                 "com.android.facelock.SetupFaceLock");
448         intent.putExtra("moveTempGallery", true);
449         mContext.startActivity(intent);
450     }
451 
452     /**
453      * Calls back SetupFaceLock to delete the temporary gallery file
454      */
deleteTempGallery()455     public void deleteTempGallery() {
456         Intent intent = new Intent().setClassName("com.android.facelock",
457                 "com.android.facelock.SetupFaceLock");
458         intent.putExtra("deleteTempGallery", true);
459         mContext.startActivity(intent);
460     }
461 
462     /**
463      * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
464     */
deleteGallery()465     void deleteGallery() {
466         if(usingBiometricWeak()) {
467             Intent intent = new Intent().setClassName("com.android.facelock",
468                     "com.android.facelock.SetupFaceLock");
469             intent.putExtra("deleteGallery", true);
470             mContext.startActivity(intent);
471         }
472     }
473 
474     /**
475      * Save a lock pattern.
476      * @param pattern The new pattern to save.
477      * @param isFallback Specifies if this is a fallback to biometric weak
478      */
saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback)479     public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
480         // Compute the hash
481         final byte[] hash = LockPatternUtils.patternToHash(pattern);
482         try {
483             // Write the hash to file
484             RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw");
485             // Truncate the file if pattern is null, to clear the lock
486             if (pattern == null) {
487                 raf.setLength(0);
488             } else {
489                 raf.write(hash, 0, hash.length);
490             }
491             raf.close();
492             DevicePolicyManager dpm = getDevicePolicyManager();
493             KeyStore keyStore = KeyStore.getInstance();
494             if (pattern != null) {
495                 keyStore.password(patternToString(pattern));
496                 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
497                 if (!isFallback) {
498                     deleteGallery();
499                     setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
500                 } else {
501                     setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
502                     setLong(PASSWORD_TYPE_ALTERNATE_KEY,
503                             DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
504                     setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
505                     moveTempGallery();
506                 }
507                 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
508                         .size(), 0, 0, 0, 0, 0, 0);
509             } else {
510                 if (keyStore.isEmpty()) {
511                     keyStore.reset();
512                 }
513                 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
514                         0, 0, 0, 0, 0);
515             }
516         } catch (FileNotFoundException fnfe) {
517             // Cant do much, unless we want to fail over to using the settings
518             // provider
519             Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
520         } catch (IOException ioe) {
521             // Cant do much
522             Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
523         }
524     }
525 
526     /**
527      * Compute the password quality from the given password string.
528      */
computePasswordQuality(String password)529     static public int computePasswordQuality(String password) {
530         boolean hasDigit = false;
531         boolean hasNonDigit = false;
532         final int len = password.length();
533         for (int i = 0; i < len; i++) {
534             if (Character.isDigit(password.charAt(i))) {
535                 hasDigit = true;
536             } else {
537                 hasNonDigit = true;
538             }
539         }
540 
541         if (hasNonDigit && hasDigit) {
542             return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
543         }
544         if (hasNonDigit) {
545             return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
546         }
547         if (hasDigit) {
548             return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
549         }
550         return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
551     }
552 
553     /** Update the encryption password if it is enabled **/
updateEncryptionPassword(String password)554     private void updateEncryptionPassword(String password) {
555         DevicePolicyManager dpm = getDevicePolicyManager();
556         if (dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
557             return;
558         }
559 
560         IBinder service = ServiceManager.getService("mount");
561         if (service == null) {
562             Log.e(TAG, "Could not find the mount service to update the encryption password");
563             return;
564         }
565 
566         IMountService mountService = IMountService.Stub.asInterface(service);
567         try {
568             mountService.changeEncryptionPassword(password);
569         } catch (RemoteException e) {
570             Log.e(TAG, "Error changing encryption password", e);
571         }
572     }
573 
574     /**
575      * Save a lock password.  Does not ensure that the password is as good
576      * as the requested mode, but will adjust the mode to be as good as the
577      * pattern.
578      * @param password The password to save
579      * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
580      */
saveLockPassword(String password, int quality)581     public void saveLockPassword(String password, int quality) {
582         this.saveLockPassword(password, quality, false);
583     }
584 
585     /**
586      * Save a lock password.  Does not ensure that the password is as good
587      * as the requested mode, but will adjust the mode to be as good as the
588      * pattern.
589      * @param password The password to save
590      * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
591      * @param isFallback Specifies if this is a fallback to biometric weak
592      */
saveLockPassword(String password, int quality, boolean isFallback)593     public void saveLockPassword(String password, int quality, boolean isFallback) {
594         // Compute the hash
595         final byte[] hash = passwordToHash(password);
596         try {
597             // Write the hash to file
598             RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
599             // Truncate the file if pattern is null, to clear the lock
600             if (password == null) {
601                 raf.setLength(0);
602             } else {
603                 raf.write(hash, 0, hash.length);
604             }
605             raf.close();
606             DevicePolicyManager dpm = getDevicePolicyManager();
607             KeyStore keyStore = KeyStore.getInstance();
608             if (password != null) {
609                 // Update the encryption password.
610                 updateEncryptionPassword(password);
611 
612                 // Update the keystore password
613                 keyStore.password(password);
614 
615                 int computedQuality = computePasswordQuality(password);
616                 if (!isFallback) {
617                     deleteGallery();
618                     setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality));
619                 } else {
620                     setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
621                     setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality));
622                     setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
623                     moveTempGallery();
624                 }
625                 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
626                     int letters = 0;
627                     int uppercase = 0;
628                     int lowercase = 0;
629                     int numbers = 0;
630                     int symbols = 0;
631                     int nonletter = 0;
632                     for (int i = 0; i < password.length(); i++) {
633                         char c = password.charAt(i);
634                         if (c >= 'A' && c <= 'Z') {
635                             letters++;
636                             uppercase++;
637                         } else if (c >= 'a' && c <= 'z') {
638                             letters++;
639                             lowercase++;
640                         } else if (c >= '0' && c <= '9') {
641                             numbers++;
642                             nonletter++;
643                         } else {
644                             symbols++;
645                             nonletter++;
646                         }
647                     }
648                     dpm.setActivePasswordState(Math.max(quality, computedQuality), password
649                             .length(), letters, uppercase, lowercase, numbers, symbols, nonletter);
650                 } else {
651                     // The password is not anything.
652                     dpm.setActivePasswordState(
653                             DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
654                 }
655                 // Add the password to the password history. We assume all
656                 // password
657                 // hashes have the same length for simplicity of implementation.
658                 String passwordHistory = getString(PASSWORD_HISTORY_KEY);
659                 if (passwordHistory == null) {
660                     passwordHistory = new String();
661                 }
662                 int passwordHistoryLength = getRequestedPasswordHistoryLength();
663                 if (passwordHistoryLength == 0) {
664                     passwordHistory = "";
665                 } else {
666                     passwordHistory = new String(hash) + "," + passwordHistory;
667                     // Cut it to contain passwordHistoryLength hashes
668                     // and passwordHistoryLength -1 commas.
669                     passwordHistory = passwordHistory.substring(0, Math.min(hash.length
670                             * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
671                             .length()));
672                 }
673                 setString(PASSWORD_HISTORY_KEY, passwordHistory);
674             } else {
675                 // Conditionally reset the keystore if empty. If
676                 // non-empty, we are just switching key guard type
677                 if (keyStore.isEmpty()) {
678                     keyStore.reset();
679                 }
680                 dpm.setActivePasswordState(
681                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
682             }
683         } catch (FileNotFoundException fnfe) {
684             // Cant do much, unless we want to fail over to using the settings provider
685             Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
686         } catch (IOException ioe) {
687             // Cant do much
688             Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
689         }
690     }
691 
692     /**
693      * Retrieves the quality mode we're in.
694      * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
695      *
696      * @return stored password quality
697      */
getKeyguardStoredPasswordQuality()698     public int getKeyguardStoredPasswordQuality() {
699         int quality =
700                 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
701         // If the user has chosen to use weak biometric sensor, then return the backup locking
702         // method and treat biometric as a special case.
703         if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
704             quality =
705                 (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
706                         DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
707         }
708         return quality;
709     }
710 
711     /**
712      * @return true if the lockscreen method is set to biometric weak
713      */
usingBiometricWeak()714     public boolean usingBiometricWeak() {
715         int quality =
716                 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
717         return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
718     }
719 
720     /**
721      * Deserialize a pattern.
722      * @param string The pattern serialized with {@link #patternToString}
723      * @return The pattern.
724      */
stringToPattern(String string)725     public static List<LockPatternView.Cell> stringToPattern(String string) {
726         List<LockPatternView.Cell> result = Lists.newArrayList();
727 
728         final byte[] bytes = string.getBytes();
729         for (int i = 0; i < bytes.length; i++) {
730             byte b = bytes[i];
731             result.add(LockPatternView.Cell.of(b / 3, b % 3));
732         }
733         return result;
734     }
735 
736     /**
737      * Serialize a pattern.
738      * @param pattern The pattern.
739      * @return The pattern in string form.
740      */
patternToString(List<LockPatternView.Cell> pattern)741     public static String patternToString(List<LockPatternView.Cell> pattern) {
742         if (pattern == null) {
743             return "";
744         }
745         final int patternSize = pattern.size();
746 
747         byte[] res = new byte[patternSize];
748         for (int i = 0; i < patternSize; i++) {
749             LockPatternView.Cell cell = pattern.get(i);
750             res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
751         }
752         return new String(res);
753     }
754 
755     /*
756      * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
757      * at least a second level of protection. First level is that the file
758      * is in a location only readable by the system process.
759      * @param pattern the gesture pattern.
760      * @return the hash of the pattern in a byte array.
761      */
patternToHash(List<LockPatternView.Cell> pattern)762     private static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
763         if (pattern == null) {
764             return null;
765         }
766 
767         final int patternSize = pattern.size();
768         byte[] res = new byte[patternSize];
769         for (int i = 0; i < patternSize; i++) {
770             LockPatternView.Cell cell = pattern.get(i);
771             res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
772         }
773         try {
774             MessageDigest md = MessageDigest.getInstance("SHA-1");
775             byte[] hash = md.digest(res);
776             return hash;
777         } catch (NoSuchAlgorithmException nsa) {
778             return res;
779         }
780     }
781 
getSalt()782     private String getSalt() {
783         long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0);
784         if (salt == 0) {
785             try {
786                 salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
787                 setLong(LOCK_PASSWORD_SALT_KEY, salt);
788                 Log.v(TAG, "Initialized lock password salt");
789             } catch (NoSuchAlgorithmException e) {
790                 // Throw an exception rather than storing a password we'll never be able to recover
791                 throw new IllegalStateException("Couldn't get SecureRandom number", e);
792             }
793         }
794         return Long.toHexString(salt);
795     }
796 
797     /*
798      * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
799      * Not the most secure, but it is at least a second level of protection. First level is that
800      * the file is in a location only readable by the system process.
801      * @param password the gesture pattern.
802      * @return the hash of the pattern in a byte array.
803      */
passwordToHash(String password)804     public byte[] passwordToHash(String password) {
805         if (password == null) {
806             return null;
807         }
808         String algo = null;
809         byte[] hashed = null;
810         try {
811             byte[] saltedPassword = (password + getSalt()).getBytes();
812             byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
813             byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
814             hashed = (toHex(sha1) + toHex(md5)).getBytes();
815         } catch (NoSuchAlgorithmException e) {
816             Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
817         }
818         return hashed;
819     }
820 
toHex(byte[] ary)821     private static String toHex(byte[] ary) {
822         final String hex = "0123456789ABCDEF";
823         String ret = "";
824         for (int i = 0; i < ary.length; i++) {
825             ret += hex.charAt((ary[i] >> 4) & 0xf);
826             ret += hex.charAt(ary[i] & 0xf);
827         }
828         return ret;
829     }
830 
831     /**
832      * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak
833      */
isLockPasswordEnabled()834     public boolean isLockPasswordEnabled() {
835         long mode = getLong(PASSWORD_TYPE_KEY, 0);
836         long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0);
837         final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
838                 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
839                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
840                 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
841         final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
842                 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
843                 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
844                 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
845 
846         return savedPasswordExists() && (passwordEnabled ||
847                 (usingBiometricWeak() && backupEnabled));
848     }
849 
850     /**
851      * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
852      */
isLockPatternEnabled()853     public boolean isLockPatternEnabled() {
854         final boolean backupEnabled =
855                 getLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
856                 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
857 
858         return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED)
859                 && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
860                         == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
861                         (usingBiometricWeak() && backupEnabled));
862     }
863 
864     /**
865      * @return Whether biometric weak lock is installed and that the front facing camera exists
866      */
isBiometricWeakInstalled()867     public boolean isBiometricWeakInstalled() {
868         // Check that the system flag was set
869         if (!OPTION_ENABLE_FACELOCK.equals(getString(LOCKSCREEN_OPTIONS))) {
870             return false;
871         }
872 
873         // Check that it's installed
874         PackageManager pm = mContext.getPackageManager();
875         try {
876             pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES);
877         } catch (PackageManager.NameNotFoundException e) {
878             return false;
879         }
880 
881         // Check that the camera is enabled
882         if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
883             return false;
884         }
885         if (getDevicePolicyManager().getCameraDisabled(null)) {
886             return false;
887         }
888 
889 
890         return true;
891     }
892 
893     /**
894      * Set whether the lock pattern is enabled.
895      */
setLockPatternEnabled(boolean enabled)896     public void setLockPatternEnabled(boolean enabled) {
897         setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
898     }
899 
900     /**
901      * @return Whether the visible pattern is enabled.
902      */
isVisiblePatternEnabled()903     public boolean isVisiblePatternEnabled() {
904         return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE);
905     }
906 
907     /**
908      * Set whether the visible pattern is enabled.
909      */
setVisiblePatternEnabled(boolean enabled)910     public void setVisiblePatternEnabled(boolean enabled) {
911         setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
912     }
913 
914     /**
915      * @return Whether tactile feedback for the pattern is enabled.
916      */
isTactileFeedbackEnabled()917     public boolean isTactileFeedbackEnabled() {
918         return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
919     }
920 
921     /**
922      * Set whether tactile feedback for the pattern is enabled.
923      */
setTactileFeedbackEnabled(boolean enabled)924     public void setTactileFeedbackEnabled(boolean enabled) {
925         setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled);
926     }
927 
928     /**
929      * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
930      * pattern until the deadline has passed.
931      * @return the chosen deadline.
932      */
setLockoutAttemptDeadline()933     public long setLockoutAttemptDeadline() {
934         final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
935         setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
936         return deadline;
937     }
938 
939     /**
940      * @return The elapsed time in millis in the future when the user is allowed to
941      *   attempt to enter his/her lock pattern, or 0 if the user is welcome to
942      *   enter a pattern.
943      */
getLockoutAttemptDeadline()944     public long getLockoutAttemptDeadline() {
945         final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
946         final long now = SystemClock.elapsedRealtime();
947         if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
948             return 0L;
949         }
950         return deadline;
951     }
952 
953     /**
954      * @return Whether the user is permanently locked out until they verify their
955      *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
956      *   attempts.
957      */
isPermanentlyLocked()958     public boolean isPermanentlyLocked() {
959         return getBoolean(LOCKOUT_PERMANENT_KEY);
960     }
961 
962     /**
963      * Set the state of whether the device is permanently locked, meaning the user
964      * must authenticate via other means.
965      *
966      * @param locked Whether the user is permanently locked out until they verify their
967      *   credentials.  Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
968      *   attempts.
969      */
setPermanentlyLocked(boolean locked)970     public void setPermanentlyLocked(boolean locked) {
971         setBoolean(LOCKOUT_PERMANENT_KEY, locked);
972     }
973 
isEmergencyCallCapable()974     public boolean isEmergencyCallCapable() {
975         return mContext.getResources().getBoolean(
976                 com.android.internal.R.bool.config_voice_capable);
977     }
978 
isPukUnlockScreenEnable()979     public boolean isPukUnlockScreenEnable() {
980         return mContext.getResources().getBoolean(
981                 com.android.internal.R.bool.config_enable_puk_unlock_screen);
982     }
983 
984     /**
985      * @return A formatted string of the next alarm (for showing on the lock screen),
986      *   or null if there is no next alarm.
987      */
getNextAlarm()988     public String getNextAlarm() {
989         String nextAlarm = Settings.System.getString(mContentResolver,
990                 Settings.System.NEXT_ALARM_FORMATTED);
991         if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
992             return null;
993         }
994         return nextAlarm;
995     }
996 
getBoolean(String secureSettingKey)997     private boolean getBoolean(String secureSettingKey) {
998         return 1 ==
999                 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
1000     }
1001 
setBoolean(String secureSettingKey, boolean enabled)1002     private void setBoolean(String secureSettingKey, boolean enabled) {
1003         android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
1004                                                 enabled ? 1 : 0);
1005     }
1006 
getLong(String secureSettingKey, long def)1007     private long getLong(String secureSettingKey, long def) {
1008         return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
1009     }
1010 
setLong(String secureSettingKey, long value)1011     private void setLong(String secureSettingKey, long value) {
1012         android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
1013     }
1014 
getString(String secureSettingKey)1015     private String getString(String secureSettingKey) {
1016         return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
1017     }
1018 
setString(String secureSettingKey, String value)1019     private void setString(String secureSettingKey, String value) {
1020         android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
1021     }
1022 
isSecure()1023     public boolean isSecure() {
1024         long mode = getKeyguardStoredPasswordQuality();
1025         final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1026         final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1027                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
1028                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1029                 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
1030         final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
1031                 || isPassword && savedPasswordExists();
1032         return secure;
1033     }
1034 
1035     /**
1036      * Sets the emergency button visibility based on isEmergencyCallCapable().
1037      *
1038      * If the emergency button is visible, sets the text on the emergency button
1039      * to indicate what action will be taken.
1040      *
1041      * If there's currently a call in progress, the button will take them to the call
1042      * @param button the button to update
1043      * @param the phone state:
1044      *  {@link TelephonyManager#CALL_STATE_IDLE}
1045      *  {@link TelephonyManager#CALL_STATE_RINGING}
1046      *  {@link TelephonyManager#CALL_STATE_OFFHOOK}
1047      * @param showIfCapable indicates whether the button should be shown if emergency calls are
1048      *                      possible on the device
1049      */
updateEmergencyCallButtonState(Button button, int phoneState, boolean showIfCapable)1050     public void updateEmergencyCallButtonState(Button button, int  phoneState,
1051             boolean showIfCapable) {
1052         if (isEmergencyCallCapable() && showIfCapable) {
1053             button.setVisibility(View.VISIBLE);
1054         } else {
1055             button.setVisibility(View.GONE);
1056             return;
1057         }
1058 
1059         int textId;
1060         if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
1061             // show "return to call" text and show phone icon
1062             textId = R.string.lockscreen_return_to_call;
1063             int phoneCallIcon = R.drawable.stat_sys_phone_call;
1064             button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
1065         } else {
1066             textId = R.string.lockscreen_emergency_call;
1067             int emergencyIcon = R.drawable.ic_emergency;
1068             button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
1069         }
1070         button.setText(textId);
1071     }
1072 
1073     /**
1074      * Resumes a call in progress. Typically launched from the EmergencyCall button
1075      * on various lockscreens.
1076      *
1077      * @return true if we were able to tell InCallScreen to show.
1078      */
resumeCall()1079     public boolean resumeCall() {
1080         ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1081         try {
1082             if (phone != null && phone.showCallScreen()) {
1083                 return true;
1084             }
1085         } catch (RemoteException e) {
1086             // What can we do?
1087         }
1088         return false;
1089     }
1090 }
1091