OSDN Git Service

Refactor LockSettingsService into storage and logic
authorAdrian Roos <roosa@google.com>
Wed, 29 Oct 2014 13:42:38 +0000 (14:42 +0100)
committerAdrian Roos <roosa@google.com>
Tue, 4 Nov 2014 16:38:20 +0000 (17:38 +0100)
Creates a new LockSettingsStorage class to handle
stroring and retreiving data.

Bug: 18163444
Change-Id: Ia3bc4ad679c5e1217756ae6b3b80cfce3599baed

services/core/java/com/android/server/LockSettingsService.java
services/core/java/com/android/server/LockSettingsStorage.java [new file with mode: 0644]

index b708c3f..11ba8e8 100644 (file)
@@ -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 TAG = "LockSettingsService";
 
-    private static final String[] COLUMNS_FOR_QUERY = {
-        COLUMN_VALUE
-    };
+    private final Context mContext;
 
-    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 LockSettingsStorage mStorage;
 
-    private final Context mContext;
     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;
+        }
+
+        boolean matched = Arrays.equals(hash, storedHash);
+        if (matched && !TextUtils.isEmpty(password)) {
+            maybeUpdateKeystore(password, userId);
         }
-        return true;
+        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,
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
new file mode 100644 (file)
index 0000000..acbf8ef
--- /dev/null
@@ -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!");
+            }
+        }
+    }
+}