1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.widget; 18 19 import android.app.admin.DevicePolicyManager; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.os.RemoteException; 23 import android.os.ServiceManager; 24 import android.os.SystemClock; 25 import android.provider.Settings; 26 import android.security.MessageDigest; 27 import android.telephony.TelephonyManager; 28 import android.text.TextUtils; 29 import android.util.Log; 30 import android.widget.Button; 31 32 import com.android.internal.R; 33 import com.android.internal.telephony.ITelephony; 34 import com.google.android.collect.Lists; 35 36 import java.io.FileNotFoundException; 37 import java.io.IOException; 38 import java.io.RandomAccessFile; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.SecureRandom; 41 import java.util.Arrays; 42 import java.util.List; 43 44 /** 45 * Utilities for the lock patten and its settings. 46 */ 47 public class LockPatternUtils { 48 49 private static final String TAG = "LockPatternUtils"; 50 51 private static final String LOCK_PATTERN_FILE = "/system/gesture.key"; 52 private static final String LOCK_PASSWORD_FILE = "/system/password.key"; 53 54 /** 55 * The maximum number of incorrect attempts before the user is prevented 56 * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}. 57 */ 58 public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5; 59 60 /** 61 * The number of incorrect attempts before which we fall back on an alternative 62 * method of verifying the user, and resetting their lock pattern. 63 */ 64 public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20; 65 66 /** 67 * How long the user is prevented from trying again after entering the 68 * wrong pattern too many times. 69 */ 70 public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L; 71 72 /** 73 * The interval of the countdown for showing progress of the lockout. 74 */ 75 public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L; 76 77 /** 78 * The minimum number of dots in a valid pattern. 79 */ 80 public static final int MIN_LOCK_PATTERN_SIZE = 4; 81 82 /** 83 * The minimum number of dots the user must include in a wrong pattern 84 * attempt for it to be counted against the counts that affect 85 * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET} 86 */ 87 public static final int MIN_PATTERN_REGISTER_FAIL = 3; 88 89 private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; 90 private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; 91 private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; 92 public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; 93 private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; 94 95 private final Context mContext; 96 private final ContentResolver mContentResolver; 97 private DevicePolicyManager mDevicePolicyManager; 98 private static String sLockPatternFilename; 99 private static String sLockPasswordFilename; 100 101 public DevicePolicyManager getDevicePolicyManager() { 102 if (mDevicePolicyManager == null) { 103 mDevicePolicyManager = 104 (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 105 if (mDevicePolicyManager == null) { 106 Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?", 107 new IllegalStateException("Stack trace:")); 108 } 109 } 110 return mDevicePolicyManager; 111 } 112 /** 113 * @param contentResolver Used to look up and save settings. 114 */ 115 public LockPatternUtils(Context context) { 116 mContext = context; 117 mContentResolver = context.getContentResolver(); 118 // Initialize the location of gesture lock file 119 if (sLockPatternFilename == null) { 120 sLockPatternFilename = android.os.Environment.getDataDirectory() 121 .getAbsolutePath() + LOCK_PATTERN_FILE; 122 sLockPasswordFilename = android.os.Environment.getDataDirectory() 123 .getAbsolutePath() + LOCK_PASSWORD_FILE; 124 } 125 126 } 127 128 public int getRequestedMinimumPasswordLength() { 129 return getDevicePolicyManager().getPasswordMinimumLength(null); 130 } 131 132 133 /** 134 * Gets the device policy password mode. If the mode is non-specific, returns 135 * MODE_PATTERN which allows the user to choose anything. 136 */ 137 public int getRequestedPasswordQuality() { 138 return getDevicePolicyManager().getPasswordQuality(null); 139 } 140 141 /** 142 * Returns the actual password mode, as set by keyguard after updating the password. 143 * 144 * @return 145 */ 146 public void reportFailedPasswordAttempt() { 147 getDevicePolicyManager().reportFailedPasswordAttempt(); 148 } 149 150 public void reportSuccessfulPasswordAttempt() { 151 getDevicePolicyManager().reportSuccessfulPasswordAttempt(); 152 } 153 154 /** 155 * Check to see if a pattern matches the saved pattern. If no pattern exists, 156 * always returns true. 157 * @param pattern The pattern to check. 158 * @return Whether the pattern matches the stored one. 159 */ 160 public boolean checkPattern(List<LockPatternView.Cell> pattern) { 161 try { 162 // Read all the bytes from the file 163 RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r"); 164 final byte[] stored = new byte[(int) raf.length()]; 165 int got = raf.read(stored, 0, stored.length); 166 raf.close(); 167 if (got <= 0) { 168 return true; 169 } 170 // Compare the hash from the file with the entered pattern's hash 171 return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern)); 172 } catch (FileNotFoundException fnfe) { 173 return true; 174 } catch (IOException ioe) { 175 return true; 176 } 177 } 178 179 /** 180 * Check to see if a password matches the saved password. If no password exists, 181 * always returns true. 182 * @param password The password to check. 183 * @return Whether the password matches the stored one. 184 */ 185 public boolean checkPassword(String password) { 186 try { 187 // Read all the bytes from the file 188 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r"); 189 final byte[] stored = new byte[(int) raf.length()]; 190 int got = raf.read(stored, 0, stored.length); 191 raf.close(); 192 if (got <= 0) { 193 return true; 194 } 195 // Compare the hash from the file with the entered password's hash 196 return Arrays.equals(stored, passwordToHash(password)); 197 } catch (FileNotFoundException fnfe) { 198 return true; 199 } catch (IOException ioe) { 200 return true; 201 } 202 } 203 204 /** 205 * Checks to see if the given file exists and contains any data. Returns true if it does, 206 * false otherwise. 207 * @param filename 208 * @return true if file exists and is non-empty. 209 */ 210 private boolean nonEmptyFileExists(String filename) { 211 try { 212 // Check if we can read a byte from the file 213 RandomAccessFile raf = new RandomAccessFile(filename, "r"); 214 raf.readByte(); 215 raf.close(); 216 return true; 217 } catch (FileNotFoundException fnfe) { 218 return false; 219 } catch (IOException ioe) { 220 return false; 221 } 222 } 223 224 /** 225 * Check to see if the user has stored a lock pattern. 226 * @return Whether a saved pattern exists. 227 */ 228 public boolean savedPatternExists() { 229 return nonEmptyFileExists(sLockPatternFilename); 230 } 231 232 /** 233 * Check to see if the user has stored a lock pattern. 234 * @return Whether a saved pattern exists. 235 */ 236 public boolean savedPasswordExists() { 237 return nonEmptyFileExists(sLockPasswordFilename); 238 } 239 240 /** 241 * Return true if the user has ever chosen a pattern. This is true even if the pattern is 242 * currently cleared. 243 * 244 * @return True if the user has ever chosen a pattern. 245 */ 246 public boolean isPatternEverChosen() { 247 return getBoolean(PATTERN_EVER_CHOSEN_KEY); 248 } 249 250 /** 251 * Used by device policy manager to validate the current password 252 * information it has. 253 */ 254 public int getActivePasswordQuality() { 255 int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 256 switch (getKeyguardStoredPasswordQuality()) { 257 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 258 if (isLockPatternEnabled()) { 259 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 260 } 261 break; 262 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 263 if (isLockPasswordEnabled()) { 264 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 265 } 266 break; 267 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 268 if (isLockPasswordEnabled()) { 269 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 270 } 271 break; 272 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 273 if (isLockPasswordEnabled()) { 274 activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 275 } 276 break; 277 } 278 return activePasswordQuality; 279 } 280 281 /** 282 * Clear any lock pattern or password. 283 */ 284 public void clearLock() { 285 getDevicePolicyManager().setActivePasswordState( 286 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); 287 saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 288 setLockPatternEnabled(false); 289 saveLockPattern(null); 290 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 291 } 292 293 /** 294 * Save a lock pattern. 295 * @param pattern The new pattern to save. 296 */ 297 public void saveLockPattern(List<LockPatternView.Cell> pattern) { 298 // Compute the hash 299 final byte[] hash = LockPatternUtils.patternToHash(pattern); 300 try { 301 // Write the hash to file 302 RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw"); 303 // Truncate the file if pattern is null, to clear the lock 304 if (pattern == null) { 305 raf.setLength(0); 306 } else { 307 raf.write(hash, 0, hash.length); 308 } 309 raf.close(); 310 DevicePolicyManager dpm = getDevicePolicyManager(); 311 if (pattern != null) { 312 setBoolean(PATTERN_EVER_CHOSEN_KEY, true); 313 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 314 dpm.setActivePasswordState( 315 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size()); 316 } else { 317 dpm.setActivePasswordState( 318 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); 319 } 320 } catch (FileNotFoundException fnfe) { 321 // Cant do much, unless we want to fail over to using the settings provider 322 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename); 323 } catch (IOException ioe) { 324 // Cant do much 325 Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename); 326 } 327 } 328 329 /** 330 * Compute the password quality from the given password string. 331 */ 332 static public int computePasswordQuality(String password) { 333 boolean hasDigit = false; 334 boolean hasNonDigit = false; 335 final int len = password.length(); 336 for (int i = 0; i < len; i++) { 337 if (Character.isDigit(password.charAt(i))) { 338 hasDigit = true; 339 } else { 340 hasNonDigit = true; 341 } 342 } 343 344 if (hasNonDigit && hasDigit) { 345 return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 346 } 347 if (hasNonDigit) { 348 return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 349 } 350 if (hasDigit) { 351 return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 352 } 353 return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 354 } 355 356 /** 357 * Save a lock password. Does not ensure that the password is as good 358 * as the requested mode, but will adjust the mode to be as good as the 359 * pattern. 360 * @param password The password to save 361 * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} 362 */ 363 public void saveLockPassword(String password, int quality) { 364 // Compute the hash 365 final byte[] hash = passwordToHash(password); 366 try { 367 // Write the hash to file 368 RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw"); 369 // Truncate the file if pattern is null, to clear the lock 370 if (password == null) { 371 raf.setLength(0); 372 } else { 373 raf.write(hash, 0, hash.length); 374 } 375 raf.close(); 376 DevicePolicyManager dpm = getDevicePolicyManager(); 377 if (password != null) { 378 int computedQuality = computePasswordQuality(password); 379 setLong(PASSWORD_TYPE_KEY, computedQuality); 380 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { 381 dpm.setActivePasswordState(computedQuality, password.length()); 382 } else { 383 // The password is not anything. 384 dpm.setActivePasswordState( 385 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); 386 } 387 } else { 388 dpm.setActivePasswordState( 389 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0); 390 } 391 } catch (FileNotFoundException fnfe) { 392 // Cant do much, unless we want to fail over to using the settings provider 393 Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename); 394 } catch (IOException ioe) { 395 // Cant do much 396 Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename); 397 } 398 } 399 400 /** 401 * Retrieves the quality mode we're in. 402 * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} 403 * 404 * @return stored password quality 405 */ 406 public int getKeyguardStoredPasswordQuality() { 407 return (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 408 } 409 410 /** 411 * Deserialize a pattern. 412 * @param string The pattern serialized with {@link #patternToString} 413 * @return The pattern. 414 */ 415 public static List<LockPatternView.Cell> stringToPattern(String string) { 416 List<LockPatternView.Cell> result = Lists.newArrayList(); 417 418 final byte[] bytes = string.getBytes(); 419 for (int i = 0; i < bytes.length; i++) { 420 byte b = bytes[i]; 421 result.add(LockPatternView.Cell.of(b / 3, b % 3)); 422 } 423 return result; 424 } 425 426 /** 427 * Serialize a pattern. 428 * @param pattern The pattern. 429 * @return The pattern in string form. 430 */ 431 public static String patternToString(List<LockPatternView.Cell> pattern) { 432 if (pattern == null) { 433 return ""; 434 } 435 final int patternSize = pattern.size(); 436 437 byte[] res = new byte[patternSize]; 438 for (int i = 0; i < patternSize; i++) { 439 LockPatternView.Cell cell = pattern.get(i); 440 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); 441 } 442 return new String(res); 443 } 444 445 /* 446 * Generate an SHA-1 hash for the pattern. Not the most secure, but it is 447 * at least a second level of protection. First level is that the file 448 * is in a location only readable by the system process. 449 * @param pattern the gesture pattern. 450 * @return the hash of the pattern in a byte array. 451 */ 452 private static byte[] patternToHash(List<LockPatternView.Cell> pattern) { 453 if (pattern == null) { 454 return null; 455 } 456 457 final int patternSize = pattern.size(); 458 byte[] res = new byte[patternSize]; 459 for (int i = 0; i < patternSize; i++) { 460 LockPatternView.Cell cell = pattern.get(i); 461 res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); 462 } 463 try { 464 MessageDigest md = MessageDigest.getInstance("SHA-1"); 465 byte[] hash = md.digest(res); 466 return hash; 467 } catch (NoSuchAlgorithmException nsa) { 468 return res; 469 } 470 } 471 472 private String getSalt() { 473 long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0); 474 if (salt == 0) { 475 try { 476 salt = SecureRandom.getInstance("SHA1PRNG").nextLong(); 477 setLong(LOCK_PASSWORD_SALT_KEY, salt); 478 Log.v(TAG, "Initialized lock password salt"); 479 } catch (NoSuchAlgorithmException e) { 480 // Throw an exception rather than storing a password we'll never be able to recover 481 throw new IllegalStateException("Couldn't get SecureRandom number", e); 482 } 483 } 484 return Long.toHexString(salt); 485 } 486 487 /* 488 * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash. 489 * Not the most secure, but it is at least a second level of protection. First level is that 490 * the file is in a location only readable by the system process. 491 * @param password the gesture pattern. 492 * @return the hash of the pattern in a byte array. 493 */ 494 public byte[] passwordToHash(String password) { 495 if (password == null) { 496 return null; 497 } 498 String algo = null; 499 byte[] hashed = null; 500 try { 501 byte[] saltedPassword = (password + getSalt()).getBytes(); 502 byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword); 503 byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword); 504 hashed = (toHex(sha1) + toHex(md5)).getBytes(); 505 } catch (NoSuchAlgorithmException e) { 506 Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo); 507 } 508 return hashed; 509 } 510 511 private static String toHex(byte[] ary) { 512 final String hex = "0123456789ABCDEF"; 513 String ret = ""; 514 for (int i = 0; i < ary.length; i++) { 515 ret += hex.charAt((ary[i] >> 4) & 0xf); 516 ret += hex.charAt(ary[i] & 0xf); 517 } 518 return ret; 519 } 520 521 /** 522 * @return Whether the lock password is enabled. 523 */ 524 public boolean isLockPasswordEnabled() { 525 long mode = getLong(PASSWORD_TYPE_KEY, 0); 526 return savedPasswordExists() && 527 (mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC 528 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC 529 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC); 530 } 531 532 /** 533 * @return Whether the lock pattern is enabled. 534 */ 535 public boolean isLockPatternEnabled() { 536 return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED) 537 && getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) 538 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 539 } 540 541 /** 542 * Set whether the lock pattern is enabled. 543 */ 544 public void setLockPatternEnabled(boolean enabled) { 545 setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled); 546 } 547 548 /** 549 * @return Whether the visible pattern is enabled. 550 */ 551 public boolean isVisiblePatternEnabled() { 552 return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE); 553 } 554 555 /** 556 * Set whether the visible pattern is enabled. 557 */ 558 public void setVisiblePatternEnabled(boolean enabled) { 559 setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled); 560 } 561 562 /** 563 * @return Whether tactile feedback for the pattern is enabled. 564 */ 565 public boolean isTactileFeedbackEnabled() { 566 return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED); 567 } 568 569 /** 570 * Set whether tactile feedback for the pattern is enabled. 571 */ 572 public void setTactileFeedbackEnabled(boolean enabled) { 573 setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled); 574 } 575 576 /** 577 * Set and store the lockout deadline, meaning the user can't attempt his/her unlock 578 * pattern until the deadline has passed. 579 * @return the chosen deadline. 580 */ 581 public long setLockoutAttemptDeadline() { 582 final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS; 583 setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline); 584 return deadline; 585 } 586 587 /** 588 * @return The elapsed time in millis in the future when the user is allowed to 589 * attempt to enter his/her lock pattern, or 0 if the user is welcome to 590 * enter a pattern. 591 */ 592 public long getLockoutAttemptDeadline() { 593 final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L); 594 final long now = SystemClock.elapsedRealtime(); 595 if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) { 596 return 0L; 597 } 598 return deadline; 599 } 600 601 /** 602 * @return Whether the user is permanently locked out until they verify their 603 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed 604 * attempts. 605 */ 606 public boolean isPermanentlyLocked() { 607 return getBoolean(LOCKOUT_PERMANENT_KEY); 608 } 609 610 /** 611 * Set the state of whether the device is permanently locked, meaning the user 612 * must authenticate via other means. 613 * 614 * @param locked Whether the user is permanently locked out until they verify their 615 * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed 616 * attempts. 617 */ 618 public void setPermanentlyLocked(boolean locked) { 619 setBoolean(LOCKOUT_PERMANENT_KEY, locked); 620 } 621 622 /** 623 * @return A formatted string of the next alarm (for showing on the lock screen), 624 * or null if there is no next alarm. 625 */ 626 public String getNextAlarm() { 627 String nextAlarm = Settings.System.getString(mContentResolver, 628 Settings.System.NEXT_ALARM_FORMATTED); 629 if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) { 630 return null; 631 } 632 return nextAlarm; 633 } 634 635 private boolean getBoolean(String secureSettingKey) { 636 return 1 == 637 android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0); 638 } 639 640 private void setBoolean(String secureSettingKey, boolean enabled) { 641 android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey, 642 enabled ? 1 : 0); 643 } 644 645 private long getLong(String secureSettingKey, long def) { 646 return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def); 647 } 648 649 private void setLong(String secureSettingKey, long value) { 650 android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value); 651 } 652 653 public boolean isSecure() { 654 long mode = getKeyguardStoredPasswordQuality(); 655 final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 656 final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC 657 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC 658 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 659 final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() 660 || isPassword && savedPasswordExists(); 661 return secure; 662 } 663 664 /** 665 * Sets the text on the emergency button to indicate what action will be taken. 666 * If there's currently a call in progress, the button will take them to the call 667 * @param button the button to update 668 */ 669 public void updateEmergencyCallButtonState(Button button) { 670 int newState = TelephonyManager.getDefault().getCallState(); 671 int textId; 672 if (newState == TelephonyManager.CALL_STATE_OFFHOOK) { 673 // show "return to call" text and show phone icon 674 textId = R.string.lockscreen_return_to_call; 675 int phoneCallIcon = R.drawable.stat_sys_phone_call; 676 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0); 677 } else { 678 textId = R.string.lockscreen_emergency_call; 679 int emergencyIcon = R.drawable.ic_emergency; 680 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0); 681 } 682 button.setText(textId); 683 } 684 685 /** 686 * Resumes a call in progress. Typically launched from the EmergencyCall button 687 * on various lockscreens. 688 * 689 * @return true if we were able to tell InCallScreen to show. 690 */ 691 public boolean resumeCall() { 692 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 693 try { 694 if (phone != null && phone.showCallScreen()) { 695 return true; 696 } 697 } catch (RemoteException e) { 698 // What can we do? 699 } 700 return false; 701 } 702 } 703