LockSettingsStorage.java revision 261d5ab8f4c3fdd34163468fd48ab07f7ad13d3c
1/* 2 * Copyright (C) 2014 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.ContentValues; 20import android.content.Context; 21import android.content.pm.UserInfo; 22import android.database.Cursor; 23import android.database.sqlite.SQLiteDatabase; 24import android.database.sqlite.SQLiteOpenHelper; 25import android.os.Environment; 26import android.os.UserManager; 27import android.util.ArrayMap; 28import android.util.Log; 29import android.util.Slog; 30 31import java.io.File; 32import java.io.IOException; 33import java.io.RandomAccessFile; 34 35import static android.content.Context.USER_SERVICE; 36 37/** 38 * Storage for the lock settings service. 39 */ 40class LockSettingsStorage { 41 42 private static final String TAG = "LockSettingsStorage"; 43 private static final String TABLE = "locksettings"; 44 45 private static final String COLUMN_KEY = "name"; 46 private static final String COLUMN_USERID = "user"; 47 private static final String COLUMN_VALUE = "value"; 48 49 private static final String[] COLUMNS_FOR_QUERY = { 50 COLUMN_VALUE 51 }; 52 53 private static final String SYSTEM_DIRECTORY = "/system/"; 54 private static final String LOCK_PATTERN_FILE = "gesture.key"; 55 private static final String LOCK_PASSWORD_FILE = "password.key"; 56 57 private final DatabaseHelper mOpenHelper; 58 private final Context mContext; 59 private final Object mFileWriteLock = new Object(); 60 61 LockSettingsStorage(Context context, Callback callback) { 62 mContext = context; 63 mOpenHelper = new DatabaseHelper(context, callback); 64 } 65 66 void writeKeyValue(String key, String value, int userId) { 67 writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId); 68 } 69 70 void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) { 71 ContentValues cv = new ContentValues(); 72 cv.put(COLUMN_KEY, key); 73 cv.put(COLUMN_USERID, userId); 74 cv.put(COLUMN_VALUE, value); 75 76 db.beginTransaction(); 77 try { 78 db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", 79 new String[] {key, Integer.toString(userId)}); 80 db.insert(TABLE, null, cv); 81 db.setTransactionSuccessful(); 82 } finally { 83 db.endTransaction(); 84 } 85 86 } 87 88 String readKeyValue(String key, String defaultValue, int userId) { 89 Cursor cursor; 90 String result = defaultValue; 91 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 92 if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, 93 COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", 94 new String[] { Integer.toString(userId), key }, 95 null, null, null)) != null) { 96 if (cursor.moveToFirst()) { 97 result = cursor.getString(0); 98 } 99 cursor.close(); 100 } 101 return result; 102 } 103 104 byte[] readPasswordHash(int userId) { 105 final byte[] stored = readFile(getLockPasswordFilename(userId), userId); 106 if (stored != null && stored.length > 0) { 107 return stored; 108 } 109 return null; 110 } 111 112 byte[] readPatternHash(int userId) { 113 final byte[] stored = readFile(getLockPatternFilename(userId), userId); 114 if (stored != null && stored.length > 0) { 115 return stored; 116 } 117 return null; 118 } 119 120 boolean hasPassword(int userId) { 121 return hasFile(getLockPasswordFilename(userId), userId); 122 } 123 124 boolean hasPattern(int userId) { 125 return hasFile(getLockPatternFilename(userId), userId); 126 } 127 128 private boolean hasFile(String name, int userId) { 129 byte[] contents = readFile(name, userId); 130 return contents != null && contents.length > 0; 131 } 132 133 private byte[] readFile(String name, int userId) { 134 RandomAccessFile raf = null; 135 byte[] stored = null; 136 try { 137 raf = new RandomAccessFile(name, "r"); 138 stored = new byte[(int) raf.length()]; 139 raf.readFully(stored, 0, stored.length); 140 raf.close(); 141 } catch (IOException e) { 142 Slog.e(TAG, "Cannot read file " + e); 143 } finally { 144 if (raf != null) { 145 try { 146 raf.close(); 147 } catch (IOException e) { 148 Slog.e(TAG, "Error closing file " + e); 149 } 150 } 151 } 152 return stored; 153 } 154 155 private void writeFile(String name, byte[] hash, int userId) { 156 synchronized (mFileWriteLock) { 157 RandomAccessFile raf = null; 158 try { 159 // Write the hash to file 160 raf = new RandomAccessFile(name, "rw"); 161 // Truncate the file if pattern is null, to clear the lock 162 if (hash == null || hash.length == 0) { 163 raf.setLength(0); 164 } else { 165 raf.write(hash, 0, hash.length); 166 } 167 raf.close(); 168 } catch (IOException e) { 169 Slog.e(TAG, "Error writing to file " + e); 170 } finally { 171 if (raf != null) { 172 try { 173 raf.close(); 174 } catch (IOException e) { 175 Slog.e(TAG, "Error closing file " + e); 176 } 177 } 178 } 179 } 180 } 181 182 public void writePatternHash(byte[] hash, int userId) { 183 writeFile(getLockPatternFilename(userId), hash, userId); 184 } 185 186 public void writePasswordHash(byte[] hash, int userId) { 187 writeFile(getLockPasswordFilename(userId), hash, userId); 188 } 189 190 191 private String getLockPatternFilename(int userId) { 192 String dataSystemDirectory = 193 android.os.Environment.getDataDirectory().getAbsolutePath() + 194 SYSTEM_DIRECTORY; 195 userId = getUserParentOrSelfId(userId); 196 if (userId == 0) { 197 // Leave it in the same place for user 0 198 return dataSystemDirectory + LOCK_PATTERN_FILE; 199 } else { 200 return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE) 201 .getAbsolutePath(); 202 } 203 } 204 205 private String getLockPasswordFilename(int userId) { 206 userId = getUserParentOrSelfId(userId); 207 String dataSystemDirectory = 208 android.os.Environment.getDataDirectory().getAbsolutePath() + 209 SYSTEM_DIRECTORY; 210 if (userId == 0) { 211 // Leave it in the same place for user 0 212 return dataSystemDirectory + LOCK_PASSWORD_FILE; 213 } else { 214 return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE) 215 .getAbsolutePath(); 216 } 217 } 218 219 private int getUserParentOrSelfId(int userId) { 220 if (userId != 0) { 221 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 222 final UserInfo pi = um.getProfileParent(userId); 223 if (pi != null) { 224 return pi.id; 225 } 226 } 227 return userId; 228 } 229 230 231 public void removeUser(int userId) { 232 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 233 234 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 235 final UserInfo parentInfo = um.getProfileParent(userId); 236 237 synchronized (mFileWriteLock) { 238 if (parentInfo == null) { 239 // This user owns its lock settings files - safe to delete them 240 File file = new File(getLockPasswordFilename(userId)); 241 if (file.exists()) { 242 file.delete(); 243 } 244 file = new File(getLockPatternFilename(userId)); 245 if (file.exists()) { 246 file.delete(); 247 } 248 } 249 } 250 251 try { 252 db.beginTransaction(); 253 db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); 254 db.setTransactionSuccessful(); 255 } finally { 256 db.endTransaction(); 257 } 258 } 259 260 261 interface Callback { 262 void initialize(SQLiteDatabase db); 263 } 264 265 class DatabaseHelper extends SQLiteOpenHelper { 266 private static final String TAG = "LockSettingsDB"; 267 private static final String DATABASE_NAME = "locksettings.db"; 268 269 private static final int DATABASE_VERSION = 2; 270 271 private final Callback mCallback; 272 273 public DatabaseHelper(Context context, Callback callback) { 274 super(context, DATABASE_NAME, null, DATABASE_VERSION); 275 setWriteAheadLoggingEnabled(true); 276 mCallback = callback; 277 } 278 279 private void createTable(SQLiteDatabase db) { 280 db.execSQL("CREATE TABLE " + TABLE + " (" + 281 "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 282 COLUMN_KEY + " TEXT," + 283 COLUMN_USERID + " INTEGER," + 284 COLUMN_VALUE + " TEXT" + 285 ");"); 286 } 287 288 @Override 289 public void onCreate(SQLiteDatabase db) { 290 createTable(db); 291 mCallback.initialize(db); 292 } 293 294 @Override 295 public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { 296 int upgradeVersion = oldVersion; 297 if (upgradeVersion == 1) { 298 // Previously migrated lock screen widget settings. Now defunct. 299 upgradeVersion = 2; 300 } 301 302 if (upgradeVersion != DATABASE_VERSION) { 303 Log.w(TAG, "Failed to upgrade database!"); 304 } 305 } 306 } 307} 308