LockSettingsService.java revision 1096cf8664963a136a325b2bc511c8f381b9ba77
1/* 2 * Copyright (C) 2012 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.server; 18 19import android.content.BroadcastReceiver; 20import android.content.ContentResolver; 21import android.content.ContentValues; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.pm.PackageManager; 26import android.content.pm.UserInfo; 27 28import static android.content.Context.USER_SERVICE; 29import static android.Manifest.permission.READ_PROFILE; 30import android.database.Cursor; 31import android.database.sqlite.SQLiteDatabase; 32import android.database.sqlite.SQLiteOpenHelper; 33import android.database.sqlite.SQLiteStatement; 34import android.os.Binder; 35import android.os.Environment; 36import android.os.IBinder; 37import android.os.Process; 38import android.os.RemoteException; 39import android.os.storage.IMountService; 40import android.os.ServiceManager; 41import android.os.storage.StorageManager; 42import android.os.SystemProperties; 43import android.os.UserHandle; 44import android.os.UserManager; 45import android.provider.Settings; 46import android.provider.Settings.Secure; 47import android.provider.Settings.SettingNotFoundException; 48import android.security.KeyChain; 49import android.security.KeyChain.KeyChainConnection; 50import android.security.KeyStore; 51import android.text.TextUtils; 52import android.util.Log; 53import android.util.Slog; 54 55import com.android.internal.os.BackgroundThread; 56import com.android.internal.widget.ILockSettings; 57import com.android.internal.widget.ILockSettingsObserver; 58import com.android.internal.widget.LockPatternUtils; 59 60import java.io.File; 61import java.io.FileNotFoundException; 62import java.io.IOException; 63import java.io.RandomAccessFile; 64import java.util.ArrayList; 65import java.util.Arrays; 66import java.util.List; 67 68/** 69 * Keeps the lock pattern/password data and related settings for each user. 70 * Used by LockPatternUtils. Needs to be a service because Settings app also needs 71 * to be able to save lockscreen information for secondary users. 72 * @hide 73 */ 74public class LockSettingsService extends ILockSettings.Stub { 75 76 private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"; 77 78 private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; 79 80 private final DatabaseHelper mOpenHelper; 81 private static final String TAG = "LockSettingsService"; 82 83 private static final String TABLE = "locksettings"; 84 private static final String COLUMN_KEY = "name"; 85 private static final String COLUMN_USERID = "user"; 86 private static final String COLUMN_VALUE = "value"; 87 88 private static final String[] COLUMNS_FOR_QUERY = { 89 COLUMN_VALUE 90 }; 91 92 private static final String SYSTEM_DIRECTORY = "/system/"; 93 private static final String LOCK_PATTERN_FILE = "gesture.key"; 94 private static final String LOCK_PASSWORD_FILE = "password.key"; 95 96 private final Context mContext; 97 private LockPatternUtils mLockPatternUtils; 98 private boolean mFirstCallToVold; 99 100 private final ArrayList<LockSettingsObserver> mObservers = new ArrayList<>(); 101 102 public LockSettingsService(Context context) { 103 mContext = context; 104 // Open the database 105 mOpenHelper = new DatabaseHelper(mContext); 106 107 mLockPatternUtils = new LockPatternUtils(context); 108 mFirstCallToVold = true; 109 110 IntentFilter filter = new IntentFilter(); 111 filter.addAction(Intent.ACTION_USER_ADDED); 112 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 113 } 114 115 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 116 @Override 117 public void onReceive(Context context, Intent intent) { 118 // Update keystore settings for profiles which use the same password as their parent 119 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) { 120 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 121 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 122 final UserInfo parentInfo = um.getProfileParent(userHandle); 123 if (parentInfo != null) { 124 final KeyStore ks = KeyStore.getInstance(); 125 final int profileUid = UserHandle.getUid(userHandle, Process.SYSTEM_UID); 126 final int parentUid = UserHandle.getUid(parentInfo.id, Process.SYSTEM_UID); 127 ks.syncUid(parentUid, profileUid); 128 } 129 } 130 } 131 }; 132 133 public void systemReady() { 134 migrateOldData(); 135 } 136 137 private void migrateOldData() { 138 try { 139 // These Settings moved before multi-user was enabled, so we only have to do it for the 140 // root user. 141 if (getString("migrated", null, 0) == null) { 142 final ContentResolver cr = mContext.getContentResolver(); 143 for (String validSetting : VALID_SETTINGS) { 144 String value = Settings.Secure.getString(cr, validSetting); 145 if (value != null) { 146 setString(validSetting, value, 0); 147 } 148 } 149 // No need to move the password / pattern files. They're already in the right place. 150 setString("migrated", "true", 0); 151 Slog.i(TAG, "Migrated lock settings to new location"); 152 } 153 154 // These Settings changed after multi-user was enabled, hence need to be moved per user. 155 if (getString("migrated_user_specific", null, 0) == null) { 156 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 157 final ContentResolver cr = mContext.getContentResolver(); 158 List<UserInfo> users = um.getUsers(); 159 for (int user = 0; user < users.size(); user++) { 160 // Migrate owner info 161 final int userId = users.get(user).id; 162 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO; 163 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId); 164 if (ownerInfo != null) { 165 setString(OWNER_INFO, ownerInfo, userId); 166 Settings.Secure.putStringForUser(cr, ownerInfo, "", userId); 167 } 168 169 // Migrate owner info enabled. Note there was a bug where older platforms only 170 // stored this value if the checkbox was toggled at least once. The code detects 171 // this case by handling the exception. 172 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; 173 boolean enabled; 174 try { 175 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId); 176 enabled = ivalue != 0; 177 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId); 178 } catch (SettingNotFoundException e) { 179 // Setting was never stored. Store it if the string is not empty. 180 if (!TextUtils.isEmpty(ownerInfo)) { 181 setLong(OWNER_INFO_ENABLED, 1, userId); 182 } 183 } 184 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId); 185 } 186 // No need to move the password / pattern files. They're already in the right place. 187 setString("migrated_user_specific", "true", 0); 188 Slog.i(TAG, "Migrated per-user lock settings to new location"); 189 } 190 } catch (RemoteException re) { 191 Slog.e(TAG, "Unable to migrate old data", re); 192 } 193 } 194 195 private final void checkWritePermission(int userId) { 196 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite"); 197 } 198 199 private final void checkPasswordReadPermission(int userId) { 200 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead"); 201 } 202 203 private final void checkReadPermission(String requestedKey, int userId) { 204 final int callingUid = Binder.getCallingUid(); 205 for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) { 206 String key = READ_PROFILE_PROTECTED_SETTINGS[i]; 207 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE) 208 != PackageManager.PERMISSION_GRANTED) { 209 throw new SecurityException("uid=" + callingUid 210 + " needs permission " + READ_PROFILE + " to read " 211 + requestedKey + " for user " + userId); 212 } 213 } 214 } 215 216 @Override 217 public void setBoolean(String key, boolean value, int userId) throws RemoteException { 218 checkWritePermission(userId); 219 220 writeToDb(key, value ? "1" : "0", userId); 221 } 222 223 @Override 224 public void setLong(String key, long value, int userId) throws RemoteException { 225 checkWritePermission(userId); 226 227 writeToDb(key, Long.toString(value), userId); 228 } 229 230 @Override 231 public void setString(String key, String value, int userId) throws RemoteException { 232 checkWritePermission(userId); 233 234 writeToDb(key, value, userId); 235 } 236 237 @Override 238 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { 239 checkReadPermission(key, userId); 240 241 String value = readFromDb(key, null, userId); 242 return TextUtils.isEmpty(value) ? 243 defaultValue : (value.equals("1") || value.equals("true")); 244 } 245 246 @Override 247 public long getLong(String key, long defaultValue, int userId) throws RemoteException { 248 checkReadPermission(key, userId); 249 250 String value = readFromDb(key, null, userId); 251 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); 252 } 253 254 @Override 255 public String getString(String key, String defaultValue, int userId) throws RemoteException { 256 checkReadPermission(key, userId); 257 258 return readFromDb(key, defaultValue, userId); 259 } 260 261 @Override 262 public void registerObserver(ILockSettingsObserver remote) throws RemoteException { 263 synchronized (mObservers) { 264 for (int i = 0; i < mObservers.size(); i++) { 265 if (mObservers.get(i).remote.asBinder() == remote.asBinder()) { 266 boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); 267 if (isDebuggable) { 268 throw new IllegalStateException("Observer was already registered."); 269 } else { 270 Log.e(TAG, "Observer was already registered."); 271 return; 272 } 273 } 274 } 275 LockSettingsObserver o = new LockSettingsObserver(); 276 o.remote = remote; 277 o.remote.asBinder().linkToDeath(o, 0); 278 mObservers.add(o); 279 } 280 } 281 282 @Override 283 public void unregisterObserver(ILockSettingsObserver remote) throws RemoteException { 284 synchronized (mObservers) { 285 for (int i = 0; i < mObservers.size(); i++) { 286 if (mObservers.get(i).remote.asBinder() == remote.asBinder()) { 287 mObservers.remove(i); 288 return; 289 } 290 } 291 } 292 } 293 294 public void notifyObservers(String key, int userId) { 295 synchronized (mObservers) { 296 for (int i = 0; i < mObservers.size(); i++) { 297 try { 298 mObservers.get(i).remote.onLockSettingChanged(key, userId); 299 } catch (RemoteException e) { 300 // The stack trace is not really helpful here. 301 Log.e(TAG, "Failed to notify ILockSettingsObserver: " + e); 302 } 303 } 304 } 305 } 306 307 private int getUserParentOrSelfId(int userId) { 308 if (userId != 0) { 309 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 310 final UserInfo pi = um.getProfileParent(userId); 311 if (pi != null) { 312 return pi.id; 313 } 314 } 315 return userId; 316 } 317 318 private String getLockPatternFilename(int userId) { 319 String dataSystemDirectory = 320 android.os.Environment.getDataDirectory().getAbsolutePath() + 321 SYSTEM_DIRECTORY; 322 userId = getUserParentOrSelfId(userId); 323 if (userId == 0) { 324 // Leave it in the same place for user 0 325 return dataSystemDirectory + LOCK_PATTERN_FILE; 326 } else { 327 return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE) 328 .getAbsolutePath(); 329 } 330 } 331 332 private String getLockPasswordFilename(int userId) { 333 userId = getUserParentOrSelfId(userId); 334 String dataSystemDirectory = 335 android.os.Environment.getDataDirectory().getAbsolutePath() + 336 SYSTEM_DIRECTORY; 337 if (userId == 0) { 338 // Leave it in the same place for user 0 339 return dataSystemDirectory + LOCK_PASSWORD_FILE; 340 } else { 341 return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE) 342 .getAbsolutePath(); 343 } 344 } 345 346 @Override 347 public boolean havePassword(int userId) throws RemoteException { 348 // Do we need a permissions check here? 349 350 return new File(getLockPasswordFilename(userId)).length() > 0; 351 } 352 353 @Override 354 public boolean havePattern(int userId) throws RemoteException { 355 // Do we need a permissions check here? 356 357 return new File(getLockPatternFilename(userId)).length() > 0; 358 } 359 360 private void maybeUpdateKeystore(String password, int userHandle) { 361 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 362 final KeyStore ks = KeyStore.getInstance(); 363 364 final List<UserInfo> profiles = um.getProfiles(userHandle); 365 boolean shouldReset = TextUtils.isEmpty(password); 366 367 // For historical reasons, don't wipe a non-empty keystore if we have a single user with a 368 // single profile. 369 if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) { 370 if (!ks.isEmpty()) { 371 shouldReset = false; 372 } 373 } 374 375 for (UserInfo pi : profiles) { 376 final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID); 377 if (shouldReset) { 378 ks.resetUid(profileUid); 379 } else { 380 ks.passwordUid(password, profileUid); 381 } 382 } 383 } 384 385 @Override 386 public void setLockPattern(String pattern, int userId) throws RemoteException { 387 checkWritePermission(userId); 388 389 maybeUpdateKeystore(pattern, userId); 390 391 final byte[] hash = LockPatternUtils.patternToHash( 392 LockPatternUtils.stringToPattern(pattern)); 393 writeFile(getLockPatternFilename(userId), hash); 394 } 395 396 @Override 397 public void setLockPassword(String password, int userId) throws RemoteException { 398 checkWritePermission(userId); 399 400 maybeUpdateKeystore(password, userId); 401 402 writeFile(getLockPasswordFilename(userId), 403 mLockPatternUtils.passwordToHash(password, userId)); 404 } 405 406 @Override 407 public boolean checkPattern(String pattern, int userId) throws RemoteException { 408 checkPasswordReadPermission(userId); 409 try { 410 // Read all the bytes from the file 411 RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r"); 412 final byte[] stored = new byte[(int) raf.length()]; 413 int got = raf.read(stored, 0, stored.length); 414 raf.close(); 415 if (got <= 0) { 416 return true; 417 } 418 // Compare the hash from the file with the entered pattern's hash 419 final byte[] hash = LockPatternUtils.patternToHash( 420 LockPatternUtils.stringToPattern(pattern)); 421 final boolean matched = Arrays.equals(stored, hash); 422 if (matched && !TextUtils.isEmpty(pattern)) { 423 maybeUpdateKeystore(pattern, userId); 424 } 425 return matched; 426 } catch (FileNotFoundException fnfe) { 427 Slog.e(TAG, "Cannot read file " + fnfe); 428 } catch (IOException ioe) { 429 Slog.e(TAG, "Cannot read file " + ioe); 430 } 431 return true; 432 } 433 434 @Override 435 public boolean checkPassword(String password, int userId) throws RemoteException { 436 checkPasswordReadPermission(userId); 437 438 try { 439 // Read all the bytes from the file 440 RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r"); 441 final byte[] stored = new byte[(int) raf.length()]; 442 int got = raf.read(stored, 0, stored.length); 443 raf.close(); 444 if (got <= 0) { 445 return true; 446 } 447 // Compare the hash from the file with the entered password's hash 448 final byte[] hash = mLockPatternUtils.passwordToHash(password, userId); 449 final boolean matched = Arrays.equals(stored, hash); 450 if (matched && !TextUtils.isEmpty(password)) { 451 maybeUpdateKeystore(password, userId); 452 } 453 return matched; 454 } catch (FileNotFoundException fnfe) { 455 Slog.e(TAG, "Cannot read file " + fnfe); 456 } catch (IOException ioe) { 457 Slog.e(TAG, "Cannot read file " + ioe); 458 } 459 return true; 460 } 461 462 @Override 463 public boolean checkVoldPassword(int userId) throws RemoteException { 464 if (!mFirstCallToVold) { 465 return false; 466 } 467 mFirstCallToVold = false; 468 469 checkPasswordReadPermission(userId); 470 471 // There's no guarantee that this will safely connect, but if it fails 472 // we will simply show the lock screen when we shouldn't, so relatively 473 // benign. There is an outside chance something nasty would happen if 474 // this service restarted before vold stales out the password in this 475 // case. The nastiness is limited to not showing the lock screen when 476 // we should, within the first minute of decrypting the phone if this 477 // service can't connect to vold, it restarts, and then the new instance 478 // does successfully connect. 479 final IMountService service = getMountService(); 480 String password = service.getPassword(); 481 service.clearPassword(); 482 if (password == null) { 483 return false; 484 } 485 486 try { 487 if (mLockPatternUtils.isLockPatternEnabled()) { 488 if (checkPattern(password, userId)) { 489 return true; 490 } 491 } 492 } catch (Exception e) { 493 } 494 495 try { 496 if (mLockPatternUtils.isLockPasswordEnabled()) { 497 if (checkPassword(password, userId)) { 498 return true; 499 } 500 } 501 } catch (Exception e) { 502 } 503 504 return false; 505 } 506 507 @Override 508 public void removeUser(int userId) { 509 checkWritePermission(userId); 510 511 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 512 try { 513 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 514 final UserInfo parentInfo = um.getProfileParent(userId); 515 if (parentInfo == null) { 516 // This user owns its lock settings files - safe to delete them 517 File file = new File(getLockPasswordFilename(userId)); 518 if (file.exists()) { 519 file.delete(); 520 } 521 file = new File(getLockPatternFilename(userId)); 522 if (file.exists()) { 523 file.delete(); 524 } 525 } 526 527 db.beginTransaction(); 528 db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); 529 db.setTransactionSuccessful(); 530 } finally { 531 db.endTransaction(); 532 } 533 } 534 535 private void writeFile(String name, byte[] hash) { 536 try { 537 // Write the hash to file 538 RandomAccessFile raf = new RandomAccessFile(name, "rw"); 539 // Truncate the file if pattern is null, to clear the lock 540 if (hash == null || hash.length == 0) { 541 raf.setLength(0); 542 } else { 543 raf.write(hash, 0, hash.length); 544 } 545 raf.close(); 546 } catch (IOException ioe) { 547 Slog.e(TAG, "Error writing to file " + ioe); 548 } 549 } 550 551 private void writeToDb(String key, String value, int userId) { 552 writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId); 553 notifyObservers(key, userId); 554 } 555 556 private void writeToDb(SQLiteDatabase db, String key, String value, int userId) { 557 ContentValues cv = new ContentValues(); 558 cv.put(COLUMN_KEY, key); 559 cv.put(COLUMN_USERID, userId); 560 cv.put(COLUMN_VALUE, value); 561 562 db.beginTransaction(); 563 try { 564 db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", 565 new String[] {key, Integer.toString(userId)}); 566 db.insert(TABLE, null, cv); 567 db.setTransactionSuccessful(); 568 } finally { 569 db.endTransaction(); 570 } 571 } 572 573 private String readFromDb(String key, String defaultValue, int userId) { 574 Cursor cursor; 575 String result = defaultValue; 576 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 577 if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, 578 COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", 579 new String[] { Integer.toString(userId), key }, 580 null, null, null)) != null) { 581 if (cursor.moveToFirst()) { 582 result = cursor.getString(0); 583 } 584 cursor.close(); 585 } 586 return result; 587 } 588 589 class DatabaseHelper extends SQLiteOpenHelper { 590 private static final String TAG = "LockSettingsDB"; 591 private static final String DATABASE_NAME = "locksettings.db"; 592 593 private static final int DATABASE_VERSION = 2; 594 595 public DatabaseHelper(Context context) { 596 super(context, DATABASE_NAME, null, DATABASE_VERSION); 597 setWriteAheadLoggingEnabled(true); 598 } 599 600 private void createTable(SQLiteDatabase db) { 601 db.execSQL("CREATE TABLE " + TABLE + " (" + 602 "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 603 COLUMN_KEY + " TEXT," + 604 COLUMN_USERID + " INTEGER," + 605 COLUMN_VALUE + " TEXT" + 606 ");"); 607 } 608 609 @Override 610 public void onCreate(SQLiteDatabase db) { 611 createTable(db); 612 initializeDefaults(db); 613 } 614 615 private void initializeDefaults(SQLiteDatabase db) { 616 // Get the lockscreen default from a system property, if available 617 boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default", 618 false); 619 if (lockScreenDisable) { 620 writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); 621 } 622 } 623 624 @Override 625 public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { 626 int upgradeVersion = oldVersion; 627 if (upgradeVersion == 1) { 628 // Set the initial value for {@link LockPatternUtils#LOCKSCREEN_WIDGETS_ENABLED} 629 // during upgrade based on whether each user previously had widgets in keyguard. 630 maybeEnableWidgetSettingForUsers(db); 631 upgradeVersion = 2; 632 } 633 634 if (upgradeVersion != DATABASE_VERSION) { 635 Log.w(TAG, "Failed to upgrade database!"); 636 } 637 } 638 639 private void maybeEnableWidgetSettingForUsers(SQLiteDatabase db) { 640 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 641 final ContentResolver cr = mContext.getContentResolver(); 642 final List<UserInfo> users = um.getUsers(); 643 for (int i = 0; i < users.size(); i++) { 644 final int userId = users.get(i).id; 645 final boolean enabled = mLockPatternUtils.hasWidgetsEnabledInKeyguard(userId); 646 Log.v(TAG, "Widget upgrade uid=" + userId + ", enabled=" 647 + enabled + ", w[]=" + mLockPatternUtils.getAppWidgets()); 648 loadSetting(db, LockPatternUtils.LOCKSCREEN_WIDGETS_ENABLED, userId, enabled); 649 } 650 } 651 652 private void loadSetting(SQLiteDatabase db, String key, int userId, boolean value) { 653 SQLiteStatement stmt = null; 654 try { 655 stmt = db.compileStatement( 656 "INSERT OR REPLACE INTO locksettings(name,user,value) VALUES(?,?,?);"); 657 stmt.bindString(1, key); 658 stmt.bindLong(2, userId); 659 stmt.bindLong(3, value ? 1 : 0); 660 stmt.execute(); 661 } finally { 662 if (stmt != null) stmt.close(); 663 } 664 } 665 } 666 667 private static final String[] VALID_SETTINGS = new String[] { 668 LockPatternUtils.LOCKOUT_PERMANENT_KEY, 669 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, 670 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, 671 LockPatternUtils.PASSWORD_TYPE_KEY, 672 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 673 LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 674 LockPatternUtils.DISABLE_LOCKSCREEN_KEY, 675 LockPatternUtils.LOCKSCREEN_OPTIONS, 676 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, 677 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY, 678 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, 679 LockPatternUtils.PASSWORD_HISTORY_KEY, 680 Secure.LOCK_PATTERN_ENABLED, 681 Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 682 Secure.LOCK_PATTERN_VISIBLE, 683 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED 684 }; 685 686 // These are protected with a read permission 687 private static final String[] READ_PROFILE_PROTECTED_SETTINGS = new String[] { 688 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 689 Secure.LOCK_SCREEN_OWNER_INFO 690 }; 691 692 private IMountService getMountService() { 693 final IBinder service = ServiceManager.getService("mount"); 694 if (service != null) { 695 return IMountService.Stub.asInterface(service); 696 } 697 return null; 698 } 699 700 private class LockSettingsObserver implements DeathRecipient { 701 ILockSettingsObserver remote; 702 703 @Override 704 public void binderDied() { 705 mObservers.remove(this); 706 } 707 } 708} 709