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.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 {
    139         public PasswordFileObserver(String path, int mask) {
    140             super(path, mask);
    141         }
    142 
    143         @Override
    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 
    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      */
    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 
    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      */
    198     public int getRequestedPasswordQuality() {
    199         return getDevicePolicyManager().getPasswordQuality(null);
    200     }
    201 
    202     public int getRequestedPasswordHistoryLength() {
    203         return getDevicePolicyManager().getPasswordHistoryLength(null);
    204     }
    205 
    206     public int getRequestedPasswordMinimumLetters() {
    207         return getDevicePolicyManager().getPasswordMinimumLetters(null);
    208     }
    209 
    210     public int getRequestedPasswordMinimumUpperCase() {
    211         return getDevicePolicyManager().getPasswordMinimumUpperCase(null);
    212     }
    213 
    214     public int getRequestedPasswordMinimumLowerCase() {
    215         return getDevicePolicyManager().getPasswordMinimumLowerCase(null);
    216     }
    217 
    218     public int getRequestedPasswordMinimumNumeric() {
    219         return getDevicePolicyManager().getPasswordMinimumNumeric(null);
    220     }
    221 
    222     public int getRequestedPasswordMinimumSymbols() {
    223         return getDevicePolicyManager().getPasswordMinimumSymbols(null);
    224     }
    225 
    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      */
    234     public void reportFailedPasswordAttempt() {
    235         getDevicePolicyManager().reportFailedPasswordAttempt();
    236     }
    237 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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     */
    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      */
    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     */
    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      */
    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      */
    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 **/
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    903     public boolean isVisiblePatternEnabled() {
    904         return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE);
    905     }
    906 
    907     /**
    908      * Set whether the visible pattern is enabled.
    909      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    970     public void setPermanentlyLocked(boolean locked) {
    971         setBoolean(LOCKOUT_PERMANENT_KEY, locked);
    972     }
    973 
    974     public boolean isEmergencyCallCapable() {
    975         return mContext.getResources().getBoolean(
    976                 com.android.internal.R.bool.config_voice_capable);
    977     }
    978 
    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      */
    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 
    997     private boolean getBoolean(String secureSettingKey) {
    998         return 1 ==
    999                 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
   1000     }
   1001 
   1002     private void setBoolean(String secureSettingKey, boolean enabled) {
   1003         android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
   1004                                                 enabled ? 1 : 0);
   1005     }
   1006 
   1007     private long getLong(String secureSettingKey, long def) {
   1008         return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
   1009     }
   1010 
   1011     private void setLong(String secureSettingKey, long value) {
   1012         android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
   1013     }
   1014 
   1015     private String getString(String secureSettingKey) {
   1016         return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
   1017     }
   1018 
   1019     private void setString(String secureSettingKey, String value) {
   1020         android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
   1021     }
   1022 
   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      */
   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      */
   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