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