blob: adb0ac999d0bd8e2456f447719eb67051ac4d91f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package com.android.internal.widget;
18
Jason parksf7b3cd42011-01-27 09:28:25 -060019import com.android.internal.R;
20import com.android.internal.telephony.ITelephony;
21import com.google.android.collect.Lists;
22
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080023import android.app.admin.DevicePolicyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.ContentResolver;
Jim Miller31f90b62010-01-20 13:35:20 -080025import android.content.Context;
Steven Ross329979c2011-09-28 11:42:56 -040026import android.content.Intent;
Danielle Millett58396982011-09-30 13:55:07 -040027import android.content.pm.PackageManager;
Brad Fitzpatrick90881002010-08-23 18:30:08 -070028import android.os.FileObserver;
Jason parksf7b3cd42011-01-27 09:28:25 -060029import android.os.IBinder;
Jim Miller69ac9882010-02-24 15:35:05 -080030import android.os.RemoteException;
31import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.os.SystemClock;
Jason parksf7b3cd42011-01-27 09:28:25 -060033import android.os.storage.IMountService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.provider.Settings;
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -070035import android.security.KeyStore;
Jim Miller69ac9882010-02-24 15:35:05 -080036import android.telephony.TelephonyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.text.TextUtils;
38import android.util.Log;
John Wang0f7b3f82011-05-31 11:20:55 -070039import android.view.View;
Jim Miller69ac9882010-02-24 15:35:05 -080040import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
Brad Fitzpatrick90881002010-08-23 18:30:08 -070042import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import java.io.FileNotFoundException;
44import java.io.IOException;
45import java.io.RandomAccessFile;
Brian Carlstrom929a1c22011-02-01 21:54:09 -080046import java.security.MessageDigest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.security.NoSuchAlgorithmException;
Jim Miller11b019d2010-01-20 16:34:45 -080048import java.security.SecureRandom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.Arrays;
50import java.util.List;
Brad Fitzpatrick90881002010-08-23 18:30:08 -070051import java.util.concurrent.atomic.AtomicBoolean;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53/**
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -070054 * Utilities for the lock pattern and its settings.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 */
56public class LockPatternUtils {
57
Jim Millercb3521e2011-10-03 20:42:26 -070058 private static final String OPTION_ENABLE_FACELOCK = "enable_facelock";
59
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 private static final String TAG = "LockPatternUtils";
Jim Miller69aa4a92009-12-22 19:03:28 -080061
Brad Fitzpatrick90881002010-08-23 18:30:08 -070062 private static final String SYSTEM_DIRECTORY = "/system/";
63 private static final String LOCK_PATTERN_FILE = "gesture.key";
64 private static final String LOCK_PASSWORD_FILE = "password.key";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66 /**
67 * The maximum number of incorrect attempts before the user is prevented
68 * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
69 */
70 public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
71
72 /**
73 * The number of incorrect attempts before which we fall back on an alternative
74 * method of verifying the user, and resetting their lock pattern.
75 */
76 public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
77
78 /**
79 * How long the user is prevented from trying again after entering the
80 * wrong pattern too many times.
81 */
82 public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
83
84 /**
85 * The interval of the countdown for showing progress of the lockout.
86 */
87 public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
88
Jim Miller4f369952011-08-19 18:29:22 -070089
90 /**
91 * This dictates when we start telling the user that continued failed attempts will wipe
92 * their device.
93 */
94 public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5;
95
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 /**
97 * The minimum number of dots in a valid pattern.
98 */
99 public static final int MIN_LOCK_PATTERN_SIZE = 4;
100
101 /**
102 * The minimum number of dots the user must include in a wrong pattern
103 * attempt for it to be counted against the counts that affect
104 * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
105 */
Jim Miller4f369952011-08-19 18:29:22 -0700106 public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
108 private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
109 private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
Jim Miller69aa4a92009-12-22 19:03:28 -0800110 private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
111 public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
Jim Miller6edf2632011-09-05 16:03:14 -0700112 public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
Jim Miller11b019d2010-01-20 16:34:45 -0800113 private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
Jim Miller2a98a4c2010-11-19 18:49:26 -0800114 private final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
Jim Millercb3521e2011-10-03 20:42:26 -0700115 private final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
Jim Miller6edf2632011-09-05 16:03:14 -0700116 public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
117 = "lockscreen.biometric_weak_fallback";
Danielle Millett7a072192011-10-03 17:36:01 -0400118 public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
119 = "lockscreen.biometricweakeverchosen";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700121 private final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
122
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800123 private final Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 private final ContentResolver mContentResolver;
Jim Miller31f90b62010-01-20 13:35:20 -0800125 private DevicePolicyManager mDevicePolicyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 private static String sLockPatternFilename;
Jim Miller69aa4a92009-12-22 19:03:28 -0800127 private static String sLockPasswordFilename;
128
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700129 private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false);
130 private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false);
Jim Miller4f369952011-08-19 18:29:22 -0700131
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700132 private static FileObserver sPasswordObserver;
133
Dianne Hackbornde4c26f2011-07-17 13:42:47 -0700134 private static class PasswordFileObserver extends FileObserver {
135 public PasswordFileObserver(String path, int mask) {
136 super(path, mask);
137 }
138
139 @Override
140 public void onEvent(int event, String path) {
141 if (LOCK_PATTERN_FILE.equals(path)) {
142 Log.d(TAG, "lock pattern file changed");
143 sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
144 } else if (LOCK_PASSWORD_FILE.equals(path)) {
145 Log.d(TAG, "lock password file changed");
146 sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
147 }
148 }
149 }
150
Jim Millercd709882010-03-25 18:24:02 -0700151 public DevicePolicyManager getDevicePolicyManager() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800152 if (mDevicePolicyManager == null) {
153 mDevicePolicyManager =
154 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
155 if (mDevicePolicyManager == null) {
156 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
157 new IllegalStateException("Stack trace:"));
158 }
159 }
160 return mDevicePolicyManager;
161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 /**
163 * @param contentResolver Used to look up and save settings.
164 */
Jim Miller31f90b62010-01-20 13:35:20 -0800165 public LockPatternUtils(Context context) {
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800166 mContext = context;
Jim Miller31f90b62010-01-20 13:35:20 -0800167 mContentResolver = context.getContentResolver();
Jim Miller69aa4a92009-12-22 19:03:28 -0800168
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700169 // Initialize the location of gesture & PIN lock files
170 if (sLockPatternFilename == null) {
171 String dataSystemDirectory =
172 android.os.Environment.getDataDirectory().getAbsolutePath() +
173 SYSTEM_DIRECTORY;
174 sLockPatternFilename = dataSystemDirectory + LOCK_PATTERN_FILE;
175 sLockPasswordFilename = dataSystemDirectory + LOCK_PASSWORD_FILE;
176 sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
177 sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
178 int fileObserverMask = FileObserver.CLOSE_WRITE | FileObserver.DELETE |
179 FileObserver.MOVED_TO | FileObserver.CREATE;
Dianne Hackbornde4c26f2011-07-17 13:42:47 -0700180 sPasswordObserver = new PasswordFileObserver(dataSystemDirectory, fileObserverMask);
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700181 sPasswordObserver.startWatching();
182 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 }
184
Jim Miller31f90b62010-01-20 13:35:20 -0800185 public int getRequestedMinimumPasswordLength() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800186 return getDevicePolicyManager().getPasswordMinimumLength(null);
Jim Miller31f90b62010-01-20 13:35:20 -0800187 }
188
Jim Millercd709882010-03-25 18:24:02 -0700189
Jim Miller31f90b62010-01-20 13:35:20 -0800190 /**
191 * Gets the device policy password mode. If the mode is non-specific, returns
192 * MODE_PATTERN which allows the user to choose anything.
Jim Miller31f90b62010-01-20 13:35:20 -0800193 */
Jim Millercd709882010-03-25 18:24:02 -0700194 public int getRequestedPasswordQuality() {
195 return getDevicePolicyManager().getPasswordQuality(null);
Jim Miller31f90b62010-01-20 13:35:20 -0800196 }
197
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700198 public int getRequestedPasswordHistoryLength() {
199 return getDevicePolicyManager().getPasswordHistoryLength(null);
200 }
201
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700202 public int getRequestedPasswordMinimumLetters() {
203 return getDevicePolicyManager().getPasswordMinimumLetters(null);
204 }
205
206 public int getRequestedPasswordMinimumUpperCase() {
207 return getDevicePolicyManager().getPasswordMinimumUpperCase(null);
208 }
209
210 public int getRequestedPasswordMinimumLowerCase() {
211 return getDevicePolicyManager().getPasswordMinimumLowerCase(null);
212 }
213
214 public int getRequestedPasswordMinimumNumeric() {
215 return getDevicePolicyManager().getPasswordMinimumNumeric(null);
216 }
217
218 public int getRequestedPasswordMinimumSymbols() {
219 return getDevicePolicyManager().getPasswordMinimumSymbols(null);
220 }
221
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700222 public int getRequestedPasswordMinimumNonLetter() {
223 return getDevicePolicyManager().getPasswordMinimumNonLetter(null);
224 }
Jim Miller31f90b62010-01-20 13:35:20 -0800225 /**
226 * Returns the actual password mode, as set by keyguard after updating the password.
227 *
228 * @return
229 */
Jim Miller31f90b62010-01-20 13:35:20 -0800230 public void reportFailedPasswordAttempt() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800231 getDevicePolicyManager().reportFailedPasswordAttempt();
Jim Miller31f90b62010-01-20 13:35:20 -0800232 }
233
234 public void reportSuccessfulPasswordAttempt() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800235 getDevicePolicyManager().reportSuccessfulPasswordAttempt();
Jim Miller31f90b62010-01-20 13:35:20 -0800236 }
237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 /**
239 * Check to see if a pattern matches the saved pattern. If no pattern exists,
240 * always returns true.
241 * @param pattern The pattern to check.
Jim Miller69aa4a92009-12-22 19:03:28 -0800242 * @return Whether the pattern matches the stored one.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 */
244 public boolean checkPattern(List<LockPatternView.Cell> pattern) {
245 try {
246 // Read all the bytes from the file
247 RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
248 final byte[] stored = new byte[(int) raf.length()];
249 int got = raf.read(stored, 0, stored.length);
250 raf.close();
251 if (got <= 0) {
252 return true;
253 }
254 // Compare the hash from the file with the entered pattern's hash
255 return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern));
256 } catch (FileNotFoundException fnfe) {
257 return true;
258 } catch (IOException ioe) {
259 return true;
260 }
261 }
262
263 /**
Jim Miller69aa4a92009-12-22 19:03:28 -0800264 * Check to see if a password matches the saved password. If no password exists,
265 * always returns true.
266 * @param password The password to check.
267 * @return Whether the password matches the stored one.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 */
Jim Miller69aa4a92009-12-22 19:03:28 -0800269 public boolean checkPassword(String password) {
270 try {
271 // Read all the bytes from the file
272 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
273 final byte[] stored = new byte[(int) raf.length()];
274 int got = raf.read(stored, 0, stored.length);
275 raf.close();
276 if (got <= 0) {
277 return true;
278 }
279 // Compare the hash from the file with the entered password's hash
Jim Miller11b019d2010-01-20 16:34:45 -0800280 return Arrays.equals(stored, passwordToHash(password));
Jim Miller69aa4a92009-12-22 19:03:28 -0800281 } catch (FileNotFoundException fnfe) {
282 return true;
283 } catch (IOException ioe) {
284 return true;
285 }
286 }
287
288 /**
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700289 * Check to see if a password matches any of the passwords stored in the
290 * password history.
291 *
292 * @param password The password to check.
293 * @return Whether the password matches any in the history.
294 */
295 public boolean checkPasswordHistory(String password) {
296 String passwordHashString = new String(passwordToHash(password));
297 String passwordHistory = getString(PASSWORD_HISTORY_KEY);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700298 if (passwordHistory == null) {
299 return false;
300 }
301 // Password History may be too long...
302 int passwordHashLength = passwordHashString.length();
303 int passwordHistoryLength = getRequestedPasswordHistoryLength();
304 if(passwordHistoryLength == 0) {
305 return false;
306 }
307 int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
308 + passwordHistoryLength - 1;
309 if (passwordHistory.length() > neededPasswordHistoryLength) {
310 passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
311 }
312 return passwordHistory.contains(passwordHashString);
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700313 }
314
315 /**
Jim Miller69aa4a92009-12-22 19:03:28 -0800316 * Check to see if the user has stored a lock pattern.
317 * @return Whether a saved pattern exists.
318 */
319 public boolean savedPatternExists() {
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700320 return sHaveNonZeroPatternFile.get();
Jim Miller69aa4a92009-12-22 19:03:28 -0800321 }
322
323 /**
324 * Check to see if the user has stored a lock pattern.
325 * @return Whether a saved pattern exists.
326 */
327 public boolean savedPasswordExists() {
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700328 return sHaveNonZeroPasswordFile.get();
Jim Miller69aa4a92009-12-22 19:03:28 -0800329 }
330
331 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700332 * Return true if the user has ever chosen a pattern. This is true even if the pattern is
333 * currently cleared.
334 *
335 * @return True if the user has ever chosen a pattern.
336 */
337 public boolean isPatternEverChosen() {
Jim Miller69aa4a92009-12-22 19:03:28 -0800338 return getBoolean(PATTERN_EVER_CHOSEN_KEY);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700339 }
340
341 /**
Danielle Millett7a072192011-10-03 17:36:01 -0400342 * Return true if the user has ever chosen biometric weak. This is true even if biometric
343 * weak is not current set.
344 *
345 * @return True if the user has ever chosen biometric weak.
346 */
347 public boolean isBiometricWeakEverChosen() {
348 return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY);
349 }
350
351 /**
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700352 * Used by device policy manager to validate the current password
353 * information it has.
354 */
355 public int getActivePasswordQuality() {
Jim Millercd709882010-03-25 18:24:02 -0700356 int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Danielle Millettc8fb5322011-10-04 12:18:51 -0400357 // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to
358 // return biometric_weak if that is being used instead of the backup
359 int quality =
360 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
361 switch (quality) {
Jim Millercd709882010-03-25 18:24:02 -0700362 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700363 if (isLockPatternEnabled()) {
Jim Millercd709882010-03-25 18:24:02 -0700364 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700365 }
Jim Millercd709882010-03-25 18:24:02 -0700366 break;
Danielle Millettc8fb5322011-10-04 12:18:51 -0400367 case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK:
368 if (isBiometricWeakInstalled()) {
369 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
370 }
371 break;
Jim Millercd709882010-03-25 18:24:02 -0700372 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700373 if (isLockPasswordEnabled()) {
Jim Millercd709882010-03-25 18:24:02 -0700374 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700375 }
Jim Millercd709882010-03-25 18:24:02 -0700376 break;
377 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700378 if (isLockPasswordEnabled()) {
Jim Millercd709882010-03-25 18:24:02 -0700379 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700380 }
Jim Millercd709882010-03-25 18:24:02 -0700381 break;
382 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
383 if (isLockPasswordEnabled()) {
384 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
385 }
386 break;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700387 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
388 if (isLockPasswordEnabled()) {
389 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
390 }
391 break;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700392 }
Danielle Millettc8fb5322011-10-04 12:18:51 -0400393
Jim Millercd709882010-03-25 18:24:02 -0700394 return activePasswordQuality;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700395 }
Jim Millercd709882010-03-25 18:24:02 -0700396
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700397 /**
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800398 * Clear any lock pattern or password.
399 */
Steven Ross329979c2011-09-28 11:42:56 -0400400 public void clearLock(boolean isFallback) {
401 if(!isFallback) deleteGallery();
Jim Millercd709882010-03-25 18:24:02 -0700402 saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800403 setLockPatternEnabled(false);
404 saveLockPattern(null);
Jim Millercd709882010-03-25 18:24:02 -0700405 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Jim Miller6edf2632011-09-05 16:03:14 -0700406 setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800407 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800408
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800409 /**
Jim Miller2a98a4c2010-11-19 18:49:26 -0800410 * Disable showing lock screen at all when the DevicePolicyManager allows it.
411 * This is only meaningful if pattern, pin or password are not set.
412 *
413 * @param disable Disables lock screen when true
414 */
415 public void setLockScreenDisabled(boolean disable) {
416 setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0);
417 }
418
419 /**
420 * Determine if LockScreen can be disabled. This is used, for example, to tell if we should
421 * show LockScreen or go straight to the home screen.
422 *
423 * @return true if lock screen is can be disabled
424 */
425 public boolean isLockScreenDisabled() {
426 return !isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0;
427 }
428
429 /**
Steven Ross3553c292011-09-30 15:48:40 -0400430 * Calls back SetupFaceLock to delete the temporary gallery file
Steven Ross329979c2011-09-28 11:42:56 -0400431 */
432 public void deleteTempGallery() {
Steven Ross3553c292011-09-30 15:48:40 -0400433 Intent intent = new Intent().setClassName("com.android.facelock",
434 "com.android.facelock.SetupFaceLock");
435 intent.putExtra("deleteTempGallery", true);
436 mContext.startActivity(intent);
Steven Ross329979c2011-09-28 11:42:56 -0400437 }
438
439 /**
440 * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
441 */
442 void deleteGallery() {
Danielle Millett58396982011-09-30 13:55:07 -0400443 if(usingBiometricWeak()) {
Steven Ross329979c2011-09-28 11:42:56 -0400444 Intent intent = new Intent().setClassName("com.android.facelock",
445 "com.android.facelock.SetupFaceLock");
446 intent.putExtra("deleteGallery", true);
447 mContext.startActivity(intent);
448 }
449 }
450
451 /**
Jim Miller6edf2632011-09-05 16:03:14 -0700452 * Save a lock pattern.
453 * @param pattern The new pattern to save.
Danielle Millett2364a222011-12-21 17:02:32 -0500454 */
455 public void saveLockPattern(List<LockPatternView.Cell> pattern) {
456 this.saveLockPattern(pattern, false);
457 }
458
459 /**
460 * Save a lock pattern.
461 * @param pattern The new pattern to save.
Jim Miller6edf2632011-09-05 16:03:14 -0700462 * @param isFallback Specifies if this is a fallback to biometric weak
463 */
464 public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 // Compute the hash
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700466 final byte[] hash = LockPatternUtils.patternToHash(pattern);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 try {
468 // Write the hash to file
469 RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw");
470 // Truncate the file if pattern is null, to clear the lock
471 if (pattern == null) {
472 raf.setLength(0);
473 } else {
474 raf.write(hash, 0, hash.length);
475 }
476 raf.close();
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800477 DevicePolicyManager dpm = getDevicePolicyManager();
Brian Carlstrome2afc242011-06-02 16:21:55 -0700478 KeyStore keyStore = KeyStore.getInstance();
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800479 if (pattern != null) {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700480 keyStore.password(patternToString(pattern));
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800481 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
Jim Miller6edf2632011-09-05 16:03:14 -0700482 if (!isFallback) {
Steven Ross329979c2011-09-28 11:42:56 -0400483 deleteGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700484 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Danielle Millett2364a222011-12-21 17:02:32 -0500485 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
486 pattern.size(), 0, 0, 0, 0, 0, 0);
Jim Miller6edf2632011-09-05 16:03:14 -0700487 } else {
488 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
489 setLong(PASSWORD_TYPE_ALTERNATE_KEY,
490 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Danielle Millett044a0a72011-11-07 15:42:12 -0500491 finishBiometricWeak();
Danielle Millett2364a222011-12-21 17:02:32 -0500492 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
493 0, 0, 0, 0, 0, 0, 0);
Jim Miller6edf2632011-09-05 16:03:14 -0700494 }
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800495 } else {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700496 if (keyStore.isEmpty()) {
497 keyStore.reset();
498 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700499 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700500 0, 0, 0, 0, 0);
Jim Miller31f90b62010-01-20 13:35:20 -0800501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 } catch (FileNotFoundException fnfe) {
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700503 // Cant do much, unless we want to fail over to using the settings
504 // provider
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
506 } catch (IOException ioe) {
507 // Cant do much
508 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
509 }
510 }
511
512 /**
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700513 * Compute the password quality from the given password string.
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800514 */
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700515 static public int computePasswordQuality(String password) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800516 boolean hasDigit = false;
517 boolean hasNonDigit = false;
518 final int len = password.length();
519 for (int i = 0; i < len; i++) {
520 if (Character.isDigit(password.charAt(i))) {
521 hasDigit = true;
522 } else {
523 hasNonDigit = true;
524 }
525 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800526
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700527 if (hasNonDigit && hasDigit) {
528 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800529 }
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800530 if (hasNonDigit) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700531 return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800532 }
533 if (hasDigit) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700534 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800535 }
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700536 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800537 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800538
Jason parksf7b3cd42011-01-27 09:28:25 -0600539 /** Update the encryption password if it is enabled **/
540 private void updateEncryptionPassword(String password) {
541 DevicePolicyManager dpm = getDevicePolicyManager();
542 if (dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
543 return;
544 }
545
546 IBinder service = ServiceManager.getService("mount");
547 if (service == null) {
548 Log.e(TAG, "Could not find the mount service to update the encryption password");
549 return;
550 }
551
552 IMountService mountService = IMountService.Stub.asInterface(service);
553 try {
554 mountService.changeEncryptionPassword(password);
555 } catch (RemoteException e) {
556 Log.e(TAG, "Error changing encryption password", e);
557 }
558 }
559
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800560 /**
Jim Millercd709882010-03-25 18:24:02 -0700561 * Save a lock password. Does not ensure that the password is as good
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800562 * as the requested mode, but will adjust the mode to be as good as the
563 * pattern.
Jim Miller69aa4a92009-12-22 19:03:28 -0800564 * @param password The password to save
Jim Millercd709882010-03-25 18:24:02 -0700565 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
Jim Miller69aa4a92009-12-22 19:03:28 -0800566 */
Jim Millercd709882010-03-25 18:24:02 -0700567 public void saveLockPassword(String password, int quality) {
Jim Miller6edf2632011-09-05 16:03:14 -0700568 this.saveLockPassword(password, quality, false);
569 }
570
571 /**
572 * Save a lock password. Does not ensure that the password is as good
573 * as the requested mode, but will adjust the mode to be as good as the
574 * pattern.
575 * @param password The password to save
576 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
577 * @param isFallback Specifies if this is a fallback to biometric weak
578 */
579 public void saveLockPassword(String password, int quality, boolean isFallback) {
Jim Miller69aa4a92009-12-22 19:03:28 -0800580 // Compute the hash
Jim Miller11b019d2010-01-20 16:34:45 -0800581 final byte[] hash = passwordToHash(password);
Jim Miller69aa4a92009-12-22 19:03:28 -0800582 try {
583 // Write the hash to file
584 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
585 // Truncate the file if pattern is null, to clear the lock
586 if (password == null) {
587 raf.setLength(0);
588 } else {
589 raf.write(hash, 0, hash.length);
590 }
591 raf.close();
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800592 DevicePolicyManager dpm = getDevicePolicyManager();
Brian Carlstrome2afc242011-06-02 16:21:55 -0700593 KeyStore keyStore = KeyStore.getInstance();
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800594 if (password != null) {
Jason parksf7b3cd42011-01-27 09:28:25 -0600595 // Update the encryption password.
596 updateEncryptionPassword(password);
597
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700598 // Update the keystore password
Brian Carlstrome2afc242011-06-02 16:21:55 -0700599 keyStore.password(password);
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700600
Jim Millercd709882010-03-25 18:24:02 -0700601 int computedQuality = computePasswordQuality(password);
Jim Miller6edf2632011-09-05 16:03:14 -0700602 if (!isFallback) {
Steven Ross329979c2011-09-28 11:42:56 -0400603 deleteGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700604 setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality));
Danielle Millett2364a222011-12-21 17:02:32 -0500605 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
606 int letters = 0;
607 int uppercase = 0;
608 int lowercase = 0;
609 int numbers = 0;
610 int symbols = 0;
611 int nonletter = 0;
612 for (int i = 0; i < password.length(); i++) {
613 char c = password.charAt(i);
614 if (c >= 'A' && c <= 'Z') {
615 letters++;
616 uppercase++;
617 } else if (c >= 'a' && c <= 'z') {
618 letters++;
619 lowercase++;
620 } else if (c >= '0' && c <= '9') {
621 numbers++;
622 nonletter++;
623 } else {
624 symbols++;
625 nonletter++;
626 }
627 }
628 dpm.setActivePasswordState(Math.max(quality, computedQuality),
629 password.length(), letters, uppercase, lowercase,
630 numbers, symbols, nonletter);
631 } else {
632 // The password is not anything.
633 dpm.setActivePasswordState(
634 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
635 0, 0, 0, 0, 0, 0, 0);
636 }
Jim Miller6edf2632011-09-05 16:03:14 -0700637 } else {
Danielle Millett2364a222011-12-21 17:02:32 -0500638 // Case where it's a fallback for biometric weak
Jim Miller6edf2632011-09-05 16:03:14 -0700639 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
640 setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality));
Danielle Millett044a0a72011-11-07 15:42:12 -0500641 finishBiometricWeak();
Danielle Millett2364a222011-12-21 17:02:32 -0500642 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
643 0, 0, 0, 0, 0, 0, 0);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700644 }
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700645 // Add the password to the password history. We assume all
646 // password
647 // hashes have the same length for simplicity of implementation.
648 String passwordHistory = getString(PASSWORD_HISTORY_KEY);
649 if (passwordHistory == null) {
650 passwordHistory = new String();
651 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700652 int passwordHistoryLength = getRequestedPasswordHistoryLength();
653 if (passwordHistoryLength == 0) {
654 passwordHistory = "";
655 } else {
656 passwordHistory = new String(hash) + "," + passwordHistory;
657 // Cut it to contain passwordHistoryLength hashes
658 // and passwordHistoryLength -1 commas.
659 passwordHistory = passwordHistory.substring(0, Math.min(hash.length
660 * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
661 .length()));
662 }
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700663 setString(PASSWORD_HISTORY_KEY, passwordHistory);
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800664 } else {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700665 // Conditionally reset the keystore if empty. If
666 // non-empty, we are just switching key guard type
667 if (keyStore.isEmpty()) {
668 keyStore.reset();
669 }
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800670 dpm.setActivePasswordState(
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700671 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
Jim Miller31f90b62010-01-20 13:35:20 -0800672 }
Jim Miller69aa4a92009-12-22 19:03:28 -0800673 } catch (FileNotFoundException fnfe) {
674 // Cant do much, unless we want to fail over to using the settings provider
675 Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
676 } catch (IOException ioe) {
677 // Cant do much
678 Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
679 }
680 }
681
Jim Millercd709882010-03-25 18:24:02 -0700682 /**
683 * Retrieves the quality mode we're in.
684 * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
685 *
686 * @return stored password quality
687 */
688 public int getKeyguardStoredPasswordQuality() {
Jim Miller6edf2632011-09-05 16:03:14 -0700689 int quality =
690 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
691 // If the user has chosen to use weak biometric sensor, then return the backup locking
692 // method and treat biometric as a special case.
693 if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
694 quality =
695 (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
696 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
697 }
698 return quality;
699 }
700
Danielle Millett58396982011-09-30 13:55:07 -0400701 /**
702 * @return true if the lockscreen method is set to biometric weak
703 */
Jim Miller6edf2632011-09-05 16:03:14 -0700704 public boolean usingBiometricWeak() {
705 int quality =
706 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
707 return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
Jim Miller69aa4a92009-12-22 19:03:28 -0800708 }
709
710 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 * Deserialize a pattern.
712 * @param string The pattern serialized with {@link #patternToString}
713 * @return The pattern.
714 */
715 public static List<LockPatternView.Cell> stringToPattern(String string) {
716 List<LockPatternView.Cell> result = Lists.newArrayList();
717
718 final byte[] bytes = string.getBytes();
719 for (int i = 0; i < bytes.length; i++) {
720 byte b = bytes[i];
721 result.add(LockPatternView.Cell.of(b / 3, b % 3));
722 }
723 return result;
724 }
725
726 /**
727 * Serialize a pattern.
728 * @param pattern The pattern.
729 * @return The pattern in string form.
730 */
731 public static String patternToString(List<LockPatternView.Cell> pattern) {
732 if (pattern == null) {
733 return "";
734 }
735 final int patternSize = pattern.size();
736
737 byte[] res = new byte[patternSize];
738 for (int i = 0; i < patternSize; i++) {
739 LockPatternView.Cell cell = pattern.get(i);
740 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
741 }
742 return new String(res);
743 }
Jim Miller69aa4a92009-12-22 19:03:28 -0800744
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 /*
746 * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
747 * at least a second level of protection. First level is that the file
748 * is in a location only readable by the system process.
749 * @param pattern the gesture pattern.
750 * @return the hash of the pattern in a byte array.
751 */
Jim Miller69aa4a92009-12-22 19:03:28 -0800752 private static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 if (pattern == null) {
754 return null;
755 }
Jim Miller69aa4a92009-12-22 19:03:28 -0800756
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 final int patternSize = pattern.size();
758 byte[] res = new byte[patternSize];
759 for (int i = 0; i < patternSize; i++) {
760 LockPatternView.Cell cell = pattern.get(i);
761 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
762 }
763 try {
764 MessageDigest md = MessageDigest.getInstance("SHA-1");
765 byte[] hash = md.digest(res);
766 return hash;
767 } catch (NoSuchAlgorithmException nsa) {
768 return res;
769 }
770 }
771
Jim Miller11b019d2010-01-20 16:34:45 -0800772 private String getSalt() {
773 long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0);
774 if (salt == 0) {
775 try {
776 salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
777 setLong(LOCK_PASSWORD_SALT_KEY, salt);
778 Log.v(TAG, "Initialized lock password salt");
779 } catch (NoSuchAlgorithmException e) {
780 // Throw an exception rather than storing a password we'll never be able to recover
781 throw new IllegalStateException("Couldn't get SecureRandom number", e);
782 }
783 }
784 return Long.toHexString(salt);
785 }
786
Jim Miller69aa4a92009-12-22 19:03:28 -0800787 /*
788 * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
789 * Not the most secure, but it is at least a second level of protection. First level is that
790 * the file is in a location only readable by the system process.
791 * @param password the gesture pattern.
792 * @return the hash of the pattern in a byte array.
793 */
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700794 public byte[] passwordToHash(String password) {
Jim Miller69aa4a92009-12-22 19:03:28 -0800795 if (password == null) {
796 return null;
797 }
798 String algo = null;
799 byte[] hashed = null;
800 try {
Jim Miller11b019d2010-01-20 16:34:45 -0800801 byte[] saltedPassword = (password + getSalt()).getBytes();
Jim Miller69aa4a92009-12-22 19:03:28 -0800802 byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
803 byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
804 hashed = (toHex(sha1) + toHex(md5)).getBytes();
805 } catch (NoSuchAlgorithmException e) {
806 Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
807 }
808 return hashed;
809 }
810
811 private static String toHex(byte[] ary) {
812 final String hex = "0123456789ABCDEF";
813 String ret = "";
814 for (int i = 0; i < ary.length; i++) {
815 ret += hex.charAt((ary[i] >> 4) & 0xf);
816 ret += hex.charAt(ary[i] & 0xf);
817 }
818 return ret;
819 }
820
821 /**
Danielle Millett73da5fe2011-09-13 16:20:05 -0400822 * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak
Jim Miller69aa4a92009-12-22 19:03:28 -0800823 */
824 public boolean isLockPasswordEnabled() {
825 long mode = getLong(PASSWORD_TYPE_KEY, 0);
Danielle Millett73da5fe2011-09-13 16:20:05 -0400826 long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0);
827 final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
828 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
829 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
830 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
831 final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
832 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
833 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
834 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
835
836 return savedPasswordExists() && (passwordEnabled ||
Danielle Millett58396982011-09-30 13:55:07 -0400837 (usingBiometricWeak() && backupEnabled));
Jim Miller69aa4a92009-12-22 19:03:28 -0800838 }
839
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 /**
Danielle Millett73da5fe2011-09-13 16:20:05 -0400841 * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 */
843 public boolean isLockPatternEnabled() {
Danielle Millett73da5fe2011-09-13 16:20:05 -0400844 final boolean backupEnabled =
845 getLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
846 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
847
Amith Yamasani156c4352010-03-05 17:10:03 -0800848 return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED)
Danielle Millett73da5fe2011-09-13 16:20:05 -0400849 && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
850 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
Danielle Millett58396982011-09-30 13:55:07 -0400851 (usingBiometricWeak() && backupEnabled));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
853
854 /**
Danielle Millett58396982011-09-30 13:55:07 -0400855 * @return Whether biometric weak lock is installed and that the front facing camera exists
Jim Miller6edf2632011-09-05 16:03:14 -0700856 */
Danielle Millett58396982011-09-30 13:55:07 -0400857 public boolean isBiometricWeakInstalled() {
858 // Check that the system flag was set
Jim Millercb3521e2011-10-03 20:42:26 -0700859 if (!OPTION_ENABLE_FACELOCK.equals(getString(LOCKSCREEN_OPTIONS))) {
Danielle Millett58396982011-09-30 13:55:07 -0400860 return false;
861 }
862
863 // Check that it's installed
864 PackageManager pm = mContext.getPackageManager();
865 try {
866 pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES);
867 } catch (PackageManager.NameNotFoundException e) {
868 return false;
869 }
870
871 // Check that the camera is enabled
872 if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
873 return false;
874 }
875 if (getDevicePolicyManager().getCameraDisabled(null)) {
876 return false;
877 }
878
879
880 return true;
Jim Miller6edf2632011-09-05 16:03:14 -0700881 }
882
883 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 * Set whether the lock pattern is enabled.
885 */
886 public void setLockPatternEnabled(boolean enabled) {
Amith Yamasani156c4352010-03-05 17:10:03 -0800887 setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 }
889
890 /**
891 * @return Whether the visible pattern is enabled.
892 */
893 public boolean isVisiblePatternEnabled() {
Amith Yamasani156c4352010-03-05 17:10:03 -0800894 return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 }
896
897 /**
898 * Set whether the visible pattern is enabled.
899 */
900 public void setVisiblePatternEnabled(boolean enabled) {
Amith Yamasani156c4352010-03-05 17:10:03 -0800901 setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 }
903
904 /**
905 * @return Whether tactile feedback for the pattern is enabled.
906 */
907 public boolean isTactileFeedbackEnabled() {
Amith Yamasani156c4352010-03-05 17:10:03 -0800908 return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 }
910
911 /**
912 * Set whether tactile feedback for the pattern is enabled.
913 */
914 public void setTactileFeedbackEnabled(boolean enabled) {
Amith Yamasani156c4352010-03-05 17:10:03 -0800915 setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 }
917
918 /**
919 * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
920 * pattern until the deadline has passed.
921 * @return the chosen deadline.
922 */
923 public long setLockoutAttemptDeadline() {
924 final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
925 setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
926 return deadline;
927 }
928
929 /**
930 * @return The elapsed time in millis in the future when the user is allowed to
931 * attempt to enter his/her lock pattern, or 0 if the user is welcome to
932 * enter a pattern.
933 */
934 public long getLockoutAttemptDeadline() {
935 final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
936 final long now = SystemClock.elapsedRealtime();
937 if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
938 return 0L;
939 }
940 return deadline;
941 }
942
943 /**
944 * @return Whether the user is permanently locked out until they verify their
945 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
946 * attempts.
947 */
948 public boolean isPermanentlyLocked() {
949 return getBoolean(LOCKOUT_PERMANENT_KEY);
950 }
951
952 /**
953 * Set the state of whether the device is permanently locked, meaning the user
Karl Rosaen678771b2009-08-21 14:00:26 -0700954 * must authenticate via other means.
955 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 * @param locked Whether the user is permanently locked out until they verify their
957 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
958 * attempts.
959 */
960 public void setPermanentlyLocked(boolean locked) {
961 setBoolean(LOCKOUT_PERMANENT_KEY, locked);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 }
963
John Wang0f7b3f82011-05-31 11:20:55 -0700964 public boolean isEmergencyCallCapable() {
965 return mContext.getResources().getBoolean(
966 com.android.internal.R.bool.config_voice_capable);
967 }
968
969 public boolean isPukUnlockScreenEnable() {
970 return mContext.getResources().getBoolean(
971 com.android.internal.R.bool.config_enable_puk_unlock_screen);
972 }
973
Jim Miller1f56edc2011-11-07 19:00:48 -0800974 public boolean isEmergencyCallEnabledWhileSimLocked() {
975 return mContext.getResources().getBoolean(
976 com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
977 }
978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 /**
980 * @return A formatted string of the next alarm (for showing on the lock screen),
981 * or null if there is no next alarm.
982 */
983 public String getNextAlarm() {
984 String nextAlarm = Settings.System.getString(mContentResolver,
985 Settings.System.NEXT_ALARM_FORMATTED);
986 if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
987 return null;
988 }
989 return nextAlarm;
990 }
991
Amith Yamasani156c4352010-03-05 17:10:03 -0800992 private boolean getBoolean(String secureSettingKey) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 return 1 ==
Amith Yamasani156c4352010-03-05 17:10:03 -0800994 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996
Amith Yamasani156c4352010-03-05 17:10:03 -0800997 private void setBoolean(String secureSettingKey, boolean enabled) {
998 android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
999 enabled ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 }
1001
Amith Yamasani156c4352010-03-05 17:10:03 -08001002 private long getLong(String secureSettingKey, long def) {
1003 return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 }
1005
Amith Yamasani156c4352010-03-05 17:10:03 -08001006 private void setLong(String secureSettingKey, long value) {
1007 android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
1009
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -07001010 private String getString(String secureSettingKey) {
1011 return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
1012 }
1013
1014 private void setString(String secureSettingKey, String value) {
1015 android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
1016 }
1017
Jim Miller69aa4a92009-12-22 19:03:28 -08001018 public boolean isSecure() {
Jim Millercd709882010-03-25 18:24:02 -07001019 long mode = getKeyguardStoredPasswordQuality();
1020 final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1021 final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1022 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001023 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1024 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
Jim Millercd709882010-03-25 18:24:02 -07001025 final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
Danielle Millett73da5fe2011-09-13 16:20:05 -04001026 || isPassword && savedPasswordExists();
Jim Miller69aa4a92009-12-22 19:03:28 -08001027 return secure;
1028 }
Jim Miller69ac9882010-02-24 15:35:05 -08001029
1030 /**
John Wang0f7b3f82011-05-31 11:20:55 -07001031 * Sets the emergency button visibility based on isEmergencyCallCapable().
1032 *
1033 * If the emergency button is visible, sets the text on the emergency button
1034 * to indicate what action will be taken.
1035 *
Jim Miller69ac9882010-02-24 15:35:05 -08001036 * If there's currently a call in progress, the button will take them to the call
1037 * @param button the button to update
Jim Miller3f5f83b2011-09-26 15:17:05 -07001038 * @param the phone state:
1039 * {@link TelephonyManager#CALL_STATE_IDLE}
1040 * {@link TelephonyManager#CALL_STATE_RINGING}
1041 * {@link TelephonyManager#CALL_STATE_OFFHOOK}
Jim Miller1f56edc2011-11-07 19:00:48 -08001042 * @param shown indicates whether the given screen wants the emergency button to show at all
Jim Miller69ac9882010-02-24 15:35:05 -08001043 */
Jim Miller1f56edc2011-11-07 19:00:48 -08001044 public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) {
1045 if (isEmergencyCallCapable() && shown) {
John Wang0f7b3f82011-05-31 11:20:55 -07001046 button.setVisibility(View.VISIBLE);
1047 } else {
1048 button.setVisibility(View.GONE);
1049 return;
1050 }
1051
Jim Miller69ac9882010-02-24 15:35:05 -08001052 int textId;
Jim Miller3f5f83b2011-09-26 15:17:05 -07001053 if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
Jim Miller69ac9882010-02-24 15:35:05 -08001054 // show "return to call" text and show phone icon
1055 textId = R.string.lockscreen_return_to_call;
1056 int phoneCallIcon = R.drawable.stat_sys_phone_call;
1057 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
1058 } else {
1059 textId = R.string.lockscreen_emergency_call;
1060 int emergencyIcon = R.drawable.ic_emergency;
1061 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
1062 }
1063 button.setText(textId);
1064 }
1065
1066 /**
1067 * Resumes a call in progress. Typically launched from the EmergencyCall button
1068 * on various lockscreens.
1069 *
1070 * @return true if we were able to tell InCallScreen to show.
1071 */
1072 public boolean resumeCall() {
1073 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1074 try {
1075 if (phone != null && phone.showCallScreen()) {
1076 return true;
1077 }
1078 } catch (RemoteException e) {
1079 // What can we do?
1080 }
1081 return false;
1082 }
Danielle Millett044a0a72011-11-07 15:42:12 -05001083
1084 private void finishBiometricWeak() {
1085 setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
1086
1087 // Launch intent to show final screen, this also
1088 // moves the temporary gallery to the actual gallery
1089 Intent intent = new Intent();
1090 intent.setClassName("com.android.facelock",
1091 "com.android.facelock.SetupEndScreen");
1092 mContext.startActivity(intent);
1093 }
1094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095}