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/"; 59e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales private static final String LOCK_PATTERN_FILE = "gatekeeper.pattern.key"; 60e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales private static final String BASE_ZERO_LOCK_PATTERN_FILE = "gatekeeper.gesture.key"; 618fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key"; 628fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key"; 638fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key"; 64261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 653dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static final Object DEFAULT = new Object(); 663dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 67261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final DatabaseHelper mOpenHelper; 68261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final Context mContext; 693dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private final Cache mCache = new Cache(); 70261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final Object mFileWriteLock = new Object(); 71261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 728fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales private int mStoredCredentialType; 738fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 748fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales class CredentialHash { 758fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales static final int TYPE_NONE = -1; 768fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales static final int TYPE_PATTERN = 1; 778fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales static final int TYPE_PASSWORD = 2; 788fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 798fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales static final int VERSION_LEGACY = 0; 808fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales static final int VERSION_GATEKEEPER = 1; 818fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 828fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales CredentialHash(byte[] hash, int version) { 838fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales this.hash = hash; 848fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales this.version = version; 85e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales this.isBaseZeroPattern = false; 86e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales } 87e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales 88e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales CredentialHash(byte[] hash, boolean isBaseZeroPattern) { 89e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales this.hash = hash; 90e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales this.version = VERSION_GATEKEEPER; 91e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales this.isBaseZeroPattern = isBaseZeroPattern; 928fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 938fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 948fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales byte[] hash; 958fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales int version; 96e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales boolean isBaseZeroPattern; 978fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 988fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public LockSettingsStorage(Context context, Callback callback) { 100261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mContext = context; 101261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mOpenHelper = new DatabaseHelper(context, callback); 102261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 103261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1043dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public void writeKeyValue(String key, String value, int userId) { 105261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId); 106261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 107261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1083dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) { 109261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos ContentValues cv = new ContentValues(); 110261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cv.put(COLUMN_KEY, key); 111261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cv.put(COLUMN_USERID, userId); 112261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cv.put(COLUMN_VALUE, value); 113261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 114261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.beginTransaction(); 115261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 116261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", 117261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos new String[] {key, Integer.toString(userId)}); 118261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.insert(TABLE, null, cv); 119261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.setTransactionSuccessful(); 1203dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putKeyValue(key, value, userId); 121261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 122261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.endTransaction(); 123261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 124261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 125261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 126261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1273dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public String readKeyValue(String key, String defaultValue, int userId) { 1283dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version; 1293dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized (mCache) { 1303dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.hasKeyValue(key, userId)) { 1313dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.peekKeyValue(key, defaultValue, userId); 1323dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1333dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos version = mCache.getVersion(); 1343dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1353dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 136261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Cursor cursor; 1373dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos Object result = DEFAULT; 138261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 139261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, 140261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", 141261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos new String[] { Integer.toString(userId), key }, 142261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos null, null, null)) != null) { 143261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (cursor.moveToFirst()) { 144261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos result = cursor.getString(0); 145261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 146261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos cursor.close(); 147261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 1483dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putKeyValueIfUnchanged(key, result, userId, version); 1493dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return result == DEFAULT ? defaultValue : (String) result; 1503dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1513dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 1523dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public void prefetchUser(int userId) { 1533dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version; 1543dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized (mCache) { 1553dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.isFetched(userId)) { 1563dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return; 1573dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1583dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.setFetched(userId); 1593dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos version = mCache.getVersion(); 1603dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1613dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 1623dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos Cursor cursor; 1633dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 1643dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if ((cursor = db.query(TABLE, COLUMNS_FOR_PREFETCH, 1653dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos COLUMN_USERID + "=?", 1663dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos new String[] { Integer.toString(userId) }, 1673dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos null, null, null)) != null) { 1683dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos while (cursor.moveToNext()) { 1693dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String key = cursor.getString(0); 1703dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String value = cursor.getString(1); 1713dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putKeyValueIfUnchanged(key, value, userId, version); 1723dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1733dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos cursor.close(); 1743dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 1753dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 1763dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos // Populate cache by reading the password and pattern files. 1773dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos readPasswordHash(userId); 1783dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos readPatternHash(userId); 179261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 180261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 1818fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales public int getStoredCredentialType(int userId) { 1828fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (mStoredCredentialType != 0) { 1838fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return mStoredCredentialType; 1848fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 1858fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 1868fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales CredentialHash pattern = readPatternHash(userId); 1878fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (pattern == null) { 1888fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (readPasswordHash(userId) != null) { 1898fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = CredentialHash.TYPE_PASSWORD; 1908fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } else { 1918fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = CredentialHash.TYPE_NONE; 1928fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 1938fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } else { 1948fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales CredentialHash password = readPasswordHash(userId); 1958fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (password != null) { 1968fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales // Both will never be GateKeeper 1978fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (password.version == CredentialHash.VERSION_GATEKEEPER) { 1988fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = CredentialHash.TYPE_PASSWORD; 1998fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } else { 2008fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = CredentialHash.TYPE_PATTERN; 2018fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 2028fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } else { 2038fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = CredentialHash.TYPE_PATTERN; 2048fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 2058fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 2068fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 2078fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return mStoredCredentialType; 2088fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 2098fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 2108fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 2118fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales public CredentialHash readPasswordHash(int userId) { 2128fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales byte[] stored = readFile(getLockPasswordFilename(userId)); 213261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (stored != null && stored.length > 0) { 2148fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER); 215261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 2168fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 2178fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales stored = readFile(getLegacyLockPasswordFilename(userId)); 2188fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (stored != null && stored.length > 0) { 2198fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return new CredentialHash(stored, CredentialHash.VERSION_LEGACY); 2208fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 2218fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 222261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return null; 223261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 224261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 2258fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales public CredentialHash readPatternHash(int userId) { 2268fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales byte[] stored = readFile(getLockPatternFilename(userId)); 227261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (stored != null && stored.length > 0) { 2288fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER); 229261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 2308fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 231e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales stored = readFile(getBaseZeroLockPatternFilename(userId)); 232e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales if (stored != null && stored.length > 0) { 233e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales return new CredentialHash(stored, true); 234e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales } 235e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales 2368fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales stored = readFile(getLegacyLockPatternFilename(userId)); 2378fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales if (stored != null && stored.length > 0) { 2388fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return new CredentialHash(stored, CredentialHash.VERSION_LEGACY); 2398fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 2408fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 241261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return null; 242261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 243261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 244e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales 2453dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public boolean hasPassword(int userId) { 2468fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return hasFile(getLockPasswordFilename(userId)) || 2478fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales hasFile(getLegacyLockPasswordFilename(userId)); 248261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 249261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 2503dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public boolean hasPattern(int userId) { 2518fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return hasFile(getLockPatternFilename(userId)) || 252e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales hasFile(getBaseZeroLockPatternFilename(userId)) || 2538fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales hasFile(getLegacyLockPatternFilename(userId)); 254261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 255261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 2563dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private boolean hasFile(String name) { 2573dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos byte[] contents = readFile(name); 258261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return contents != null && contents.length > 0; 259261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 260261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 2613dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private byte[] readFile(String name) { 2623dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version; 2633dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized (mCache) { 2643dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.hasFile(name)) { 2653dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.peekFile(name); 2663dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 2673dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos version = mCache.getVersion(); 2683dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 2693dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 270261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos RandomAccessFile raf = null; 271261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos byte[] stored = null; 272261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 273261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf = new RandomAccessFile(name, "r"); 274261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos stored = new byte[(int) raf.length()]; 275261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.readFully(stored, 0, stored.length); 276261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 277261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 278261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Cannot read file " + e); 279261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 280261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (raf != null) { 281261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 282261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 283261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 284261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Error closing file " + e); 285261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 286261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 287261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 2883dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFileIfUnchanged(name, stored, version); 289261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return stored; 290261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 291261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 2923dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private void writeFile(String name, byte[] hash) { 293261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos synchronized (mFileWriteLock) { 294261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos RandomAccessFile raf = null; 295261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 296261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Write the hash to file 297261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf = new RandomAccessFile(name, "rw"); 298261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Truncate the file if pattern is null, to clear the lock 299261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (hash == null || hash.length == 0) { 300261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.setLength(0); 301261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } else { 302261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.write(hash, 0, hash.length); 303261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 304261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 305261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 306261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Error writing to file " + e); 307261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 308261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (raf != null) { 309261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 310261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos raf.close(); 311261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } catch (IOException e) { 312261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Slog.e(TAG, "Error closing file " + e); 313261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 314261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 315261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 3163dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFile(name, hash); 317261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 318261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 319261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 320e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales private void deleteFile(String name) { 321e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales File f = new File(name); 322e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales if (f != null) { 323e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales f.delete(); 324e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales } 325e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales } 326e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales 327261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void writePatternHash(byte[] hash, int userId) { 3288fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = hash == null 3298fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales ? CredentialHash.TYPE_NONE 3308fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales : CredentialHash.TYPE_PATTERN; 3313dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos writeFile(getLockPatternFilename(userId), hash); 33268e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee clearPasswordHash(userId); 33368e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee } 33468e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee 33568e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee private void clearPatternHash(int userId) { 33668e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee writeFile(getLockPatternFilename(userId), null); 337261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 338261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 339261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void writePasswordHash(byte[] hash, int userId) { 3408fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales mStoredCredentialType = hash == null 3418fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales ? CredentialHash.TYPE_NONE 3428fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales : CredentialHash.TYPE_PASSWORD; 3433dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos writeFile(getLockPasswordFilename(userId), hash); 34468e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee clearPatternHash(userId); 345261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 346261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 34768e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee private void clearPasswordHash(int userId) { 34868e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee writeFile(getLockPasswordFilename(userId), null); 34968e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee } 350261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 351e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 352e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos String getLockPatternFilename(int userId) { 3533dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return getLockCredentialFilePathForUser(userId, LOCK_PATTERN_FILE); 354261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 355261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 356e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 357e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos String getLockPasswordFilename(int userId) { 3583dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return getLockCredentialFilePathForUser(userId, LOCK_PASSWORD_FILE); 3593dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 3603dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 3618fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales @VisibleForTesting 3628fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales String getLegacyLockPatternFilename(int userId) { 3638fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PATTERN_FILE); 3648fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 3658fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 3668fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales @VisibleForTesting 3678fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales String getLegacyLockPasswordFilename(int userId) { 3688fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PASSWORD_FILE); 3698fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales } 3708fa5665f0e757cec0063fb4cf1354f1596f93a91Andres Morales 371e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales private String getBaseZeroLockPatternFilename(int userId) { 372e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE); 373e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales } 374e40bad8cf9397becdf05776c775c8286d3de46faAndres Morales 3753dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private String getLockCredentialFilePathForUser(int userId, String basename) { 376261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos userId = getUserParentOrSelfId(userId); 377261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos String dataSystemDirectory = 378261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos android.os.Environment.getDataDirectory().getAbsolutePath() + 379261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos SYSTEM_DIRECTORY; 380261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (userId == 0) { 381261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Leave it in the same place for user 0 3823dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return dataSystemDirectory + basename; 383261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } else { 3843dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return new File(Environment.getUserSystemDirectory(userId), basename).getAbsolutePath(); 385261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 386261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 387261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 388261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private int getUserParentOrSelfId(int userId) { 389261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (userId != 0) { 390261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 391261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserInfo pi = um.getProfileParent(userId); 392261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (pi != null) { 393261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return pi.id; 394261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 395261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 396261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos return userId; 397261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 398261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 399261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void removeUser(int userId) { 400261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 401261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 402261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); 403261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos final UserInfo parentInfo = um.getProfileParent(userId); 404261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 40568e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee if (parentInfo == null) { 40668e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee // This user owns its lock settings files - safe to delete them 40768e4ba4dd0412f7923d3c22c92ebae485fc89961Robin Lee synchronized (mFileWriteLock) { 4083dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String name = getLockPasswordFilename(userId); 4093dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos File file = new File(name); 410261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (file.exists()) { 411261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos file.delete(); 4123dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFile(name, null); 413261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 4143dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos name = getLockPatternFilename(userId); 4153dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos file = new File(name); 416261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (file.exists()) { 417261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos file.delete(); 4183dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.putFile(name, null); 419261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 420261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 421261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 422261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 423261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos try { 424261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.beginTransaction(); 425261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); 426261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.setTransactionSuccessful(); 4273dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.removeUser(userId); 428261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } finally { 429261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.endTransaction(); 430261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 431261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 432261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 433e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 434e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos void closeDatabase() { 435e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mOpenHelper.close(); 436e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos } 437e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos 438e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos @VisibleForTesting 439e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos void clearCache() { 440e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mCache.clear(); 441e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos } 442261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 4433dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public interface Callback { 444261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos void initialize(SQLiteDatabase db); 445261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 446261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 447261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos class DatabaseHelper extends SQLiteOpenHelper { 448261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String TAG = "LockSettingsDB"; 449261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final String DATABASE_NAME = "locksettings.db"; 450261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 451261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private static final int DATABASE_VERSION = 2; 452261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 453261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private final Callback mCallback; 454261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 455261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public DatabaseHelper(Context context, Callback callback) { 456261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos super(context, DATABASE_NAME, null, DATABASE_VERSION); 457261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos setWriteAheadLoggingEnabled(true); 458261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mCallback = callback; 459261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 460261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 461261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos private void createTable(SQLiteDatabase db) { 462261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos db.execSQL("CREATE TABLE " + TABLE + " (" + 463261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 464261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_KEY + " TEXT," + 465261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_USERID + " INTEGER," + 466261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos COLUMN_VALUE + " TEXT" + 467261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos ");"); 468261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 469261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 470261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos @Override 471261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void onCreate(SQLiteDatabase db) { 472261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos createTable(db); 473261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos mCallback.initialize(db); 474261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 475261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 476261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos @Override 477261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { 478261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos int upgradeVersion = oldVersion; 479261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (upgradeVersion == 1) { 480261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos // Previously migrated lock screen widget settings. Now defunct. 481261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos upgradeVersion = 2; 482261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 483261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos 484261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos if (upgradeVersion != DATABASE_VERSION) { 485261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos Log.w(TAG, "Failed to upgrade database!"); 486261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 487261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 488261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos } 4893dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 4903dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos /** 4913dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * Cache consistency model: 4923dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * - Writes to storage write directly to the cache, but this MUST happen within the atomic 4933dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * section either provided by the database transaction or mWriteLock, such that writes to the 4943dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * cache and writes to the backing storage are guaranteed to occur in the same order 4953dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * 4963dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * - Reads can populate the cache, but because they are no strong ordering guarantees with 4973dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * respect to writes this precaution is taken: 4983dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * - The cache is assigned a version number that increases every time the cache is modified. 4993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * Reads from backing storage can only populate the cache if the backing storage 5003dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * has not changed since the load operation has begun. 5013dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * This guarantees that no read operation can shadow a write to the cache that happens 5023dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos * after it had begun. 5033dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos */ 5043dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static class Cache { 5053dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private final ArrayMap<CacheKey, Object> mCache = new ArrayMap<>(); 5063dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private final CacheKey mCacheKey = new CacheKey(); 5073dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private int mVersion = 0; 5083dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5093dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String peekKeyValue(String key, String defaultValue, int userId) { 5103dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos Object cached = peek(CacheKey.TYPE_KEY_VALUE, key, userId); 5113dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return cached == DEFAULT ? defaultValue : (String) cached; 5123dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5133dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5143dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos boolean hasKeyValue(String key, int userId) { 5153dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return contains(CacheKey.TYPE_KEY_VALUE, key, userId); 5163dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5173dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5183dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putKeyValue(String key, String value, int userId) { 5193dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(CacheKey.TYPE_KEY_VALUE, key, value, userId); 5203dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5213dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5223dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putKeyValueIfUnchanged(String key, Object value, int userId, int version) { 5233dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos putIfUnchanged(CacheKey.TYPE_KEY_VALUE, key, value, userId, version); 5243dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5253dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5263dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos byte[] peekFile(String fileName) { 5273dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return (byte[]) peek(CacheKey.TYPE_FILE, fileName, -1 /* userId */); 5283dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5293dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5303dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos boolean hasFile(String fileName) { 5313dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return contains(CacheKey.TYPE_FILE, fileName, -1 /* userId */); 5323dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5333dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5343dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putFile(String key, byte[] value) { 5353dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(CacheKey.TYPE_FILE, key, value, -1 /* userId */); 5363dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5373dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5383dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void putFileIfUnchanged(String key, byte[] value, int version) { 5393dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos putIfUnchanged(CacheKey.TYPE_FILE, key, value, -1 /* userId */, version); 5403dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5413dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5423dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos void setFetched(int userId) { 5433dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(CacheKey.TYPE_FETCHED, "isFetched", "true", userId); 5443dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5453dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5463dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos boolean isFetched(int userId) { 5473dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return contains(CacheKey.TYPE_FETCHED, "", userId); 5483dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5493dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5503dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5513dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized void put(int type, String key, Object value, int userId) { 5523dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos // Create a new CachKey here because it may be saved in the map if the key is absent. 5533dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.put(new CacheKey().set(type, key, userId), value); 5543dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mVersion++; 5553dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5563dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5573dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized void putIfUnchanged(int type, String key, Object value, int userId, 5583dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int version) { 5593dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (!contains(type, key, userId) && mVersion == version) { 5603dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos put(type, key, value, userId); 5613dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5623dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5633dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5643dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized boolean contains(int type, String key, int userId) { 5653dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.containsKey(mCacheKey.set(type, key, userId)); 5663dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5673dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5683dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized Object peek(int type, String key, int userId) { 5693dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mCache.get(mCacheKey.set(type, key, userId)); 5703dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5713dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5723dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private synchronized int getVersion() { 5733dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return mVersion; 5743dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5753dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5763dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos synchronized void removeUser(int userId) { 5773dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos for (int i = mCache.size() - 1; i >= 0; i--) { 5783dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (mCache.keyAt(i).userId == userId) { 5793dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mCache.removeAt(i); 5803dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5813dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5823dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5833dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos // Make sure in-flight loads can't write to cache. 5843dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos mVersion++; 5853dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 5863dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 587e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos synchronized void clear() { 588e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mCache.clear(); 589e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos mVersion++; 590e542499a304f067372d85722e11a74b4e56b0bd7Adrian Roos } 5913dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5923dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos private static final class CacheKey { 5933dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos static final int TYPE_KEY_VALUE = 0; 5943dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos static final int TYPE_FILE = 1; 5953dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos static final int TYPE_FETCHED = 2; 5963dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 5973dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos String key; 5983dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int userId; 5993dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos int type; 6003dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 6013dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public CacheKey set(int type, String key, int userId) { 6023dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos this.type = type; 6033dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos this.key = key; 6043dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos this.userId = userId; 6053dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return this; 6063dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 6073dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 6083dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos @Override 6093dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public boolean equals(Object obj) { 6103dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos if (!(obj instanceof CacheKey)) 6113dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return false; 6123dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos CacheKey o = (CacheKey) obj; 6133dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return userId == o.userId && type == o.type && key.equals(o.key); 6143dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 6153dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos 6163dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos @Override 6173dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos public int hashCode() { 6183dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos return key.hashCode() ^ userId ^ type; 6193dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 6203dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 6213dcae68501a1fc1c433d12a9d55a31c7eaab016cAdrian Roos } 622261d5ab8f4c3fdd34163468fd48ab07f7ad13d3cAdrian Roos} 623