LockSettingsService.java revision d1645f8d0f30709340eb6b6d6da5022bbab77024
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.internal.widget; 18 19import android.content.ContentResolver; 20import android.content.ContentValues; 21import android.content.Context; 22import android.database.Cursor; 23import android.database.sqlite.SQLiteDatabase; 24import android.database.sqlite.SQLiteOpenHelper; 25import android.os.Binder; 26import android.os.RemoteException; 27import android.os.SystemProperties; 28import android.os.UserId; 29import android.provider.Settings; 30import android.provider.Settings.Secure; 31import android.text.TextUtils; 32import android.util.Slog; 33 34import java.io.File; 35import java.io.FileNotFoundException; 36import java.io.IOException; 37import java.io.RandomAccessFile; 38import java.util.Arrays; 39 40/** 41 * Keeps the lock pattern/password data and related settings for each user. 42 * Used by LockPatternUtils. Needs to be a service because Settings app also needs 43 * to be able to save lockscreen information for secondary users. 44 * @hide 45 */ 46public class LockSettingsService extends ILockSettings.Stub { 47 48 private final DatabaseHelper mOpenHelper; 49 private static final String TAG = "LockSettingsService"; 50 51 private static final String TABLE = "locksettings"; 52 private static final String COLUMN_KEY = "name"; 53 private static final String COLUMN_USERID = "user"; 54 private static final String COLUMN_VALUE = "value"; 55 56 private static final String[] COLUMNS_FOR_QUERY = { 57 COLUMN_VALUE 58 }; 59 60 private static final String SYSTEM_DIRECTORY = "/system/"; 61 private static final String LOCK_PATTERN_FILE = "gesture.key"; 62 private static final String LOCK_PASSWORD_FILE = "password.key"; 63 64 private final Context mContext; 65 66 public LockSettingsService(Context context) { 67 mContext = context; 68 // Open the database 69 mOpenHelper = new DatabaseHelper(mContext); 70 } 71 72 public void systemReady() { 73 migrateOldData(); 74 } 75 76 private void migrateOldData() { 77 try { 78 if (getString("migrated", null, 0) != null) { 79 // Already migrated 80 return; 81 } 82 83 final ContentResolver cr = mContext.getContentResolver(); 84 for (String validSetting : VALID_SETTINGS) { 85 String value = Settings.Secure.getString(cr, validSetting); 86 if (value != null) { 87 setString(validSetting, value, 0); 88 } 89 } 90 // No need to move the password / pattern files. They're already in the right place. 91 setString("migrated", "true", 0); 92 Slog.i(TAG, "Migrated lock settings to new location"); 93 } catch (RemoteException re) { 94 Slog.e(TAG, "Unable to migrate old data"); 95 } 96 } 97 98 private static final void checkWritePermission(int userId) { 99 final int callingUid = Binder.getCallingUid(); 100 if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { 101 throw new SecurityException("uid=" + callingUid 102 + " not authorized to write lock settings"); 103 } 104 } 105 106 private static final void checkPasswordReadPermission(int userId) { 107 final int callingUid = Binder.getCallingUid(); 108 if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { 109 throw new SecurityException("uid=" + callingUid 110 + " not authorized to read lock password"); 111 } 112 } 113 114 private static final void checkReadPermission(int userId) { 115 final int callingUid = Binder.getCallingUid(); 116 if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID 117 && UserId.getUserId(callingUid) != userId) { 118 throw new SecurityException("uid=" + callingUid 119 + " not authorized to read settings of user " + userId); 120 } 121 } 122 123 @Override 124 public void setBoolean(String key, boolean value, int userId) throws RemoteException { 125 checkWritePermission(userId); 126 127 writeToDb(key, value ? "1" : "0", userId); 128 } 129 130 @Override 131 public void setLong(String key, long value, int userId) throws RemoteException { 132 checkWritePermission(userId); 133 134 writeToDb(key, Long.toString(value), userId); 135 } 136 137 @Override 138 public void setString(String key, String value, int userId) throws RemoteException { 139 checkWritePermission(userId); 140 141 writeToDb(key, value, userId); 142 } 143 144 @Override 145 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { 146 //checkReadPermission(userId); 147 148 String value = readFromDb(key, null, userId); 149 return TextUtils.isEmpty(value) ? 150 defaultValue : (value.equals("1") || value.equals("true")); 151 } 152 153 @Override 154 public long getLong(String key, long defaultValue, int userId) throws RemoteException { 155 //checkReadPermission(userId); 156 157 String value = readFromDb(key, null, userId); 158 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); 159 } 160 161 @Override 162 public String getString(String key, String defaultValue, int userId) throws RemoteException { 163 //checkReadPermission(userId); 164 165 return readFromDb(key, defaultValue, userId); 166 } 167 168 private String getLockPatternFilename(int userId) { 169 String dataSystemDirectory = 170 android.os.Environment.getDataDirectory().getAbsolutePath() + 171 SYSTEM_DIRECTORY; 172 if (userId == 0) { 173 // Leave it in the same place for user 0 174 return dataSystemDirectory + LOCK_PATTERN_FILE; 175 } else { 176 return dataSystemDirectory + "users/" + userId + "/" + LOCK_PATTERN_FILE; 177 } 178 } 179 180 private String getLockPasswordFilename(int userId) { 181 String dataSystemDirectory = 182 android.os.Environment.getDataDirectory().getAbsolutePath() + 183 SYSTEM_DIRECTORY; 184 if (userId == 0) { 185 // Leave it in the same place for user 0 186 return dataSystemDirectory + LOCK_PASSWORD_FILE; 187 } else { 188 return dataSystemDirectory + "users/" + userId + "/" + LOCK_PASSWORD_FILE; 189 } 190 } 191 192 @Override 193 public boolean havePassword(int userId) throws RemoteException { 194 // Do we need a permissions check here? 195 196 return new File(getLockPasswordFilename(userId)).length() > 0; 197 } 198 199 @Override 200 public boolean havePattern(int userId) throws RemoteException { 201 // Do we need a permissions check here? 202 203 return new File(getLockPatternFilename(userId)).length() > 0; 204 } 205 206 @Override 207 public void setLockPattern(byte[] hash, int userId) throws RemoteException { 208 checkWritePermission(userId); 209 210 writeFile(getLockPatternFilename(userId), hash); 211 } 212 213 @Override 214 public boolean checkPattern(byte[] hash, int userId) throws RemoteException { 215 checkPasswordReadPermission(userId); 216 try { 217 // Read all the bytes from the file 218 RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r"); 219 final byte[] stored = new byte[(int) raf.length()]; 220 int got = raf.read(stored, 0, stored.length); 221 raf.close(); 222 if (got <= 0) { 223 return true; 224 } 225 // Compare the hash from the file with the entered pattern's hash 226 return Arrays.equals(stored, hash); 227 } catch (FileNotFoundException fnfe) { 228 Slog.e(TAG, "Cannot read file " + fnfe); 229 return true; 230 } catch (IOException ioe) { 231 Slog.e(TAG, "Cannot read file " + ioe); 232 return true; 233 } 234 } 235 236 @Override 237 public void setLockPassword(byte[] hash, int userId) throws RemoteException { 238 checkWritePermission(userId); 239 240 writeFile(getLockPasswordFilename(userId), hash); 241 } 242 243 @Override 244 public boolean checkPassword(byte[] hash, int userId) throws RemoteException { 245 checkPasswordReadPermission(userId); 246 247 try { 248 // Read all the bytes from the file 249 RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r"); 250 final byte[] stored = new byte[(int) raf.length()]; 251 int got = raf.read(stored, 0, stored.length); 252 raf.close(); 253 if (got <= 0) { 254 return true; 255 } 256 // Compare the hash from the file with the entered password's hash 257 return Arrays.equals(stored, hash); 258 } catch (FileNotFoundException fnfe) { 259 Slog.e(TAG, "Cannot read file " + fnfe); 260 return true; 261 } catch (IOException ioe) { 262 Slog.e(TAG, "Cannot read file " + ioe); 263 return true; 264 } 265 } 266 267 @Override 268 public void removeUser(int userId) { 269 checkWritePermission(userId); 270 271 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 272 try { 273 File file = new File(getLockPasswordFilename(userId)); 274 if (file.exists()) { 275 file.delete(); 276 } 277 file = new File(getLockPatternFilename(userId)); 278 if (file.exists()) { 279 file.delete(); 280 } 281 282 db.beginTransaction(); 283 db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); 284 db.setTransactionSuccessful(); 285 } finally { 286 db.endTransaction(); 287 } 288 } 289 290 private void writeFile(String name, byte[] hash) { 291 try { 292 // Write the hash to file 293 RandomAccessFile raf = new RandomAccessFile(name, "rw"); 294 // Truncate the file if pattern is null, to clear the lock 295 if (hash == null || hash.length == 0) { 296 raf.setLength(0); 297 } else { 298 raf.write(hash, 0, hash.length); 299 } 300 raf.close(); 301 } catch (IOException ioe) { 302 Slog.e(TAG, "Error writing to file " + ioe); 303 } 304 } 305 306 private void writeToDb(String key, String value, int userId) { 307 writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId); 308 } 309 310 private void writeToDb(SQLiteDatabase db, String key, String value, int userId) { 311 ContentValues cv = new ContentValues(); 312 cv.put(COLUMN_KEY, key); 313 cv.put(COLUMN_USERID, userId); 314 cv.put(COLUMN_VALUE, value); 315 316 db.beginTransaction(); 317 try { 318 db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", 319 new String[] {key, Integer.toString(userId)}); 320 db.insert(TABLE, null, cv); 321 db.setTransactionSuccessful(); 322 } finally { 323 db.endTransaction(); 324 } 325 } 326 327 private String readFromDb(String key, String defaultValue, int userId) { 328 Cursor cursor; 329 String result = defaultValue; 330 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 331 if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, 332 COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", 333 new String[] { Integer.toString(userId), key }, 334 null, null, null)) != null) { 335 if (cursor.moveToFirst()) { 336 result = cursor.getString(0); 337 } 338 cursor.close(); 339 } 340 return result; 341 } 342 343 class DatabaseHelper extends SQLiteOpenHelper { 344 private static final String TAG = "LockSettingsDB"; 345 private static final String DATABASE_NAME = "locksettings.db"; 346 347 private static final int DATABASE_VERSION = 1; 348 349 public DatabaseHelper(Context context) { 350 super(context, DATABASE_NAME, null, DATABASE_VERSION); 351 setWriteAheadLoggingEnabled(true); 352 } 353 354 private void createTable(SQLiteDatabase db) { 355 db.execSQL("CREATE TABLE " + TABLE + " (" + 356 "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 357 COLUMN_KEY + " TEXT," + 358 COLUMN_USERID + " INTEGER," + 359 COLUMN_VALUE + " TEXT" + 360 ");"); 361 } 362 363 @Override 364 public void onCreate(SQLiteDatabase db) { 365 createTable(db); 366 initializeDefaults(db); 367 } 368 369 private void initializeDefaults(SQLiteDatabase db) { 370 // Get the lockscreen default from a system property, if available 371 boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default", 372 false); 373 if (lockScreenDisable) { 374 writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); 375 } 376 } 377 378 @Override 379 public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { 380 // Nothing yet 381 } 382 } 383 384 private static final String[] VALID_SETTINGS = new String[] { 385 LockPatternUtils.LOCKOUT_PERMANENT_KEY, 386 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, 387 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, 388 LockPatternUtils.PASSWORD_TYPE_KEY, 389 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 390 LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 391 LockPatternUtils.DISABLE_LOCKSCREEN_KEY, 392 LockPatternUtils.LOCKSCREEN_OPTIONS, 393 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, 394 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY, 395 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, 396 LockPatternUtils.PASSWORD_HISTORY_KEY, 397 Secure.LOCK_PATTERN_ENABLED, 398 Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 399 Secure.LOCK_PATTERN_VISIBLE, 400 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED 401 }; 402} 403