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