blob: aa46ea23afeb8a8a5435114d7ded7bd503482ce1 [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;
28import android.hardware.Camera;
29import android.hardware.Camera.CameraInfo;
Brad Fitzpatrick90881002010-08-23 18:30:08 -070030import android.os.FileObserver;
Jason parksf7b3cd42011-01-27 09:28:25 -060031import android.os.IBinder;
Jim Miller69ac9882010-02-24 15:35:05 -080032import android.os.RemoteException;
33import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.SystemClock;
Danielle Millett58396982011-09-30 13:55:07 -040035import android.os.SystemProperties;
Jason parksf7b3cd42011-01-27 09:28:25 -060036import android.os.storage.IMountService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.provider.Settings;
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -070038import android.security.KeyStore;
Jim Miller69ac9882010-02-24 15:35:05 -080039import android.telephony.TelephonyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.text.TextUtils;
41import android.util.Log;
John Wang0f7b3f82011-05-31 11:20:55 -070042import android.view.View;
Jim Miller69ac9882010-02-24 15:35:05 -080043import android.widget.Button;
John Wang5328bf02011-06-08 19:33:56 -070044import android.widget.TextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
Brad Fitzpatrick90881002010-08-23 18:30:08 -070046import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.io.FileNotFoundException;
48import java.io.IOException;
49import java.io.RandomAccessFile;
Brian Carlstrom929a1c22011-02-01 21:54:09 -080050import java.security.MessageDigest;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import java.security.NoSuchAlgorithmException;
Jim Miller11b019d2010-01-20 16:34:45 -080052import java.security.SecureRandom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import java.util.Arrays;
54import java.util.List;
Brad Fitzpatrick90881002010-08-23 18:30:08 -070055import java.util.concurrent.atomic.AtomicBoolean;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
57/**
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -070058 * Utilities for the lock pattern and its settings.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 */
60public class LockPatternUtils {
61
Jim Millercb3521e2011-10-03 20:42:26 -070062 private static final String OPTION_ENABLE_FACELOCK = "enable_facelock";
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 private static final String TAG = "LockPatternUtils";
Jim Miller69aa4a92009-12-22 19:03:28 -080065
Brad Fitzpatrick90881002010-08-23 18:30:08 -070066 private static final String SYSTEM_DIRECTORY = "/system/";
67 private static final String LOCK_PATTERN_FILE = "gesture.key";
68 private static final String LOCK_PASSWORD_FILE = "password.key";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
70 /**
71 * The maximum number of incorrect attempts before the user is prevented
72 * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
73 */
74 public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
75
76 /**
77 * The number of incorrect attempts before which we fall back on an alternative
78 * method of verifying the user, and resetting their lock pattern.
79 */
80 public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
81
82 /**
83 * How long the user is prevented from trying again after entering the
84 * wrong pattern too many times.
85 */
86 public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
87
88 /**
89 * The interval of the countdown for showing progress of the lockout.
90 */
91 public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
92
Jim Miller4f369952011-08-19 18:29:22 -070093
94 /**
95 * This dictates when we start telling the user that continued failed attempts will wipe
96 * their device.
97 */
98 public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5;
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 /**
101 * The minimum number of dots in a valid pattern.
102 */
103 public static final int MIN_LOCK_PATTERN_SIZE = 4;
104
105 /**
106 * The minimum number of dots the user must include in a wrong pattern
107 * attempt for it to be counted against the counts that affect
108 * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
109 */
Jim Miller4f369952011-08-19 18:29:22 -0700110 public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
112 private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
113 private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
Jim Miller69aa4a92009-12-22 19:03:28 -0800114 private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
115 public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
Jim Miller6edf2632011-09-05 16:03:14 -0700116 public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
Jim Miller11b019d2010-01-20 16:34:45 -0800117 private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
Jim Miller2a98a4c2010-11-19 18:49:26 -0800118 private final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
Jim Millercb3521e2011-10-03 20:42:26 -0700119 private final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
Jim Miller6edf2632011-09-05 16:03:14 -0700120 public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
121 = "lockscreen.biometric_weak_fallback";
Danielle Millett7a072192011-10-03 17:36:01 -0400122 public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
123 = "lockscreen.biometricweakeverchosen";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700125 private final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
126
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800127 private final Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 private final ContentResolver mContentResolver;
Jim Miller31f90b62010-01-20 13:35:20 -0800129 private DevicePolicyManager mDevicePolicyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 private static String sLockPatternFilename;
Jim Miller69aa4a92009-12-22 19:03:28 -0800131 private static String sLockPasswordFilename;
132
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700133 private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false);
134 private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false);
Jim Miller4f369952011-08-19 18:29:22 -0700135
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700136 private static FileObserver sPasswordObserver;
137
Dianne Hackbornde4c26f2011-07-17 13:42:47 -0700138 private static class PasswordFileObserver extends FileObserver {
139 public PasswordFileObserver(String path, int mask) {
140 super(path, mask);
141 }
142
143 @Override
144 public void onEvent(int event, String path) {
145 if (LOCK_PATTERN_FILE.equals(path)) {
146 Log.d(TAG, "lock pattern file changed");
147 sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
148 } else if (LOCK_PASSWORD_FILE.equals(path)) {
149 Log.d(TAG, "lock password file changed");
150 sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
151 }
152 }
153 }
154
Jim Millercd709882010-03-25 18:24:02 -0700155 public DevicePolicyManager getDevicePolicyManager() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800156 if (mDevicePolicyManager == null) {
157 mDevicePolicyManager =
158 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
159 if (mDevicePolicyManager == null) {
160 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
161 new IllegalStateException("Stack trace:"));
162 }
163 }
164 return mDevicePolicyManager;
165 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 /**
167 * @param contentResolver Used to look up and save settings.
168 */
Jim Miller31f90b62010-01-20 13:35:20 -0800169 public LockPatternUtils(Context context) {
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800170 mContext = context;
Jim Miller31f90b62010-01-20 13:35:20 -0800171 mContentResolver = context.getContentResolver();
Jim Miller69aa4a92009-12-22 19:03:28 -0800172
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700173 // Initialize the location of gesture & PIN lock files
174 if (sLockPatternFilename == null) {
175 String dataSystemDirectory =
176 android.os.Environment.getDataDirectory().getAbsolutePath() +
177 SYSTEM_DIRECTORY;
178 sLockPatternFilename = dataSystemDirectory + LOCK_PATTERN_FILE;
179 sLockPasswordFilename = dataSystemDirectory + LOCK_PASSWORD_FILE;
180 sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
181 sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
182 int fileObserverMask = FileObserver.CLOSE_WRITE | FileObserver.DELETE |
183 FileObserver.MOVED_TO | FileObserver.CREATE;
Dianne Hackbornde4c26f2011-07-17 13:42:47 -0700184 sPasswordObserver = new PasswordFileObserver(dataSystemDirectory, fileObserverMask);
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700185 sPasswordObserver.startWatching();
186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 }
188
Jim Miller31f90b62010-01-20 13:35:20 -0800189 public int getRequestedMinimumPasswordLength() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800190 return getDevicePolicyManager().getPasswordMinimumLength(null);
Jim Miller31f90b62010-01-20 13:35:20 -0800191 }
192
Jim Millercd709882010-03-25 18:24:02 -0700193
Jim Miller31f90b62010-01-20 13:35:20 -0800194 /**
195 * Gets the device policy password mode. If the mode is non-specific, returns
196 * MODE_PATTERN which allows the user to choose anything.
Jim Miller31f90b62010-01-20 13:35:20 -0800197 */
Jim Millercd709882010-03-25 18:24:02 -0700198 public int getRequestedPasswordQuality() {
199 return getDevicePolicyManager().getPasswordQuality(null);
Jim Miller31f90b62010-01-20 13:35:20 -0800200 }
201
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700202 public int getRequestedPasswordHistoryLength() {
203 return getDevicePolicyManager().getPasswordHistoryLength(null);
204 }
205
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700206 public int getRequestedPasswordMinimumLetters() {
207 return getDevicePolicyManager().getPasswordMinimumLetters(null);
208 }
209
210 public int getRequestedPasswordMinimumUpperCase() {
211 return getDevicePolicyManager().getPasswordMinimumUpperCase(null);
212 }
213
214 public int getRequestedPasswordMinimumLowerCase() {
215 return getDevicePolicyManager().getPasswordMinimumLowerCase(null);
216 }
217
218 public int getRequestedPasswordMinimumNumeric() {
219 return getDevicePolicyManager().getPasswordMinimumNumeric(null);
220 }
221
222 public int getRequestedPasswordMinimumSymbols() {
223 return getDevicePolicyManager().getPasswordMinimumSymbols(null);
224 }
225
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700226 public int getRequestedPasswordMinimumNonLetter() {
227 return getDevicePolicyManager().getPasswordMinimumNonLetter(null);
228 }
Jim Miller31f90b62010-01-20 13:35:20 -0800229 /**
230 * Returns the actual password mode, as set by keyguard after updating the password.
231 *
232 * @return
233 */
Jim Miller31f90b62010-01-20 13:35:20 -0800234 public void reportFailedPasswordAttempt() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800235 getDevicePolicyManager().reportFailedPasswordAttempt();
Jim Miller31f90b62010-01-20 13:35:20 -0800236 }
237
238 public void reportSuccessfulPasswordAttempt() {
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800239 getDevicePolicyManager().reportSuccessfulPasswordAttempt();
Jim Miller31f90b62010-01-20 13:35:20 -0800240 }
241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 /**
243 * Check to see if a pattern matches the saved pattern. If no pattern exists,
244 * always returns true.
245 * @param pattern The pattern to check.
Jim Miller69aa4a92009-12-22 19:03:28 -0800246 * @return Whether the pattern matches the stored one.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 */
248 public boolean checkPattern(List<LockPatternView.Cell> pattern) {
249 try {
250 // Read all the bytes from the file
251 RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
252 final byte[] stored = new byte[(int) raf.length()];
253 int got = raf.read(stored, 0, stored.length);
254 raf.close();
255 if (got <= 0) {
256 return true;
257 }
258 // Compare the hash from the file with the entered pattern's hash
259 return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern));
260 } catch (FileNotFoundException fnfe) {
261 return true;
262 } catch (IOException ioe) {
263 return true;
264 }
265 }
266
267 /**
Jim Miller69aa4a92009-12-22 19:03:28 -0800268 * Check to see if a password matches the saved password. If no password exists,
269 * always returns true.
270 * @param password The password to check.
271 * @return Whether the password matches the stored one.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 */
Jim Miller69aa4a92009-12-22 19:03:28 -0800273 public boolean checkPassword(String password) {
274 try {
275 // Read all the bytes from the file
276 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
277 final byte[] stored = new byte[(int) raf.length()];
278 int got = raf.read(stored, 0, stored.length);
279 raf.close();
280 if (got <= 0) {
281 return true;
282 }
283 // Compare the hash from the file with the entered password's hash
Jim Miller11b019d2010-01-20 16:34:45 -0800284 return Arrays.equals(stored, passwordToHash(password));
Jim Miller69aa4a92009-12-22 19:03:28 -0800285 } catch (FileNotFoundException fnfe) {
286 return true;
287 } catch (IOException ioe) {
288 return true;
289 }
290 }
291
292 /**
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700293 * Check to see if a password matches any of the passwords stored in the
294 * password history.
295 *
296 * @param password The password to check.
297 * @return Whether the password matches any in the history.
298 */
299 public boolean checkPasswordHistory(String password) {
300 String passwordHashString = new String(passwordToHash(password));
301 String passwordHistory = getString(PASSWORD_HISTORY_KEY);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700302 if (passwordHistory == null) {
303 return false;
304 }
305 // Password History may be too long...
306 int passwordHashLength = passwordHashString.length();
307 int passwordHistoryLength = getRequestedPasswordHistoryLength();
308 if(passwordHistoryLength == 0) {
309 return false;
310 }
311 int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
312 + passwordHistoryLength - 1;
313 if (passwordHistory.length() > neededPasswordHistoryLength) {
314 passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
315 }
316 return passwordHistory.contains(passwordHashString);
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -0700317 }
318
319 /**
Jim Miller69aa4a92009-12-22 19:03:28 -0800320 * Check to see if the user has stored a lock pattern.
321 * @return Whether a saved pattern exists.
322 */
323 public boolean savedPatternExists() {
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700324 return sHaveNonZeroPatternFile.get();
Jim Miller69aa4a92009-12-22 19:03:28 -0800325 }
326
327 /**
328 * Check to see if the user has stored a lock pattern.
329 * @return Whether a saved pattern exists.
330 */
331 public boolean savedPasswordExists() {
Brad Fitzpatrick90881002010-08-23 18:30:08 -0700332 return sHaveNonZeroPasswordFile.get();
Jim Miller69aa4a92009-12-22 19:03:28 -0800333 }
334
335 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700336 * Return true if the user has ever chosen a pattern. This is true even if the pattern is
337 * currently cleared.
338 *
339 * @return True if the user has ever chosen a pattern.
340 */
341 public boolean isPatternEverChosen() {
Jim Miller69aa4a92009-12-22 19:03:28 -0800342 return getBoolean(PATTERN_EVER_CHOSEN_KEY);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700343 }
344
345 /**
Danielle Millett7a072192011-10-03 17:36:01 -0400346 * Return true if the user has ever chosen biometric weak. This is true even if biometric
347 * weak is not current set.
348 *
349 * @return True if the user has ever chosen biometric weak.
350 */
351 public boolean isBiometricWeakEverChosen() {
352 return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY);
353 }
354
355 /**
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700356 * Used by device policy manager to validate the current password
357 * information it has.
358 */
359 public int getActivePasswordQuality() {
Jim Millercd709882010-03-25 18:24:02 -0700360 int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
361 switch (getKeyguardStoredPasswordQuality()) {
362 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;
367 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700368 if (isLockPasswordEnabled()) {
Jim Millercd709882010-03-25 18:24:02 -0700369 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700370 }
Jim Millercd709882010-03-25 18:24:02 -0700371 break;
372 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700373 if (isLockPasswordEnabled()) {
Jim Millercd709882010-03-25 18:24:02 -0700374 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700375 }
Jim Millercd709882010-03-25 18:24:02 -0700376 break;
377 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
378 if (isLockPasswordEnabled()) {
379 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
380 }
381 break;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700382 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
383 if (isLockPasswordEnabled()) {
384 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
385 }
386 break;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700387 }
Jim Millercd709882010-03-25 18:24:02 -0700388 return activePasswordQuality;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700389 }
Jim Millercd709882010-03-25 18:24:02 -0700390
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700391 /**
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800392 * Clear any lock pattern or password.
393 */
Steven Ross329979c2011-09-28 11:42:56 -0400394 public void clearLock(boolean isFallback) {
395 if(!isFallback) deleteGallery();
Jim Millercd709882010-03-25 18:24:02 -0700396 saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800397 setLockPatternEnabled(false);
398 saveLockPattern(null);
Jim Millercd709882010-03-25 18:24:02 -0700399 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Jim Miller6edf2632011-09-05 16:03:14 -0700400 setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800401 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800402
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800403 /**
Jim Miller2a98a4c2010-11-19 18:49:26 -0800404 * Disable showing lock screen at all when the DevicePolicyManager allows it.
405 * This is only meaningful if pattern, pin or password are not set.
406 *
407 * @param disable Disables lock screen when true
408 */
409 public void setLockScreenDisabled(boolean disable) {
410 setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0);
411 }
412
413 /**
414 * Determine if LockScreen can be disabled. This is used, for example, to tell if we should
415 * show LockScreen or go straight to the home screen.
416 *
417 * @return true if lock screen is can be disabled
418 */
419 public boolean isLockScreenDisabled() {
420 return !isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0;
421 }
422
423 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 * Save a lock pattern.
425 * @param pattern The new pattern to save.
426 */
427 public void saveLockPattern(List<LockPatternView.Cell> pattern) {
Jim Miller6edf2632011-09-05 16:03:14 -0700428 this.saveLockPattern(pattern, false);
429 }
430
431 /**
Steven Ross329979c2011-09-28 11:42:56 -0400432 * Calls back SetupFaceLock to save the temporary gallery file if this is the backup lock.
433 * This doesn't have to verify that biometric is enabled because it's only called in that case
434 */
435 void moveTempGallery() {
436 Intent intent = new Intent().setClassName("com.android.facelock",
437 "com.android.facelock.SetupFaceLock");
438 intent.putExtra("moveTempGallery", true);
439 mContext.startActivity(intent);
440 }
441
442 /**
Steven Ross3553c292011-09-30 15:48:40 -0400443 * Calls back SetupFaceLock to delete the temporary gallery file
Steven Ross329979c2011-09-28 11:42:56 -0400444 */
445 public void deleteTempGallery() {
Steven Ross3553c292011-09-30 15:48:40 -0400446 Intent intent = new Intent().setClassName("com.android.facelock",
447 "com.android.facelock.SetupFaceLock");
448 intent.putExtra("deleteTempGallery", true);
449 mContext.startActivity(intent);
Steven Ross329979c2011-09-28 11:42:56 -0400450 }
451
452 /**
453 * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
454 */
455 void deleteGallery() {
Danielle Millett58396982011-09-30 13:55:07 -0400456 if(usingBiometricWeak()) {
Steven Ross329979c2011-09-28 11:42:56 -0400457 Intent intent = new Intent().setClassName("com.android.facelock",
458 "com.android.facelock.SetupFaceLock");
459 intent.putExtra("deleteGallery", true);
460 mContext.startActivity(intent);
461 }
462 }
463
464 /**
Jim Miller6edf2632011-09-05 16:03:14 -0700465 * Save a lock pattern.
466 * @param pattern The new pattern to save.
467 * @param isFallback Specifies if this is a fallback to biometric weak
468 */
469 public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 // Compute the hash
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700471 final byte[] hash = LockPatternUtils.patternToHash(pattern);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 try {
473 // Write the hash to file
474 RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw");
475 // Truncate the file if pattern is null, to clear the lock
476 if (pattern == null) {
477 raf.setLength(0);
478 } else {
479 raf.write(hash, 0, hash.length);
480 }
481 raf.close();
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800482 DevicePolicyManager dpm = getDevicePolicyManager();
Brian Carlstrome2afc242011-06-02 16:21:55 -0700483 KeyStore keyStore = KeyStore.getInstance();
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800484 if (pattern != null) {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700485 keyStore.password(patternToString(pattern));
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800486 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
Jim Miller6edf2632011-09-05 16:03:14 -0700487 if (!isFallback) {
Steven Ross329979c2011-09-28 11:42:56 -0400488 deleteGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700489 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
490 } else {
491 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
492 setLong(PASSWORD_TYPE_ALTERNATE_KEY,
493 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
Danielle Millett7a072192011-10-03 17:36:01 -0400494 setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
Steven Ross329979c2011-09-28 11:42:56 -0400495 moveTempGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700496 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700497 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700498 .size(), 0, 0, 0, 0, 0, 0);
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800499 } else {
Brian Carlstrome2afc242011-06-02 16:21:55 -0700500 if (keyStore.isEmpty()) {
501 keyStore.reset();
502 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700503 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700504 0, 0, 0, 0, 0);
Jim Miller31f90b62010-01-20 13:35:20 -0800505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 } catch (FileNotFoundException fnfe) {
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700507 // Cant do much, unless we want to fail over to using the settings
508 // provider
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
510 } catch (IOException ioe) {
511 // Cant do much
512 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
513 }
514 }
515
516 /**
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700517 * Compute the password quality from the given password string.
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800518 */
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700519 static public int computePasswordQuality(String password) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800520 boolean hasDigit = false;
521 boolean hasNonDigit = false;
522 final int len = password.length();
523 for (int i = 0; i < len; i++) {
524 if (Character.isDigit(password.charAt(i))) {
525 hasDigit = true;
526 } else {
527 hasNonDigit = true;
528 }
529 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800530
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700531 if (hasNonDigit && hasDigit) {
532 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800533 }
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800534 if (hasNonDigit) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700535 return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800536 }
537 if (hasDigit) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700538 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800539 }
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700540 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800541 }
Jim Miller5b0fb3a2010-02-23 13:46:35 -0800542
Jason parksf7b3cd42011-01-27 09:28:25 -0600543 /** Update the encryption password if it is enabled **/
544 private void updateEncryptionPassword(String password) {
545 DevicePolicyManager dpm = getDevicePolicyManager();
546 if (dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
547 return;
548 }
549
550 IBinder service = ServiceManager.getService("mount");
551 if (service == null) {
552 Log.e(TAG, "Could not find the mount service to update the encryption password");
553 return;
554 }
555
556 IMountService mountService = IMountService.Stub.asInterface(service);
557 try {
558 mountService.changeEncryptionPassword(password);
559 } catch (RemoteException e) {
560 Log.e(TAG, "Error changing encryption password", e);
561 }
562 }
563
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800564 /**
Jim Millercd709882010-03-25 18:24:02 -0700565 * Save a lock password. Does not ensure that the password is as good
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800566 * as the requested mode, but will adjust the mode to be as good as the
567 * pattern.
Jim Miller69aa4a92009-12-22 19:03:28 -0800568 * @param password The password to save
Jim Millercd709882010-03-25 18:24:02 -0700569 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
Jim Miller69aa4a92009-12-22 19:03:28 -0800570 */
Jim Millercd709882010-03-25 18:24:02 -0700571 public void saveLockPassword(String password, int quality) {
Jim Miller6edf2632011-09-05 16:03:14 -0700572 this.saveLockPassword(password, quality, false);
573 }
574
575 /**
576 * Save a lock password. Does not ensure that the password is as good
577 * as the requested mode, but will adjust the mode to be as good as the
578 * pattern.
579 * @param password The password to save
580 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
581 * @param isFallback Specifies if this is a fallback to biometric weak
582 */
583 public void saveLockPassword(String password, int quality, boolean isFallback) {
Jim Miller69aa4a92009-12-22 19:03:28 -0800584 // Compute the hash
Jim Miller11b019d2010-01-20 16:34:45 -0800585 final byte[] hash = passwordToHash(password);
Jim Miller69aa4a92009-12-22 19:03:28 -0800586 try {
587 // Write the hash to file
588 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
589 // Truncate the file if pattern is null, to clear the lock
590 if (password == null) {
591 raf.setLength(0);
592 } else {
593 raf.write(hash, 0, hash.length);
594 }
595 raf.close();
Dianne Hackborn2509d3c2010-03-08 12:54:25 -0800596 DevicePolicyManager dpm = getDevicePolicyManager();
Brian Carlstrome2afc242011-06-02 16:21:55 -0700597 KeyStore keyStore = KeyStore.getInstance();
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800598 if (password != null) {
Jason parksf7b3cd42011-01-27 09:28:25 -0600599 // Update the encryption password.
600 updateEncryptionPassword(password);
601
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700602 // Update the keystore password
Brian Carlstrome2afc242011-06-02 16:21:55 -0700603 keyStore.password(password);
Brian Carlstrom5cfee3f2011-05-31 01:00:15 -0700604
Jim Millercd709882010-03-25 18:24:02 -0700605 int computedQuality = computePasswordQuality(password);
Jim Miller6edf2632011-09-05 16:03:14 -0700606 if (!isFallback) {
Steven Ross329979c2011-09-28 11:42:56 -0400607 deleteGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700608 setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality));
609 } else {
610 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
611 setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality));
Danielle Millett7a072192011-10-03 17:36:01 -0400612 setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
Steven Ross329979c2011-09-28 11:42:56 -0400613 moveTempGallery();
Jim Miller6edf2632011-09-05 16:03:14 -0700614 }
Jim Millercd709882010-03-25 18:24:02 -0700615 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700616 int letters = 0;
617 int uppercase = 0;
618 int lowercase = 0;
619 int numbers = 0;
620 int symbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700621 int nonletter = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700622 for (int i = 0; i < password.length(); i++) {
623 char c = password.charAt(i);
624 if (c >= 'A' && c <= 'Z') {
625 letters++;
626 uppercase++;
627 } else if (c >= 'a' && c <= 'z') {
628 letters++;
629 lowercase++;
630 } else if (c >= '0' && c <= '9') {
631 numbers++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700632 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700633 } else {
634 symbols++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700635 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700636 }
637 }
638 dpm.setActivePasswordState(Math.max(quality, computedQuality), password
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700639 .length(), letters, uppercase, lowercase, numbers, symbols, nonletter);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700640 } else {
641 // The password is not anything.
642 dpm.setActivePasswordState(
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700643 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 /**
975 * @return A formatted string of the next alarm (for showing on the lock screen),
976 * or null if there is no next alarm.
977 */
978 public String getNextAlarm() {
979 String nextAlarm = Settings.System.getString(mContentResolver,
980 Settings.System.NEXT_ALARM_FORMATTED);
981 if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
982 return null;
983 }
984 return nextAlarm;
985 }
986
Amith Yamasani156c4352010-03-05 17:10:03 -0800987 private boolean getBoolean(String secureSettingKey) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 return 1 ==
Amith Yamasani156c4352010-03-05 17:10:03 -0800989 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 }
991
Amith Yamasani156c4352010-03-05 17:10:03 -0800992 private void setBoolean(String secureSettingKey, boolean enabled) {
993 android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
994 enabled ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996
Amith Yamasani156c4352010-03-05 17:10:03 -0800997 private long getLong(String secureSettingKey, long def) {
998 return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 }
1000
Amith Yamasani156c4352010-03-05 17:10:03 -08001001 private void setLong(String secureSettingKey, long value) {
1002 android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 }
1004
Konstantin Lopyrev863f22d2010-05-12 17:16:58 -07001005 private String getString(String secureSettingKey) {
1006 return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
1007 }
1008
1009 private void setString(String secureSettingKey, String value) {
1010 android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
1011 }
1012
Jim Miller69aa4a92009-12-22 19:03:28 -08001013 public boolean isSecure() {
Jim Millercd709882010-03-25 18:24:02 -07001014 long mode = getKeyguardStoredPasswordQuality();
1015 final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
1016 final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
1017 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001018 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
1019 || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
Jim Millercd709882010-03-25 18:24:02 -07001020 final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
Danielle Millett73da5fe2011-09-13 16:20:05 -04001021 || isPassword && savedPasswordExists();
Jim Miller69aa4a92009-12-22 19:03:28 -08001022 return secure;
1023 }
Jim Miller69ac9882010-02-24 15:35:05 -08001024
1025 /**
John Wang0f7b3f82011-05-31 11:20:55 -07001026 * Sets the emergency button visibility based on isEmergencyCallCapable().
1027 *
1028 * If the emergency button is visible, sets the text on the emergency button
1029 * to indicate what action will be taken.
1030 *
Jim Miller69ac9882010-02-24 15:35:05 -08001031 * If there's currently a call in progress, the button will take them to the call
1032 * @param button the button to update
Jim Miller3f5f83b2011-09-26 15:17:05 -07001033 * @param the phone state:
1034 * {@link TelephonyManager#CALL_STATE_IDLE}
1035 * {@link TelephonyManager#CALL_STATE_RINGING}
1036 * {@link TelephonyManager#CALL_STATE_OFFHOOK}
Adam Cohenebcd6bb2011-09-21 16:25:33 -07001037 * @param showIfCapable indicates whether the button should be shown if emergency calls are
1038 * possible on the device
Jim Miller69ac9882010-02-24 15:35:05 -08001039 */
Jim Miller3f5f83b2011-09-26 15:17:05 -07001040 public void updateEmergencyCallButtonState(Button button, int phoneState,
1041 boolean showIfCapable) {
Adam Cohenebcd6bb2011-09-21 16:25:33 -07001042 if (isEmergencyCallCapable() && showIfCapable) {
John Wang0f7b3f82011-05-31 11:20:55 -07001043 button.setVisibility(View.VISIBLE);
1044 } else {
1045 button.setVisibility(View.GONE);
1046 return;
1047 }
1048
Jim Miller69ac9882010-02-24 15:35:05 -08001049 int textId;
Jim Miller3f5f83b2011-09-26 15:17:05 -07001050 if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
Jim Miller69ac9882010-02-24 15:35:05 -08001051 // show "return to call" text and show phone icon
1052 textId = R.string.lockscreen_return_to_call;
1053 int phoneCallIcon = R.drawable.stat_sys_phone_call;
1054 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
1055 } else {
1056 textId = R.string.lockscreen_emergency_call;
1057 int emergencyIcon = R.drawable.ic_emergency;
1058 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
1059 }
1060 button.setText(textId);
1061 }
1062
1063 /**
1064 * Resumes a call in progress. Typically launched from the EmergencyCall button
1065 * on various lockscreens.
1066 *
1067 * @return true if we were able to tell InCallScreen to show.
1068 */
1069 public boolean resumeCall() {
1070 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1071 try {
1072 if (phone != null && phone.showCallScreen()) {
1073 return true;
1074 }
1075 } catch (RemoteException e) {
1076 // What can we do?
1077 }
1078 return false;
1079 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080}