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