Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 261d5ab8 authored by Adrian Roos's avatar Adrian Roos
Browse files

Refactor LockSettingsService into storage and logic

Creates a new LockSettingsStorage class to handle
stroring and retreiving data.

Bug: 18163444
Change-Id: Ia3bc4ad679c5e1217756ae6b3b80cfce3599baed
parent 1482b053
Loading
Loading
Loading
Loading
+57 −278
Original line number Diff line number Diff line
@@ -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,64 +356,42 @@ 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) {
        byte[] hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(pattern));
        byte[] storedHash = mStorage.readPatternHash(userId);

        if (storedHash == null) {
            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);

        boolean matched = Arrays.equals(hash, storedHash);
        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);
        }
        return true;
    }

    @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) {
        byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
        byte[] storedHash = mStorage.readPasswordHash(userId);

        if (storedHash == null) {
            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);

        boolean matched = Arrays.equals(hash, storedHash);
        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);
        }
        return true;
    }

    @Override
@@ -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 −0

File added.

Preview size limit exceeded, changes collapsed.