blob: 89f9d4e7fb03474caf1239a82869cf01dad61749 [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 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 * Save a lock pattern.
431 * @param pattern The new pattern to save.
432 */
433 public void saveLockPattern(List<LockPatternView.Cell> pattern) {
Jim Miller6edf2632011-09-05 16:03:14 -0700434 this.saveLockPattern(pattern, false);
435 }
436
437 /**
Steven Ross3553c292011-09-30 15:48:40 -0400438 * Calls back SetupFaceLock to delete the temporary gallery file
Steven Ross329979c2011-09-28 11:42:56 -0400439 */
440 public void deleteTempGallery() {
Steven Ross3553c292011-09-30 15:48:40 -0400441 Intent intent = new Intent().setClassName("com.android.facelock",
442 "com.android.facelock.SetupFaceLock");
443 intent.putExtra("deleteTempGallery", true);
444 mContext.startActivity(intent);
Steven Ross329979c2011-09-28 11:42:56 -0400445 }
446
447 /**
448 * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
449 */
450 void deleteGallery() {
Danielle Millett58396982011-09-30 13:55:07 -0400451 if(usingBiometricWeak()) {
Steven Ross329979c2011-09-28 11:42:56 -0400452 Intent intent = new Intent().setClassName("com.android.facelock",
453 "com.android.facelock.SetupFaceLock");
454 intent.putExtra("deleteGallery", true);
455 mContext.startActivity(intent);
456 }
457 }
458
459 /**
Jim Miller6edf2632011-09-05 16:03:14 -0700460 * Save a lock pattern.
461 * @param pattern The new pattern to save.
462 * @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);
485 } else {
486 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
487 setLong(PASSWORD_TYPE_ALTERNATE_KEY,
488 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Danielle Millett044a0a72011-11-07 15:42:12 -0500489 finishBiometricWeak();
Jim Miller6edf2632011-09-05 16:03:14 -0700490 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700491 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700492 .size(), 0, 0, 0, 0, 0, 0);
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800493 } else {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700494 if (keyStore.isEmpty()) {
495 keyStore.reset();
496 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700497 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700498 0, 0, 0, 0, 0);
Jim Miller31f90b62010-01-20 13:35:20 -0800499 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 } catch (FileNotFoundException fnfe) {
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700501 // Cant do much, unless we want to fail over to using the settings
502 // provider
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
504 } catch (IOException ioe) {
505 // Cant do much
506 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
507 }
508 }
509
510 /**
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700511 * Compute the password quality from the given password string.
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800512 */
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700513 static public int computePasswordQuality(String password) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800514 boolean hasDigit = false;
515 boolean hasNonDigit = false;
516 final int len = password.length();
517 for (int i = 0; i < len; i++) {
518 if (Character.isDigit(password.charAt(i))) {
519 hasDigit = true;
520 } else {
521 hasNonDigit = true;
522 }
523 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800524
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700525 if (hasNonDigit && hasDigit) {
526 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800527 }
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800528 if (hasNonDigit) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700529 return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800530 }
531 if (hasDigit) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700532 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800533 }
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700534 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800535 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800536
Jason parksf7b3cd42011-01-27 09:28:25 -0600537 /** Update the encryption password if it is enabled **/
538 private void updateEncryptionPassword(String password) {
539 DevicePolicyManager dpm = getDevicePolicyManager();
540 if (dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
541 return;
542 }
543
544 IBinder service = ServiceManager.getService("mount");
545 if (service == null) {
546 Log.e(TAG, "Could not find the mount service to update the encryption password");
547 return;
548 }
549
550 IMountService mountService = IMountService.Stub.asInterface(service);
551 try {
552 mountService.changeEncryptionPassword(password);
553 } catch (RemoteException e) {
554 Log.e(TAG, "Error changing encryption password", e);
555 }
556 }
557
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800558 /**
Jim Millercd709882010-03-25 18:24:02 -0700559 * Save a lock password. Does not ensure that the password is as good
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800560 * as the requested mode, but will adjust the mode to be as good as the
561 * pattern.
Jim Miller69aa4a92009-12-22 19:03:28 -0800562 * @param password The password to save
Jim Millercd709882010-03-25 18:24:02 -0700563 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
Jim Miller69aa4a92009-12-22 19:03:28 -0800564 */
Jim Millercd709882010-03-25 18:24:02 -0700565 public void saveLockPassword(String password, int quality) {
Jim Miller6edf2632011-09-05 16:03:14 -0700566 this.saveLockPassword(password, quality, false);
567 }
568
569 /**
570 * Save a lock password. Does not ensure that the password is as good
571 * as the requested mode, but will adjust the mode to be as good as the
572 * pattern.
573 * @param password The password to save
574 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
575 * @param isFallback Specifies if this is a fallback to biometric weak
576 */
577 public void saveLockPassword(String password, int quality, boolean isFallback) {
Jim Miller69aa4a92009-12-22 19:03:28 -0800578 // Compute the hash
Jim Miller11b019d2010-01-20 16:34:45 -0800579 final byte[] hash = passwordToHash(password);
Jim Miller69aa4a92009-12-22 19:03:28 -0800580 try {
581 // Write the hash to file
582 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
583 // Truncate the file if pattern is null, to clear the lock
584 if (password == null) {
585 raf.setLength(0);
586 } else {
587 raf.write(hash, 0, hash.length);
588 }
589 raf.close();
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800590 DevicePolicyManager dpm = getDevicePolicyManager();
Brian Carlstrome2afc242011-06-02 16:21:55 -0700591 KeyStore keyStore = KeyStore.getInstance();
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800592 if (password != null) {
Jason parksf7b3cd42011-01-27 09:28:25 -0600593 // Update the encryption password.
594 updateEncryptionPassword(password);
595
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700596 // Update the keystore password
Brian Carlstrome2afc242011-06-02 16:21:55 -0700597 keyStore.password(password);
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700598
Jim Millercd709882010-03-25 18:24:02 -0700599 int computedQuality = computePasswordQuality(password);
Jim Miller6edf2632011-09-05 16:03:14 -0700600 if (!isFallback) {
Steven Ross329979c2011-09-28 11:42:56 -0400601 deleteGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700602 setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality));
603 } else {
604 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
605 setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality));
Danielle Millett044a0a72011-11-07 15:42:12 -0500606 finishBiometricWeak();
Jim Miller6edf2632011-09-05 16:03:14 -0700607 }
Jim Millercd709882010-03-25 18:24:02 -0700608 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700609 int letters = 0;
610 int uppercase = 0;
611 int lowercase = 0;
612 int numbers = 0;
613 int symbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700614 int nonletter = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700615 for (int i = 0; i < password.length(); i++) {
616 char c = password.charAt(i);
617 if (c >= 'A' && c <= 'Z') {
618 letters++;
619 uppercase++;
620 } else if (c >= 'a' && c <= 'z') {
621 letters++;
622 lowercase++;
623 } else if (c >= '0' && c <= '9') {
624 numbers++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700625 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700626 } else {
627 symbols++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700628 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700629 }
630 }
631 dpm.setActivePasswordState(Math.max(quality, computedQuality), password
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700632 .length(), letters, uppercase, lowercase, numbers, symbols, nonletter);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700633 } else {
634 // The password is not anything.
635 dpm.setActivePasswordState(
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700636 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700637 }
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700638 // Add the password to the password history. We assume all
639 // password
640 // hashes have the same length for simplicity of implementation.
641 String passwordHistory = getString(PASSWORD_HISTORY_KEY);
642 if (passwordHistory == null) {
643 passwordHistory = new String();
644 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700645 int passwordHistoryLength = getRequestedPasswordHistoryLength();
646 if (passwordHistoryLength == 0) {
647 passwordHistory = "";
648 } else {
649 passwordHistory = new String(hash) + "," + passwordHistory;
650 // Cut it to contain passwordHistoryLength hashes
651 // and passwordHistoryLength -1 commas.
652 passwordHistory = passwordHistory.substring(0, Math.min(hash.length
653 * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
654 .length()));
655 }
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700656 setString(PASSWORD_HISTORY_KEY, passwordHistory);
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800657 } else {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700658 // Conditionally reset the keystore if empty. If
659 // non-empty, we are just switching key guard type
660 if (keyStore.isEmpty()) {
661 keyStore.reset();
662 }
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800663 dpm.setActivePasswordState(
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700664 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
Jim Miller31f90b62010-01-20 13:35:20 -0800665 }
Jim Miller69aa4a92009-12-22 19:03:28 -0800666 } catch (FileNotFoundException fnfe) {
667 // Cant do much, unless we want to fail over to using the settings provider
668 Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
669 } catch (IOException ioe) {
670 // Cant do much
671 Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
672 }
673 }
674
Jim Millercd709882010-03-25 18:24:02 -0700675 /**
676 * Retrieves the quality mode we're in.
677 * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
678 *
679 * @return stored password quality
680 */
681 public int getKeyguardStoredPasswordQuality() {
Jim Miller6edf2632011-09-05 16:03:14 -0700682 int quality =
683 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
684 // If the user has chosen to use weak biometric sensor, then return the backup locking
685 // method and treat biometric as a special case.
686 if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
687 quality =
688 (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY,
689 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
690 }
691 return quality;
692 }
693
Danielle Millett58396982011-09-30 13:55:07 -0400694 /**
695 * @return true if the lockscreen method is set to biometric weak
696 */
Jim Miller6edf2632011-09-05 16:03:14 -0700697 public boolean usingBiometricWeak() {
698 int quality =
699 (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
700 return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
Jim Miller69aa4a92009-12-22 19:03:28 -0800701 }
702
703 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 * Deserialize a pattern.
705 * @param string The pattern serialized with {@link #patternToString}
706 * @return The pattern.
707 */
708 public static List<LockPatternView.Cell> stringToPattern(String string) {
709 List<LockPatternView.Cell> result = Lists.newArrayList();
710
711 final byte[] bytes = string.getBytes();
712 for (int i = 0; i < bytes.length; i++) {
713 byte b = bytes[i];
714 result.add(LockPatternView.Cell.of(b / 3, b % 3));
715 }
716 return result;
717 }
718
719 /**
720 * Serialize a pattern.
721 * @param pattern The pattern.
722 * @return The pattern in string form.
723 */
724 public static String patternToString(List<LockPatternView.Cell> pattern) {
725 if (pattern == null) {
726 return "";
727 }
728 final int patternSize = pattern.size();
729
730 byte[] res = new byte[patternSize];
731 for (int i = 0; i < patternSize; i++) {
732 LockPatternView.Cell cell = pattern.get(i);
733 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
734 }
735 return new String(res);
736 }
Jim Miller69aa4a92009-12-22 19:03:28 -0800737
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 /*
739 * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
740 * at least a second level of protection. First level is that the file
741 * is in a location only readable by the system process.
742 * @param pattern the gesture pattern.
743 * @return the hash of the pattern in a byte array.
744 */
Jim Miller69aa4a92009-12-22 19:03:28 -0800745 private static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 if (pattern == null) {
747 return null;
748 }
Jim Miller69aa4a92009-12-22 19:03:28 -0800749
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 final int patternSize = pattern.size();
751 byte[] res = new byte[patternSize];
752 for (int i = 0; i < patternSize; i++) {
753 LockPatternView.Cell cell = pattern.get(i);
754 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
755 }
756 try {
757 MessageDigest md = MessageDigest.getInstance("SHA-1");
758 byte[] hash = md.digest(res);
759 return hash;
760 } catch (NoSuchAlgorithmException nsa) {
761 return res;
762 }
763 }
764
Jim Miller11b019d2010-01-20 16:34:45 -0800765 private String getSalt() {
766 long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0);
767 if (salt == 0) {
768 try {
769 salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
770 setLong(LOCK_PASSWORD_SALT_KEY, salt);
771 Log.v(TAG, "Initialized lock password salt");
772 } catch (NoSuchAlgorithmException e) {
773 // Throw an exception rather than storing a password we'll never be able to recover
774 throw new IllegalStateException("Couldn't get SecureRandom number", e);
775 }
776 }
777 return Long.toHexString(salt);
778 }
779
Jim Miller69aa4a92009-12-22 19:03:28 -0800780 /*
781 * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
782 * Not the most secure, but it is at least a second level of protection. First level is that
783 * the file is in a location only readable by the system process.
784 * @param password the gesture pattern.
785 * @return the hash of the pattern in a byte array.
786 */
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700787 public byte[] passwordToHash(String password) {
Jim Miller69aa4a92009-12-22 19:03:28 -0800788 if (password == null) {
789 return null;
790 }
791 String algo = null;
792 byte[] hashed = null;
793 try {
Jim Miller11b019d2010-01-20 16:34:45 -0800794 byte[] saltedPassword = (password + getSalt()).getBytes();
Jim Miller69aa4a92009-12-22 19:03:28 -0800795 byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
796 byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
797 hashed = (toHex(sha1) + toHex(md5)).getBytes();
798 } catch (NoSuchAlgorithmException e) {
799 Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
800 }
801 return hashed;
802 }
803
804 private static String toHex(byte[] ary) {
805 final String hex = "0123456789ABCDEF";
806 String ret = "";
807 for (int i = 0; i < ary.length; i++) {
808 ret += hex.charAt((ary[i] >> 4) & 0xf);
809 ret += hex.charAt(ary[i] & 0xf);
810 }
811 return ret;
812 }
813
814 /**
Danielle Millett73da5fe2011-09-13 16:20:05 -0400815 * @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 -0800816 */
817 public boolean isLockPasswordEnabled() {
818 long mode = getLong(PASSWORD_TYPE_KEY, 0);
Danielle Millett73da5fe2011-09-13 16:20:05 -0400819 long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0);
820 final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
821 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
822 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
823 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
824 final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
825 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
826 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
827 || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
828
829 return savedPasswordExists() && (passwordEnabled ||
Danielle Millett58396982011-09-30 13:55:07 -0400830 (usingBiometricWeak() && backupEnabled));
Jim Miller69aa4a92009-12-22 19:03:28 -0800831 }
832
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 /**
Danielle Millett73da5fe2011-09-13 16:20:05 -0400834 * @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 -0800835 */
836 public boolean isLockPatternEnabled() {
Danielle Millett73da5fe2011-09-13 16:20:05 -0400837 final boolean backupEnabled =
838 getLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
839 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
840
Amith Yamasani156c4352010-03-05 17:10:03 -0800841 return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED)
Danielle Millett73da5fe2011-09-13 16:20:05 -0400842 && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
843 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING ||
Danielle Millett58396982011-09-30 13:55:07 -0400844 (usingBiometricWeak() && backupEnabled));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 }
846
847 /**
Danielle Millett58396982011-09-30 13:55:07 -0400848 * @return Whether biometric weak lock is installed and that the front facing camera exists
Jim Miller6edf2632011-09-05 16:03:14 -0700849 */
Danielle Millett58396982011-09-30 13:55:07 -0400850 public boolean isBiometricWeakInstalled() {
851 // Check that the system flag was set
Jim Millercb3521e2011-10-03 20:42:26 -0700852 if (!OPTION_ENABLE_FACELOCK.equals(getString(LOCKSCREEN_OPTIONS))) {
Danielle Millett58396982011-09-30 13:55:07 -0400853 return false;
854 }
855
856 // Check that it's installed
857 PackageManager pm = mContext.getPackageManager();
858 try {
859 pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES);
860 } catch (PackageManager.NameNotFoundException e) {
861 return false;
862 }
863
864 // Check that the camera is enabled
865 if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
866 return false;
867 }
868 if (getDevicePolicyManager().getCameraDisabled(null)) {
869 return false;
870 }
871
872
873 return true;
Jim Miller6edf2632011-09-05 16:03:14 -0700874 }
875
876 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 * Set whether the lock pattern is enabled.
878 */
879 public void setLockPatternEnabled(boolean enabled) {
Amith Yamasani156c4352010-03-05 17:10:03 -0800880 setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 }
882
883 /**
884 * @return Whether the visible pattern is enabled.
885 */
886 public boolean isVisiblePatternEnabled() {
Amith Yamasani156c4352010-03-05 17:10:03 -0800887 return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 }
889
890 /**
891 * Set whether the visible pattern is enabled.
892 */
893 public void setVisiblePatternEnabled(boolean enabled) {
Amith Yamasani156c4352010-03-05 17:10:03 -0800894 setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 }
896
897 /**
898 * @return Whether tactile feedback for the pattern is enabled.
899 */
900 public boolean isTactileFeedbackEnabled() {
Amith Yamasani156c4352010-03-05 17:10:03 -0800901 return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 }
903
904 /**
905 * Set whether tactile feedback for the pattern is enabled.
906 */
907 public void setTactileFeedbackEnabled(boolean enabled) {
Amith Yamasani156c4352010-03-05 17:10:03 -0800908 setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 }
910
911 /**
912 * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
913 * pattern until the deadline has passed.
914 * @return the chosen deadline.
915 */
916 public long setLockoutAttemptDeadline() {
917 final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
918 setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline);
919 return deadline;
920 }
921
922 /**
923 * @return The elapsed time in millis in the future when the user is allowed to
924 * attempt to enter his/her lock pattern, or 0 if the user is welcome to
925 * enter a pattern.
926 */
927 public long getLockoutAttemptDeadline() {
928 final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L);
929 final long now = SystemClock.elapsedRealtime();
930 if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
931 return 0L;
932 }
933 return deadline;
934 }
935
936 /**
937 * @return Whether the user is permanently locked out until they verify their
938 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
939 * attempts.
940 */
941 public boolean isPermanentlyLocked() {
942 return getBoolean(LOCKOUT_PERMANENT_KEY);
943 }
944
945 /**
946 * Set the state of whether the device is permanently locked, meaning the user
Karl Rosaen678771b2009-08-21 14:00:26 -0700947 * must authenticate via other means.
948 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 * @param locked Whether the user is permanently locked out until they verify their
950 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
951 * attempts.
952 */
953 public void setPermanentlyLocked(boolean locked) {
954 setBoolean(LOCKOUT_PERMANENT_KEY, locked);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 }
956
John Wang0f7b3f82011-05-31 11:20:55 -0700957 public boolean isEmergencyCallCapable() {
958 return mContext.getResources().getBoolean(
959 com.android.internal.R.bool.config_voice_capable);
960 }
961
962 public boolean isPukUnlockScreenEnable() {
963 return mContext.getResources().getBoolean(
964 com.android.internal.R.bool.config_enable_puk_unlock_screen);
965 }
966
Jim Miller1f56edc2011-11-07 19:00:48 -0800967 public boolean isEmergencyCallEnabledWhileSimLocked() {
968 return mContext.getResources().getBoolean(
969 com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
970 }
971
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 /**
973 * @return A formatted string of the next alarm (for showing on the lock screen),
974 * or null if there is no next alarm.
975 */
976 public String getNextAlarm() {
977 String nextAlarm = Settings.System.getString(mContentResolver,
978 Settings.System.NEXT_ALARM_FORMATTED);
979 if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
980 return null;
981 }
982 return nextAlarm;
983 }
984
Amith Yamasani156c4352010-03-05 17:10:03 -0800985 private boolean getBoolean(String secureSettingKey) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 return 1 ==
Amith Yamasani156c4352010-03-05 17:10:03 -0800987 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 }
989
Amith Yamasani156c4352010-03-05 17:10:03 -0800990 private void setBoolean(String secureSettingKey, boolean enabled) {
991 android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
992 enabled ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 }
994
Amith Yamasani156c4352010-03-05 17:10:03 -0800995 private long getLong(String secureSettingKey, long def) {
996 return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 }
998
Amith Yamasani156c4352010-03-05 17:10:03 -0800999 private void setLong(String secureSettingKey, long value) {
1000 android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 }
1002
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -07001003 private String getString(String secureSettingKey) {
1004 return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
1005 }
1006
1007 private void setString(String secureSettingKey, String value) {
1008 android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
1009 }
1010
Jim Miller69aa4a92009-12-22 19:03:28 -08001011 public boolean isSecure() {
Jim Millercd709882010-03-25 18:24:02 -07001012 long mode = getKeyguardStoredPasswordQuality();
1013 final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1014 final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1015 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001016 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1017 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
Jim Millercd709882010-03-25 18:24:02 -07001018 final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
Danielle Millett73da5fe2011-09-13 16:20:05 -04001019 || isPassword && savedPasswordExists();
Jim Miller69aa4a92009-12-22 19:03:28 -08001020 return secure;
1021 }
Jim Miller69ac9882010-02-24 15:35:05 -08001022
1023 /**
John Wang0f7b3f82011-05-31 11:20:55 -07001024 * Sets the emergency button visibility based on isEmergencyCallCapable().
1025 *
1026 * If the emergency button is visible, sets the text on the emergency button
1027 * to indicate what action will be taken.
1028 *
Jim Miller69ac9882010-02-24 15:35:05 -08001029 * If there's currently a call in progress, the button will take them to the call
1030 * @param button the button to update
Jim Miller3f5f83b2011-09-26 15:17:05 -07001031 * @param the phone state:
1032 * {@link TelephonyManager#CALL_STATE_IDLE}
1033 * {@link TelephonyManager#CALL_STATE_RINGING}
1034 * {@link TelephonyManager#CALL_STATE_OFFHOOK}
Jim Miller1f56edc2011-11-07 19:00:48 -08001035 * @param shown indicates whether the given screen wants the emergency button to show at all
Jim Miller69ac9882010-02-24 15:35:05 -08001036 */
Jim Miller1f56edc2011-11-07 19:00:48 -08001037 public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) {
1038 if (isEmergencyCallCapable() && shown) {
John Wang0f7b3f82011-05-31 11:20:55 -07001039 button.setVisibility(View.VISIBLE);
1040 } else {
1041 button.setVisibility(View.GONE);
1042 return;
1043 }
1044
Jim Miller69ac9882010-02-24 15:35:05 -08001045 int textId;
Jim Miller3f5f83b2011-09-26 15:17:05 -07001046 if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
Jim Miller69ac9882010-02-24 15:35:05 -08001047 // show "return to call" text and show phone icon
1048 textId = R.string.lockscreen_return_to_call;
1049 int phoneCallIcon = R.drawable.stat_sys_phone_call;
1050 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
1051 } else {
1052 textId = R.string.lockscreen_emergency_call;
1053 int emergencyIcon = R.drawable.ic_emergency;
1054 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
1055 }
1056 button.setText(textId);
1057 }
1058
1059 /**
1060 * Resumes a call in progress. Typically launched from the EmergencyCall button
1061 * on various lockscreens.
1062 *
1063 * @return true if we were able to tell InCallScreen to show.
1064 */
1065 public boolean resumeCall() {
1066 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1067 try {
1068 if (phone != null && phone.showCallScreen()) {
1069 return true;
1070 }
1071 } catch (RemoteException e) {
1072 // What can we do?
1073 }
1074 return false;
1075 }
Danielle Millett044a0a72011-11-07 15:42:12 -05001076
1077 private void finishBiometricWeak() {
1078 setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
1079
1080 // Launch intent to show final screen, this also
1081 // moves the temporary gallery to the actual gallery
1082 Intent intent = new Intent();
1083 intent.setClassName("com.android.facelock",
1084 "com.android.facelock.SetupEndScreen");
1085 mContext.startActivity(intent);
1086 }
1087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088}