am a5dd432c: am e94a84d2: Merge "Refactor LockSettingsService into storage and logic" into lmp-mr1-dev
* commit 'a5dd432c088487374c08395aed98c7eacdfa791a': Refactor LockSettingsService into storage and logic
This commit is contained in:
@@ -18,49 +18,38 @@ package com.android.server;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
|
||||
import static android.content.Context.USER_SERVICE;
|
||||
import static android.Manifest.permission.READ_PROFILE;
|
||||
import android.database.Cursor;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.Binder;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.storage.IMountService;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.provider.Settings.SettingNotFoundException;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChain.KeyChainConnection;
|
||||
import android.security.KeyStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.widget.ILockSettings;
|
||||
import com.android.internal.widget.ILockSettingsObserver;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -73,27 +62,17 @@ import java.util.List;
|
||||
*/
|
||||
public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
|
||||
private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
|
||||
|
||||
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
|
||||
|
||||
private final DatabaseHelper mOpenHelper;
|
||||
|
||||
private static final String TAG = "LockSettingsService";
|
||||
|
||||
private static final String TABLE = "locksettings";
|
||||
private static final String COLUMN_KEY = "name";
|
||||
private static final String COLUMN_USERID = "user";
|
||||
private static final String COLUMN_VALUE = "value";
|
||||
|
||||
private static final String[] COLUMNS_FOR_QUERY = {
|
||||
COLUMN_VALUE
|
||||
};
|
||||
|
||||
private static final String SYSTEM_DIRECTORY = "/system/";
|
||||
private static final String LOCK_PATTERN_FILE = "gesture.key";
|
||||
private static final String LOCK_PASSWORD_FILE = "password.key";
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final LockSettingsStorage mStorage;
|
||||
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private boolean mFirstCallToVold;
|
||||
|
||||
@@ -102,7 +81,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
public LockSettingsService(Context context) {
|
||||
mContext = context;
|
||||
// Open the database
|
||||
mOpenHelper = new DatabaseHelper(mContext);
|
||||
|
||||
mLockPatternUtils = new LockPatternUtils(context);
|
||||
mFirstCallToVold = true;
|
||||
@@ -110,6 +88,18 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_USER_ADDED);
|
||||
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
|
||||
|
||||
mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
|
||||
@Override
|
||||
public void initialize(SQLiteDatabase db) {
|
||||
// Get the lockscreen default from a system property, if available
|
||||
boolean lockScreenDisable = SystemProperties.getBoolean(
|
||||
"ro.lockscreen.disable.default", false);
|
||||
if (lockScreenDisable) {
|
||||
mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@@ -220,29 +210,31 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
@Override
|
||||
public void setBoolean(String key, boolean value, int userId) throws RemoteException {
|
||||
checkWritePermission(userId);
|
||||
|
||||
writeToDb(key, value ? "1" : "0", userId);
|
||||
setStringUnchecked(key, userId, value ? "1" : "0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLong(String key, long value, int userId) throws RemoteException {
|
||||
checkWritePermission(userId);
|
||||
|
||||
writeToDb(key, Long.toString(value), userId);
|
||||
setStringUnchecked(key, userId, Long.toString(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setString(String key, String value, int userId) throws RemoteException {
|
||||
checkWritePermission(userId);
|
||||
setStringUnchecked(key, userId, value);
|
||||
}
|
||||
|
||||
writeToDb(key, value, userId);
|
||||
private void setStringUnchecked(String key, int userId, String value) {
|
||||
mStorage.writeKeyValue(key, value, userId);
|
||||
notifyObservers(key, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
|
||||
checkReadPermission(key, userId);
|
||||
|
||||
String value = readFromDb(key, null, userId);
|
||||
String value = mStorage.readKeyValue(key, null, userId);
|
||||
return TextUtils.isEmpty(value) ?
|
||||
defaultValue : (value.equals("1") || value.equals("true"));
|
||||
}
|
||||
@@ -251,7 +243,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
public long getLong(String key, long defaultValue, int userId) throws RemoteException {
|
||||
checkReadPermission(key, userId);
|
||||
|
||||
String value = readFromDb(key, null, userId);
|
||||
String value = mStorage.readKeyValue(key, null, userId);
|
||||
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
|
||||
}
|
||||
|
||||
@@ -259,7 +251,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
public String getString(String key, String defaultValue, int userId) throws RemoteException {
|
||||
checkReadPermission(key, userId);
|
||||
|
||||
return readFromDb(key, defaultValue, userId);
|
||||
return mStorage.readKeyValue(key, defaultValue, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -308,57 +300,18 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private int getUserParentOrSelfId(int userId) {
|
||||
if (userId != 0) {
|
||||
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
|
||||
final UserInfo pi = um.getProfileParent(userId);
|
||||
if (pi != null) {
|
||||
return pi.id;
|
||||
}
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
private String getLockPatternFilename(int userId) {
|
||||
String dataSystemDirectory =
|
||||
android.os.Environment.getDataDirectory().getAbsolutePath() +
|
||||
SYSTEM_DIRECTORY;
|
||||
userId = getUserParentOrSelfId(userId);
|
||||
if (userId == 0) {
|
||||
// Leave it in the same place for user 0
|
||||
return dataSystemDirectory + LOCK_PATTERN_FILE;
|
||||
} else {
|
||||
return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
|
||||
.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
private String getLockPasswordFilename(int userId) {
|
||||
userId = getUserParentOrSelfId(userId);
|
||||
String dataSystemDirectory =
|
||||
android.os.Environment.getDataDirectory().getAbsolutePath() +
|
||||
SYSTEM_DIRECTORY;
|
||||
if (userId == 0) {
|
||||
// Leave it in the same place for user 0
|
||||
return dataSystemDirectory + LOCK_PASSWORD_FILE;
|
||||
} else {
|
||||
return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
|
||||
.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean havePassword(int userId) throws RemoteException {
|
||||
// Do we need a permissions check here?
|
||||
|
||||
return new File(getLockPasswordFilename(userId)).length() > 0;
|
||||
return mStorage.hasPassword(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean havePattern(int userId) throws RemoteException {
|
||||
// Do we need a permissions check here?
|
||||
|
||||
return new File(getLockPatternFilename(userId)).length() > 0;
|
||||
return mStorage.hasPattern(userId);
|
||||
}
|
||||
|
||||
private void maybeUpdateKeystore(String password, int userHandle) {
|
||||
@@ -394,7 +347,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
final byte[] hash = LockPatternUtils.patternToHash(
|
||||
LockPatternUtils.stringToPattern(pattern));
|
||||
writeFile(getLockPatternFilename(userId), hash);
|
||||
mStorage.writePatternHash(hash, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -403,68 +356,46 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
maybeUpdateKeystore(password, userId);
|
||||
|
||||
writeFile(getLockPasswordFilename(userId),
|
||||
mLockPatternUtils.passwordToHash(password, userId));
|
||||
mStorage.writePasswordHash(mLockPatternUtils.passwordToHash(password, userId), userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPattern(String pattern, int userId) throws RemoteException {
|
||||
checkPasswordReadPermission(userId);
|
||||
try {
|
||||
// Read all the bytes from the file
|
||||
RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r");
|
||||
final byte[] stored = new byte[(int) raf.length()];
|
||||
int got = raf.read(stored, 0, stored.length);
|
||||
raf.close();
|
||||
if (got <= 0) {
|
||||
return true;
|
||||
}
|
||||
// Compare the hash from the file with the entered pattern's hash
|
||||
final byte[] hash = LockPatternUtils.patternToHash(
|
||||
LockPatternUtils.stringToPattern(pattern));
|
||||
final boolean matched = Arrays.equals(stored, hash);
|
||||
if (matched && !TextUtils.isEmpty(pattern)) {
|
||||
maybeUpdateKeystore(pattern, userId);
|
||||
}
|
||||
return matched;
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
Slog.e(TAG, "Cannot read file " + fnfe);
|
||||
} catch (IOException ioe) {
|
||||
Slog.e(TAG, "Cannot read file " + ioe);
|
||||
byte[] hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(pattern));
|
||||
byte[] storedHash = mStorage.readPatternHash(userId);
|
||||
|
||||
if (storedHash == null) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
boolean matched = Arrays.equals(hash, storedHash);
|
||||
if (matched && !TextUtils.isEmpty(pattern)) {
|
||||
maybeUpdateKeystore(pattern, userId);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPassword(String password, int userId) throws RemoteException {
|
||||
checkPasswordReadPermission(userId);
|
||||
|
||||
try {
|
||||
// Read all the bytes from the file
|
||||
RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r");
|
||||
final byte[] stored = new byte[(int) raf.length()];
|
||||
int got = raf.read(stored, 0, stored.length);
|
||||
raf.close();
|
||||
if (got <= 0) {
|
||||
return true;
|
||||
}
|
||||
// Compare the hash from the file with the entered password's hash
|
||||
final byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
|
||||
final boolean matched = Arrays.equals(stored, hash);
|
||||
if (matched && !TextUtils.isEmpty(password)) {
|
||||
maybeUpdateKeystore(password, userId);
|
||||
}
|
||||
return matched;
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
Slog.e(TAG, "Cannot read file " + fnfe);
|
||||
} catch (IOException ioe) {
|
||||
Slog.e(TAG, "Cannot read file " + ioe);
|
||||
byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
|
||||
byte[] storedHash = mStorage.readPasswordHash(userId);
|
||||
|
||||
if (storedHash == null) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
boolean matched = Arrays.equals(hash, storedHash);
|
||||
if (matched && !TextUtils.isEmpty(password)) {
|
||||
maybeUpdateKeystore(password, userId);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkVoldPassword(int userId) throws RemoteException {
|
||||
public boolean checkVoldPassword(int userId) throws RemoteException {
|
||||
if (!mFirstCallToVold) {
|
||||
return false;
|
||||
}
|
||||
@@ -512,166 +443,14 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
public void removeUser(int userId) {
|
||||
checkWritePermission(userId);
|
||||
|
||||
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
||||
try {
|
||||
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
|
||||
final UserInfo parentInfo = um.getProfileParent(userId);
|
||||
if (parentInfo == null) {
|
||||
// This user owns its lock settings files - safe to delete them
|
||||
File file = new File(getLockPasswordFilename(userId));
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
file = new File(getLockPatternFilename(userId));
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
db.beginTransaction();
|
||||
db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
mStorage.removeUser(userId);
|
||||
notifyObservers(null /* key */, userId);
|
||||
|
||||
final KeyStore ks = KeyStore.getInstance();
|
||||
final int userUid = UserHandle.getUid(userId, Process.SYSTEM_UID);
|
||||
ks.resetUid(userUid);
|
||||
}
|
||||
|
||||
private void writeFile(String name, byte[] hash) {
|
||||
try {
|
||||
// Write the hash to file
|
||||
RandomAccessFile raf = new RandomAccessFile(name, "rw");
|
||||
// Truncate the file if pattern is null, to clear the lock
|
||||
if (hash == null || hash.length == 0) {
|
||||
raf.setLength(0);
|
||||
} else {
|
||||
raf.write(hash, 0, hash.length);
|
||||
}
|
||||
raf.close();
|
||||
} catch (IOException ioe) {
|
||||
Slog.e(TAG, "Error writing to file " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeToDb(String key, String value, int userId) {
|
||||
writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId);
|
||||
notifyObservers(key, userId);
|
||||
}
|
||||
|
||||
private void writeToDb(SQLiteDatabase db, String key, String value, int userId) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(COLUMN_KEY, key);
|
||||
cv.put(COLUMN_USERID, userId);
|
||||
cv.put(COLUMN_VALUE, value);
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
|
||||
new String[] {key, Integer.toString(userId)});
|
||||
db.insert(TABLE, null, cv);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
private String readFromDb(String key, String defaultValue, int userId) {
|
||||
Cursor cursor;
|
||||
String result = defaultValue;
|
||||
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
|
||||
if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
|
||||
COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
|
||||
new String[] { Integer.toString(userId), key },
|
||||
null, null, null)) != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
result = cursor.getString(0);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class DatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String TAG = "LockSettingsDB";
|
||||
private static final String DATABASE_NAME = "locksettings.db";
|
||||
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
|
||||
public DatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
setWriteAheadLoggingEnabled(true);
|
||||
}
|
||||
|
||||
private void createTable(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE " + TABLE + " (" +
|
||||
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
COLUMN_KEY + " TEXT," +
|
||||
COLUMN_USERID + " INTEGER," +
|
||||
COLUMN_VALUE + " TEXT" +
|
||||
");");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
createTable(db);
|
||||
initializeDefaults(db);
|
||||
}
|
||||
|
||||
private void initializeDefaults(SQLiteDatabase db) {
|
||||
// Get the lockscreen default from a system property, if available
|
||||
boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default",
|
||||
false);
|
||||
if (lockScreenDisable) {
|
||||
writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
|
||||
int upgradeVersion = oldVersion;
|
||||
if (upgradeVersion == 1) {
|
||||
// Set the initial value for {@link LockPatternUtils#LOCKSCREEN_WIDGETS_ENABLED}
|
||||
// during upgrade based on whether each user previously had widgets in keyguard.
|
||||
maybeEnableWidgetSettingForUsers(db);
|
||||
upgradeVersion = 2;
|
||||
}
|
||||
|
||||
if (upgradeVersion != DATABASE_VERSION) {
|
||||
Log.w(TAG, "Failed to upgrade database!");
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeEnableWidgetSettingForUsers(SQLiteDatabase db) {
|
||||
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
final List<UserInfo> users = um.getUsers();
|
||||
for (int i = 0; i < users.size(); i++) {
|
||||
final int userId = users.get(i).id;
|
||||
final boolean enabled = mLockPatternUtils.hasWidgetsEnabledInKeyguard(userId);
|
||||
Log.v(TAG, "Widget upgrade uid=" + userId + ", enabled="
|
||||
+ enabled + ", w[]=" + mLockPatternUtils.getAppWidgets());
|
||||
loadSetting(db, LockPatternUtils.LOCKSCREEN_WIDGETS_ENABLED, userId, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSetting(SQLiteDatabase db, String key, int userId, boolean value) {
|
||||
SQLiteStatement stmt = null;
|
||||
try {
|
||||
stmt = db.compileStatement(
|
||||
"INSERT OR REPLACE INTO locksettings(name,user,value) VALUES(?,?,?);");
|
||||
stmt.bindString(1, key);
|
||||
stmt.bindLong(2, userId);
|
||||
stmt.bindLong(3, value ? 1 : 0);
|
||||
stmt.execute();
|
||||
} finally {
|
||||
if (stmt != null) stmt.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] VALID_SETTINGS = new String[] {
|
||||
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
|
||||
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
|
||||
|
||||
307
services/core/java/com/android/server/LockSettingsStorage.java
Normal file
307
services/core/java/com/android/server/LockSettingsStorage.java
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Environment;
|
||||
import android.os.UserManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import static android.content.Context.USER_SERVICE;
|
||||
|
||||
/**
|
||||
* Storage for the lock settings service.
|
||||
*/
|
||||
class LockSettingsStorage {
|
||||
|
||||
private static final String TAG = "LockSettingsStorage";
|
||||
private static final String TABLE = "locksettings";
|
||||
|
||||
private static final String COLUMN_KEY = "name";
|
||||
private static final String COLUMN_USERID = "user";
|
||||
private static final String COLUMN_VALUE = "value";
|
||||
|
||||
private static final String[] COLUMNS_FOR_QUERY = {
|
||||
COLUMN_VALUE
|
||||
};
|
||||
|
||||
private static final String SYSTEM_DIRECTORY = "/system/";
|
||||
private static final String LOCK_PATTERN_FILE = "gesture.key";
|
||||
private static final String LOCK_PASSWORD_FILE = "password.key";
|
||||
|
||||
private final DatabaseHelper mOpenHelper;
|
||||
private final Context mContext;
|
||||
private final Object mFileWriteLock = new Object();
|
||||
|
||||
LockSettingsStorage(Context context, Callback callback) {
|
||||
mContext = context;
|
||||
mOpenHelper = new DatabaseHelper(context, callback);
|
||||
}
|
||||
|
||||
void writeKeyValue(String key, String value, int userId) {
|
||||
writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId);
|
||||
}
|
||||
|
||||
void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(COLUMN_KEY, key);
|
||||
cv.put(COLUMN_USERID, userId);
|
||||
cv.put(COLUMN_VALUE, value);
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
|
||||
new String[] {key, Integer.toString(userId)});
|
||||
db.insert(TABLE, null, cv);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String readKeyValue(String key, String defaultValue, int userId) {
|
||||
Cursor cursor;
|
||||
String result = defaultValue;
|
||||
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
|
||||
if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
|
||||
COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
|
||||
new String[] { Integer.toString(userId), key },
|
||||
null, null, null)) != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
result = cursor.getString(0);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
byte[] readPasswordHash(int userId) {
|
||||
final byte[] stored = readFile(getLockPasswordFilename(userId), userId);
|
||||
if (stored != null && stored.length > 0) {
|
||||
return stored;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] readPatternHash(int userId) {
|
||||
final byte[] stored = readFile(getLockPatternFilename(userId), userId);
|
||||
if (stored != null && stored.length > 0) {
|
||||
return stored;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean hasPassword(int userId) {
|
||||
return hasFile(getLockPasswordFilename(userId), userId);
|
||||
}
|
||||
|
||||
boolean hasPattern(int userId) {
|
||||
return hasFile(getLockPatternFilename(userId), userId);
|
||||
}
|
||||
|
||||
private boolean hasFile(String name, int userId) {
|
||||
byte[] contents = readFile(name, userId);
|
||||
return contents != null && contents.length > 0;
|
||||
}
|
||||
|
||||
private byte[] readFile(String name, int userId) {
|
||||
RandomAccessFile raf = null;
|
||||
byte[] stored = null;
|
||||
try {
|
||||
raf = new RandomAccessFile(name, "r");
|
||||
stored = new byte[(int) raf.length()];
|
||||
raf.readFully(stored, 0, stored.length);
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Cannot read file " + e);
|
||||
} finally {
|
||||
if (raf != null) {
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Error closing file " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stored;
|
||||
}
|
||||
|
||||
private void writeFile(String name, byte[] hash, int userId) {
|
||||
synchronized (mFileWriteLock) {
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
// Write the hash to file
|
||||
raf = new RandomAccessFile(name, "rw");
|
||||
// Truncate the file if pattern is null, to clear the lock
|
||||
if (hash == null || hash.length == 0) {
|
||||
raf.setLength(0);
|
||||
} else {
|
||||
raf.write(hash, 0, hash.length);
|
||||
}
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Error writing to file " + e);
|
||||
} finally {
|
||||
if (raf != null) {
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Error closing file " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writePatternHash(byte[] hash, int userId) {
|
||||
writeFile(getLockPatternFilename(userId), hash, userId);
|
||||
}
|
||||
|
||||
public void writePasswordHash(byte[] hash, int userId) {
|
||||
writeFile(getLockPasswordFilename(userId), hash, userId);
|
||||
}
|
||||
|
||||
|
||||
private String getLockPatternFilename(int userId) {
|
||||
String dataSystemDirectory =
|
||||
android.os.Environment.getDataDirectory().getAbsolutePath() +
|
||||
SYSTEM_DIRECTORY;
|
||||
userId = getUserParentOrSelfId(userId);
|
||||
if (userId == 0) {
|
||||
// Leave it in the same place for user 0
|
||||
return dataSystemDirectory + LOCK_PATTERN_FILE;
|
||||
} else {
|
||||
return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
|
||||
.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
private String getLockPasswordFilename(int userId) {
|
||||
userId = getUserParentOrSelfId(userId);
|
||||
String dataSystemDirectory =
|
||||
android.os.Environment.getDataDirectory().getAbsolutePath() +
|
||||
SYSTEM_DIRECTORY;
|
||||
if (userId == 0) {
|
||||
// Leave it in the same place for user 0
|
||||
return dataSystemDirectory + LOCK_PASSWORD_FILE;
|
||||
} else {
|
||||
return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
|
||||
.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
private int getUserParentOrSelfId(int userId) {
|
||||
if (userId != 0) {
|
||||
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
|
||||
final UserInfo pi = um.getProfileParent(userId);
|
||||
if (pi != null) {
|
||||
return pi.id;
|
||||
}
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
|
||||
public void removeUser(int userId) {
|
||||
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
||||
|
||||
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
|
||||
final UserInfo parentInfo = um.getProfileParent(userId);
|
||||
|
||||
synchronized (mFileWriteLock) {
|
||||
if (parentInfo == null) {
|
||||
// This user owns its lock settings files - safe to delete them
|
||||
File file = new File(getLockPasswordFilename(userId));
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
file = new File(getLockPatternFilename(userId));
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Callback {
|
||||
void initialize(SQLiteDatabase db);
|
||||
}
|
||||
|
||||
class DatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String TAG = "LockSettingsDB";
|
||||
private static final String DATABASE_NAME = "locksettings.db";
|
||||
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
|
||||
private final Callback mCallback;
|
||||
|
||||
public DatabaseHelper(Context context, Callback callback) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
setWriteAheadLoggingEnabled(true);
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
private void createTable(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE " + TABLE + " (" +
|
||||
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
COLUMN_KEY + " TEXT," +
|
||||
COLUMN_USERID + " INTEGER," +
|
||||
COLUMN_VALUE + " TEXT" +
|
||||
");");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
createTable(db);
|
||||
mCallback.initialize(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
|
||||
int upgradeVersion = oldVersion;
|
||||
if (upgradeVersion == 1) {
|
||||
// Previously migrated lock screen widget settings. Now defunct.
|
||||
upgradeVersion = 2;
|
||||
}
|
||||
|
||||
if (upgradeVersion != DATABASE_VERSION) {
|
||||
Log.w(TAG, "Failed to upgrade database!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user