LockSettingsStorage.java revision e542499a304f067372d85722e11a74b4e56b0bd7
1261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos/* 2261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * Copyright (C) 2014 The Android Open Source Project 3261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * 4261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * Licensed under the Apache License, Version 2.0 (the "License"); 5261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * you may not use this file except in compliance with the License. 6261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * You may obtain a copy of the License at 7261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * 8261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * http://www.apache.org/licenses/LICENSE-2.0 9261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * 10261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * Unless required by applicable law or agreed to in writing, software 11261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * distributed under the License is distributed on an "AS IS" BASIS, 12261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * See the License for the specific language governing permissions and 14261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * limitations under the License 15261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos */ 16261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 17261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roospackage com.android.server; 18261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 19e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roosimport com.android.internal.annotations.VisibleForTesting; 20e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos 21261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.content.ContentValues; 22261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.content.Context; 23261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.content.pm.UserInfo; 24261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.database.Cursor; 25261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.database.sqlite.SQLiteDatabase; 26261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.database.sqlite.SQLiteOpenHelper; 27261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.os.Environment; 28261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.os.UserManager; 29261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.util.ArrayMap; 30261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.util.Log; 31261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport android.util.Slog; 32261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 33261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport java.io.File; 34261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport java.io.IOException; 35261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport java.io.RandomAccessFile; 36261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 37261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosimport static android.content.Context.USER_SERVICE; 38261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 39261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos/** 40261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos * Storage for the lock settings service. 41261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos */ 42261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roosclass LockSettingsStorage { 43261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 44261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String TAG = "LockSettingsStorage"; 45261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String TABLE = "locksettings"; 46261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 47261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String COLUMN_KEY = "name"; 48261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String COLUMN_USERID = "user"; 49261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String COLUMN_VALUE = "value"; 50261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 51261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String[] COLUMNS_FOR_QUERY = { 52261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_VALUE 53261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos }; 543dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static final String[] COLUMNS_FOR_PREFETCH = { 553dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos COLUMN_KEY, COLUMN_VALUE 563dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos }; 57261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 58261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String SYSTEM_DIRECTORY = "/system/"; 59261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String LOCK_PATTERN_FILE = "gesture.key"; 60261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String LOCK_PASSWORD_FILE = "password.key"; 61261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 623dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static final Object DEFAULT = new Object(); 633dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 64261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final DatabaseHelper mOpenHelper; 65261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final Context mContext; 663dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private final Cache mCache = new Cache(); 67261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final Object mFileWriteLock = new Object(); 68261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 693dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public LockSettingsStorage(Context context, Callback callback) { 70261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mContext = context; 71261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mOpenHelper = new DatabaseHelper(context, callback); 72261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 73261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 743dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public void writeKeyValue(String key, String value, int userId) { 75261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId); 76261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 77261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 783dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) { 79261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos ContentValues cv = new ContentValues(); 80261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cv.put(COLUMN_KEY, key); 81261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cv.put(COLUMN_USERID, userId); 82261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cv.put(COLUMN_VALUE, value); 83261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 84261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.beginTransaction(); 85261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 86261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", 87261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos new String[] {key, Integer.toString(userId)}); 88261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.insert(TABLE, null, cv); 89261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.setTransactionSuccessful(); 903dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putKeyValue(key, value, userId); 91261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 92261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.endTransaction(); 93261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 94261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 95261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 96261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 973dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public String readKeyValue(String key, String defaultValue, int userId) { 983dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version; 993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized (mCache) { 1003dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.hasKeyValue(key, userId)) { 1013dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.peekKeyValue(key, defaultValue, userId); 1023dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1033dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos version = mCache.getVersion(); 1043dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1053dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 106261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Cursor cursor; 1073dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos Object result = DEFAULT; 108261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 109261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, 110261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", 111261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos new String[] { Integer.toString(userId), key }, 112261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos null, null, null)) != null) { 113261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (cursor.moveToFirst()) { 114261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos result = cursor.getString(0); 115261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 116261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cursor.close(); 117261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 1183dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putKeyValueIfUnchanged(key, result, userId, version); 1193dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return result == DEFAULT ? defaultValue : (String) result; 1203dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1213dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 1223dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public void prefetchUser(int userId) { 1233dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version; 1243dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized (mCache) { 1253dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.isFetched(userId)) { 1263dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return; 1273dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1283dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.setFetched(userId); 1293dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos version = mCache.getVersion(); 1303dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1313dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 1323dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos Cursor cursor; 1333dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 1343dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if ((cursor = db.query(TABLE, COLUMNS_FOR_PREFETCH, 1353dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos COLUMN_USERID + "=?", 1363dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos new String[] { Integer.toString(userId) }, 1373dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos null, null, null)) != null) { 1383dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos while (cursor.moveToNext()) { 1393dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String key = cursor.getString(0); 1403dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String value = cursor.getString(1); 1413dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putKeyValueIfUnchanged(key, value, userId, version); 1423dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1433dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos cursor.close(); 1443dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1453dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 1463dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos // Populate cache by reading the password and pattern files. 1473dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos readPasswordHash(userId); 1483dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos readPatternHash(userId); 149261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 150261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1513dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public byte[] readPasswordHash(int userId) { 1523dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos final byte[] stored = readFile(getLockPasswordFilename(userId)); 153261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (stored != null && stored.length > 0) { 154261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return stored; 155261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 156261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return null; 157261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 158261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1593dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public byte[] readPatternHash(int userId) { 1603dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos final byte[] stored = readFile(getLockPatternFilename(userId)); 161261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (stored != null && stored.length > 0) { 162261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return stored; 163261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 164261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return null; 165261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 166261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1673dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public boolean hasPassword(int userId) { 1683dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return hasFile(getLockPasswordFilename(userId)); 169261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 170261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1713dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public boolean hasPattern(int userId) { 1723dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return hasFile(getLockPatternFilename(userId)); 173261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 174261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1753dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private boolean hasFile(String name) { 1763dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos byte[] contents = readFile(name); 177261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return contents != null && contents.length > 0; 178261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 179261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1803dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private byte[] readFile(String name) { 1813dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version; 1823dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized (mCache) { 1833dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.hasFile(name)) { 1843dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.peekFile(name); 1853dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1863dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos version = mCache.getVersion(); 1873dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1883dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 189261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos RandomAccessFile raf = null; 190261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos byte[] stored = null; 191261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 192261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf = new RandomAccessFile(name, "r"); 193261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos stored = new byte[(int) raf.length()]; 194261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.readFully(stored, 0, stored.length); 195261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 196261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 197261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Cannot read file " + e); 198261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 199261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (raf != null) { 200261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 201261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 202261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 203261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Error closing file " + e); 204261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 205261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 206261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 2073dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFileIfUnchanged(name, stored, version); 208261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return stored; 209261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 210261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 2113dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private void writeFile(String name, byte[] hash) { 212261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos synchronized (mFileWriteLock) { 213261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos RandomAccessFile raf = null; 214261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 215261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Write the hash to file 216261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf = new RandomAccessFile(name, "rw"); 217261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Truncate the file if pattern is null, to clear the lock 218261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (hash == null || hash.length == 0) { 219261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.setLength(0); 220261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } else { 221261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.write(hash, 0, hash.length); 222261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 223261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 224261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 225261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Error writing to file " + e); 226261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 227261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (raf != null) { 228261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 229261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 230261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 231261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Error closing file " + e); 232261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 233261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 234261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 2353dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFile(name, hash); 236261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 237261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 238261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 239261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void writePatternHash(byte[] hash, int userId) { 2403dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos writeFile(getLockPatternFilename(userId), hash); 241261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 242261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 243261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void writePasswordHash(byte[] hash, int userId) { 2443dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos writeFile(getLockPasswordFilename(userId), hash); 245261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 246261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 247261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 248e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 249e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos String getLockPatternFilename(int userId) { 2503dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return getLockCredentialFilePathForUser(userId, LOCK_PATTERN_FILE); 251261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 252261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 253e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 254e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos String getLockPasswordFilename(int userId) { 2553dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return getLockCredentialFilePathForUser(userId, LOCK_PASSWORD_FILE); 2563dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 2573dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 2583dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private String getLockCredentialFilePathForUser(int userId, String basename) { 259261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos userId = getUserParentOrSelfId(userId); 260261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos String dataSystemDirectory = 261261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos android.os.Environment.getDataDirectory().getAbsolutePath() + 262261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos SYSTEM_DIRECTORY; 263261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (userId == 0) { 264261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Leave it in the same place for user 0 2653dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return dataSystemDirectory + basename; 266261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } else { 2673dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return new File(Environment.getUserSystemDirectory(userId), basename).getAbsolutePath(); 268261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 269261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 270261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 271261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private int getUserParentOrSelfId(int userId) { 272261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (userId != 0) { 273261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 274261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserInfo pi = um.getProfileParent(userId); 275261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (pi != null) { 276261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return pi.id; 277261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 278261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 279261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return userId; 280261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 281261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 282261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 283261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void removeUser(int userId) { 284261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 285261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 286261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 287261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserInfo parentInfo = um.getProfileParent(userId); 288261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 289261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos synchronized (mFileWriteLock) { 290261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (parentInfo == null) { 291261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // This user owns its lock settings files - safe to delete them 2923dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String name = getLockPasswordFilename(userId); 2933dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos File file = new File(name); 294261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (file.exists()) { 295261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos file.delete(); 2963dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFile(name, null); 297261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 2983dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos name = getLockPatternFilename(userId); 2993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos file = new File(name); 300261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (file.exists()) { 301261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos file.delete(); 3023dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFile(name, null); 303261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 304261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 305261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 306261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 307261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 308261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.beginTransaction(); 309261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); 310261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.setTransactionSuccessful(); 3113dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.removeUser(userId); 312261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 313261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.endTransaction(); 314261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 315261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 316261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 317e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 318e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos void closeDatabase() { 319e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mOpenHelper.close(); 320e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos } 321e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos 322e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 323e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos void clearCache() { 324e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mCache.clear(); 325e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos } 326261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 3273dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public interface Callback { 328261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos void initialize(SQLiteDatabase db); 329261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 330261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 331261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos class DatabaseHelper extends SQLiteOpenHelper { 332261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String TAG = "LockSettingsDB"; 333261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String DATABASE_NAME = "locksettings.db"; 334261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 335261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final int DATABASE_VERSION = 2; 336261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 337261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final Callback mCallback; 338261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 339261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public DatabaseHelper(Context context, Callback callback) { 340261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos super(context, DATABASE_NAME, null, DATABASE_VERSION); 341261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos setWriteAheadLoggingEnabled(true); 342261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mCallback = callback; 343261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 344261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 345261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private void createTable(SQLiteDatabase db) { 346261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.execSQL("CREATE TABLE " + TABLE + " (" + 347261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 348261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_KEY + " TEXT," + 349261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_USERID + " INTEGER," + 350261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_VALUE + " TEXT" + 351261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos ");"); 352261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 353261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 354261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos @Override 355261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void onCreate(SQLiteDatabase db) { 356261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos createTable(db); 357261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mCallback.initialize(db); 358261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 359261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 360261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos @Override 361261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { 362261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos int upgradeVersion = oldVersion; 363261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (upgradeVersion == 1) { 364261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Previously migrated lock screen widget settings. Now defunct. 365261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos upgradeVersion = 2; 366261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 367261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 368261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (upgradeVersion != DATABASE_VERSION) { 369261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Log.w(TAG, "Failed to upgrade database!"); 370261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 371261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 372261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 3733dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 3743dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos /** 3753dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * Cache consistency model: 3763dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * - Writes to storage write directly to the cache, but this MUST happen within the atomic 3773dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * section either provided by the database transaction or mWriteLock, such that writes to the 3783dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * cache and writes to the backing storage are guaranteed to occur in the same order 3793dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * 3803dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * - Reads can populate the cache, but because they are no strong ordering guarantees with 3813dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * respect to writes this precaution is taken: 3823dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * - The cache is assigned a version number that increases every time the cache is modified. 3833dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * Reads from backing storage can only populate the cache if the backing storage 3843dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * has not changed since the load operation has begun. 3853dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * This guarantees that no read operation can shadow a write to the cache that happens 3863dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * after it had begun. 3873dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos */ 3883dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static class Cache { 3893dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private final ArrayMap<CacheKey, Object> mCache = new ArrayMap<>(); 3903dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private final CacheKey mCacheKey = new CacheKey(); 3913dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private int mVersion = 0; 3923dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 3933dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String peekKeyValue(String key, String defaultValue, int userId) { 3943dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos Object cached = peek(CacheKey.TYPE_KEY_VALUE, key, userId); 3953dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return cached == DEFAULT ? defaultValue : (String) cached; 3963dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 3973dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 3983dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos boolean hasKeyValue(String key, int userId) { 3993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return contains(CacheKey.TYPE_KEY_VALUE, key, userId); 4003dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4013dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4023dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putKeyValue(String key, String value, int userId) { 4033dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(CacheKey.TYPE_KEY_VALUE, key, value, userId); 4043dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4053dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4063dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putKeyValueIfUnchanged(String key, Object value, int userId, int version) { 4073dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos putIfUnchanged(CacheKey.TYPE_KEY_VALUE, key, value, userId, version); 4083dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4093dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4103dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos byte[] peekFile(String fileName) { 4113dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return (byte[]) peek(CacheKey.TYPE_FILE, fileName, -1 /* userId */); 4123dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4133dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4143dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos boolean hasFile(String fileName) { 4153dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return contains(CacheKey.TYPE_FILE, fileName, -1 /* userId */); 4163dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4173dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4183dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putFile(String key, byte[] value) { 4193dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(CacheKey.TYPE_FILE, key, value, -1 /* userId */); 4203dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4213dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4223dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putFileIfUnchanged(String key, byte[] value, int version) { 4233dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos putIfUnchanged(CacheKey.TYPE_FILE, key, value, -1 /* userId */, version); 4243dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4253dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4263dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void setFetched(int userId) { 4273dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(CacheKey.TYPE_FETCHED, "isFetched", "true", userId); 4283dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4293dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4303dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos boolean isFetched(int userId) { 4313dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return contains(CacheKey.TYPE_FETCHED, "", userId); 4323dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4333dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4343dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4353dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized void put(int type, String key, Object value, int userId) { 4363dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos // Create a new CachKey here because it may be saved in the map if the key is absent. 4373dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.put(new CacheKey().set(type, key, userId), value); 4383dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mVersion++; 4393dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4403dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4413dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized void putIfUnchanged(int type, String key, Object value, int userId, 4423dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version) { 4433dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (!contains(type, key, userId) && mVersion == version) { 4443dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(type, key, value, userId); 4453dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4463dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4473dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4483dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized boolean contains(int type, String key, int userId) { 4493dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.containsKey(mCacheKey.set(type, key, userId)); 4503dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4513dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4523dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized Object peek(int type, String key, int userId) { 4533dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.get(mCacheKey.set(type, key, userId)); 4543dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4553dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4563dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized int getVersion() { 4573dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mVersion; 4583dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4593dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4603dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized void removeUser(int userId) { 4613dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos for (int i = mCache.size() - 1; i >= 0; i--) { 4623dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.keyAt(i).userId == userId) { 4633dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.removeAt(i); 4643dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4653dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4663dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4673dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos // Make sure in-flight loads can't write to cache. 4683dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mVersion++; 4693dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4703dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 471e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos synchronized void clear() { 472e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mCache.clear(); 473e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mVersion++; 474e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos } 4753dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4763dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static final class CacheKey { 4773dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos static final int TYPE_KEY_VALUE = 0; 4783dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos static final int TYPE_FILE = 1; 4793dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos static final int TYPE_FETCHED = 2; 4803dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4813dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String key; 4823dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int userId; 4833dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int type; 4843dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4853dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public CacheKey set(int type, String key, int userId) { 4863dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos this.type = type; 4873dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos this.key = key; 4883dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos this.userId = userId; 4893dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return this; 4903dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4913dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4923dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos @Override 4933dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public boolean equals(Object obj) { 4943dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (!(obj instanceof CacheKey)) 4953dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return false; 4963dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos CacheKey o = (CacheKey) obj; 4973dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return userId == o.userId && type == o.type && key.equals(o.key); 4983dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 4993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5003dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos @Override 5013dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public int hashCode() { 5023dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return key.hashCode() ^ userId ^ type; 5033dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5043dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5053dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 506261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos} 507