[FBE] Split accounts.db into two DE/CE databases

Previously the database lived in /data/system/user/X/accounts.db.
It is now split into two databases:
 - DE database(/data/system_de/X/accounts_de.db) is opened when the user is
   running in the locked state. It contains the following tables:
      -- Grants, Shared_Accounts, Debug_Table, Meta.
      -- Accounts table only contains name/type/_id
 - CE database (/data/system_ce/X/accounts_ce.db) is opened when the user is
   unlocked. It contains remaining tables:
      -- Authtokens, Extras, Accounts

CE database is attached to the DE database by using ATTACH DATABASE command.
AccountManager code interacts through a single interface of DeDatabaseHelper,
which includes DE, and CE when the user is unlocked.

Bug: 26279712
Change-Id: I66555b46bdc16fa4a6093470d2500af09d1fbf25
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1632f92..bb32303 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -65,6 +65,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -75,11 +76,13 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
@@ -91,6 +94,7 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.security.GeneralSecurityException;
 import java.security.MessageDigest;
@@ -107,7 +111,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -126,7 +129,9 @@
     private static final String TAG = "AccountManagerService";
 
     private static final String DATABASE_NAME = "accounts.db";
-    private static final int DATABASE_VERSION = 9;
+    private static final int PRE_N_DATABASE_VERSION = 9;
+    private static final int CE_DATABASE_VERSION = 10;
+    private static final int DE_DATABASE_VERSION = 1;
 
     private static final int MAX_DEBUG_DB_SIZE = 64;
 
@@ -176,6 +181,15 @@
     private static final String META_VALUE = "value";
 
     private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
+    private static final String SHARED_ACCOUNTS_ID = "_id";
+
+    private static final String PRE_N_DATABASE_NAME = "accounts.db";
+    private static final String CE_DATABASE_NAME = "accounts_ce.db";
+    private static final String DE_DATABASE_NAME = "accounts_de.db";
+    private static final String CE_DB_PREFIX = "ceDb.";
+    private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
+    private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
+    private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
 
     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
@@ -214,7 +228,7 @@
 
     static class UserAccounts {
         private final int userId;
-        private final DatabaseHelper openHelper;
+        private final DeDatabaseHelper openHelper;
         private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
                 credentialsPermissionNotificationIds =
                 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
@@ -255,15 +269,15 @@
         UserAccounts(Context context, int userId) {
             this.userId = userId;
             synchronized (cacheLock) {
-                openHelper = new DatabaseHelper(context, userId);
+                openHelper = DeDatabaseHelper.create(context, userId);
             }
         }
     }
 
-    private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
+    private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
+    private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
 
-    private static AtomicReference<AccountManagerService> sThis =
-            new AtomicReference<AccountManagerService>();
+    private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
     private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
 
     /**
@@ -381,6 +395,11 @@
      */
     private void validateAccountsInternal(
             UserAccounts accounts, boolean invalidateAuthenticatorCache) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "validateAccountsInternal " + accounts.userId
+                    + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
+                    + " userLocked=" + mUnlockedUsers.get(accounts.userId));
+        }
         if (invalidateAuthenticatorCache) {
             mAuthenticatorCache.invalidateCache(accounts.userId);
         }
@@ -453,8 +472,7 @@
                     null, null, null, null, ACCOUNTS_ID);
             try {
                 accounts.accountCache.clear();
-                final HashMap<String, ArrayList<String>> accountNamesByType =
-                        new LinkedHashMap<String, ArrayList<String>>();
+                final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
                 while (cursor.moveToNext()) {
                     final long accountId = cursor.getLong(0);
                     final String accountType = cursor.getString(1);
@@ -482,15 +500,12 @@
                         accountNames.add(accountName);
                     }
                 }
-                for (Map.Entry<String, ArrayList<String>> cur
-                        : accountNamesByType.entrySet()) {
+                for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
                     final String accountType = cur.getKey();
                     final ArrayList<String> accountNames = cur.getValue();
                     final Account[] accountsForType = new Account[accountNames.size()];
-                    int i = 0;
-                    for (String accountName : accountNames) {
-                        accountsForType[i] = new Account(accountName, accountType);
-                        ++i;
+                    for (int i = 0; i < accountsForType.length; i++) {
+                        accountsForType[i] = new Account(accountNames.get(i), accountType);
                     }
                     accounts.accountCache.put(accountType, accountsForType);
                 }
@@ -522,12 +537,25 @@
     protected UserAccounts getUserAccounts(int userId) {
         synchronized (mUsers) {
             UserAccounts accounts = mUsers.get(userId);
+            boolean validateAccounts = false;
             if (accounts == null) {
                 accounts = new UserAccounts(mContext, userId);
                 initializeDebugDbSizeAndCompileSqlStatementForLogging(
                         accounts.openHelper.getWritableDatabase(), accounts);
                 mUsers.append(userId, accounts);
                 purgeOldGrants(accounts);
+                validateAccounts = true;
+            }
+            // open CE database if necessary
+            if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
+                Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
+                synchronized (accounts.cacheLock) {
+                    CeDatabaseHelper.create(mContext, userId);
+                    accounts.openHelper.attachCeDatabase();
+                }
+                // TODO Synchronize accounts by removing CE account not available in DE
+            }
+            if (validateAccounts) {
                 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
             }
             return accounts;
@@ -571,27 +599,51 @@
         if (userId < 1) return;
 
         UserAccounts accounts;
+        boolean userUnlocked;
         synchronized (mUsers) {
             accounts = mUsers.get(userId);
             mUsers.remove(userId);
+            userUnlocked = mUnlockedUsers.get(userId);
+            mUnlockedUsers.delete(userId);
         }
-        if (accounts == null) {
-            File dbFile = new File(getDatabaseName(userId));
-            dbFile.delete();
-            return;
+        if (accounts != null) {
+            synchronized (accounts.cacheLock) {
+                accounts.openHelper.close();
+            }
         }
+        Log.i(TAG, "Removing database files for user " + userId);
+        File dbFile = new File(getDeDatabaseName(userId));
 
-        synchronized (accounts.cacheLock) {
-            accounts.openHelper.close();
-            File dbFile = new File(getDatabaseName(userId));
-            dbFile.delete();
+        deleteDbFileWarnIfFailed(dbFile);
+        // Remove CE file if user is unlocked, or FBE is not enabled
+        boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
+        if (!fbeEnabled || userUnlocked) {
+            File ceDb = new File(getCeDatabaseName(userId));
+            if (ceDb.exists()) {
+                deleteDbFileWarnIfFailed(ceDb);
+            }
+        }
+    }
+
+    private static void deleteDbFileWarnIfFailed(File dbFile) {
+        if (!SQLiteDatabase.deleteDatabase(dbFile)) {
+            Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
         }
     }
 
     private void onUserUnlocked(Intent intent) {
         int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "onUserUnlocked " + userId);
+        }
+        synchronized (mUsers) {
+            mUnlockedUsers.put(userId, true);
+        }
         if (userId < 1) return;
+        syncSharedAccounts(userId);
+    }
 
+    private void syncSharedAccounts(int userId) {
         // Check if there's a shared account that needs to be created as an account
         Account[] sharedAccounts = getSharedAccountsAsUser(userId);
         if (sharedAccounts == null || sharedAccounts.length == 0) return;
@@ -645,20 +697,15 @@
         if (account == null) {
             return null;
         }
+        if (!isUserUnlocked(accounts.userId)) {
+            Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
+            return null;
+        }
 
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-            Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
-                    new String[]{account.name, account.type}, null, null, null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getString(0);
-                }
-                return null;
-            } finally {
-                cursor.close();
-            }
+            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
+            return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
+                    account.type);
         }
     }
 
@@ -699,7 +746,7 @@
                 try {
                     if (cursor.moveToNext()) {
                         String previousName = cursor.getString(0);
-                        previousNameRef = new AtomicReference<String>(previousName);
+                        previousNameRef = new AtomicReference<>(previousName);
                         accounts.previousNameCache.put(account, previousNameRef);
                         return previousName;
                     } else {
@@ -825,7 +872,7 @@
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
-            return addAccountInternal(accounts, account, password, extras, false, callingUid);
+            return addAccountInternal(accounts, account, password, extras, callingUid);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -999,17 +1046,22 @@
     }
 
     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
-            Bundle extras, boolean restricted, int callingUid) {
+            Bundle extras, int callingUid) {
         Bundle.setDefusable(extras, true);
         if (account == null) {
             return false;
         }
+        if (!isUserUnlocked(accounts.userId)) {
+            Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
+                    + " is locked. callingUid=" + callingUid);
+            return false;
+        }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
             db.beginTransaction();
             try {
                 long numMatches = DatabaseUtils.longForQuery(db,
-                        "select count(*) from " + TABLE_ACCOUNTS
+                        "select count(*) from " + CE_TABLE_ACCOUNTS
                                 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                         new String[]{account.name, account.type});
                 if (numMatches > 0) {
@@ -1021,13 +1073,24 @@
                 values.put(ACCOUNTS_NAME, account.name);
                 values.put(ACCOUNTS_TYPE, account.type);
                 values.put(ACCOUNTS_PASSWORD, password);
-                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
-                long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+                long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
                 if (accountId < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping the DB insert failed");
                     return false;
                 }
+                // Insert into DE table
+                values = new ContentValues();
+                values.put(ACCOUNTS_ID, accountId);
+                values.put(ACCOUNTS_NAME, account.name);
+                values.put(ACCOUNTS_TYPE, account.type);
+                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
+                        System.currentTimeMillis());
+                if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
+                    Log.w(TAG, "insertAccountIntoDatabase: " + account
+                            + ", skipping the DB insert failed");
+                    return false;
+                }
                 if (extras != null) {
                     for (String key : extras.keySet()) {
                         final String value = extras.getString(key);
@@ -1055,6 +1118,12 @@
         return true;
     }
 
+    private boolean isUserUnlocked(int userId) {
+        synchronized (mUsers) {
+            return mUnlockedUsers.get(userId);
+        }
+    }
+
     /**
      * Adds the account to all linked restricted users as shared accounts. If the user is currently
      * running, then clone the account too.
@@ -1079,7 +1148,7 @@
         values.put(EXTRAS_KEY, key);
         values.put(EXTRAS_ACCOUNTS_ID, accountId);
         values.put(EXTRAS_VALUE, value);
-        return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
+        return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
     }
 
     @Override
@@ -1226,17 +1295,19 @@
             }
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
             db.beginTransaction();
             boolean isSuccessful = false;
             Account renamedAccount = new Account(newName, accountToRename.type);
             try {
-                final ContentValues values = new ContentValues();
-                values.put(ACCOUNTS_NAME, newName);
-                values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
                 final long accountId = getAccountIdLocked(db, accountToRename);
                 if (accountId >= 0) {
+                    final ContentValues values = new ContentValues();
+                    values.put(ACCOUNTS_NAME, newName);
                     final String[] argsAccountId = { String.valueOf(accountId) };
+                    db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+                    // Update NAME/PREVIOUS_NAME in DE accounts table
+                    values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
                     db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
                     db.setTransactionSuccessful();
                     isSuccessful = true;
@@ -1332,7 +1403,7 @@
          * authenticator.  This will let users remove accounts (via Settings in the system) but not
          * arbitrary applications (like competing authenticators).
          */
-        UserHandle user = new UserHandle(userId);
+        UserHandle user = UserHandle.of(userId);
         if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
                 && !isSystemUid(callingUid)) {
             String msg = String.format(
@@ -1467,13 +1538,17 @@
     }
 
     private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
+        // For now user is required to be unlocked. TODO: Handle both cases in the future
         int deleted;
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
             final long accountId = getAccountIdLocked(db, account);
             deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
                     + "=?",
                     new String[]{account.name, account.type});
+            // Delete from CE table
+            db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                    new String[]{account.name, account.type});
             removeAccountFromCacheLocked(accounts, account);
             sendAccountsChangedBroadcast(accounts.userId);
 
@@ -1512,7 +1587,7 @@
         try {
             UserAccounts accounts = getUserAccounts(userId);
             synchronized (accounts.cacheLock) {
-                final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+                final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
                 db.beginTransaction();
                 try {
                     invalidateAuthTokenLocked(accounts, db, accountType, authToken);
@@ -1544,22 +1619,22 @@
             return;
         }
         Cursor cursor = db.rawQuery(
-                "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
-                        + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
-                        + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
-                        + " FROM " + TABLE_ACCOUNTS
-                        + " JOIN " + TABLE_AUTHTOKENS
-                        + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
-                        + " = " + AUTHTOKENS_ACCOUNTS_ID
-                        + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
-                        + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+                "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+                        + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+                        + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+                        + " FROM " + CE_TABLE_ACCOUNTS
+                        + " JOIN " + CE_TABLE_AUTHTOKENS
+                        + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+                        + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
+                        + " WHERE " + CE_TABLE_AUTHTOKENS + "."  + AUTHTOKENS_AUTHTOKEN
+                        + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
                 new String[]{authToken, accountType});
         try {
             while (cursor.moveToNext()) {
                 long authTokenId = cursor.getLong(0);
                 String accountName = cursor.getString(1);
                 String authTokenType = cursor.getString(2);
-                db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
+                db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
                 writeAuthTokenIntoCacheLocked(
                         accounts,
                         db,
@@ -1585,7 +1660,7 @@
             return;
         }
         cancelNotification(getSigninRequiredNotificationId(accounts, account),
-                new UserHandle(accounts.userId));
+                UserHandle.of(accounts.userId));
         synchronized (accounts.cacheLock) {
             accounts.accountTokenCaches.put(
                     account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
@@ -1598,23 +1673,23 @@
             return false;
         }
         cancelNotification(getSigninRequiredNotificationId(accounts, account),
-                new UserHandle(accounts.userId));
+                UserHandle.of(accounts.userId));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
             db.beginTransaction();
             try {
                 long accountId = getAccountIdLocked(db, account);
                 if (accountId < 0) {
                     return false;
                 }
-                db.delete(TABLE_AUTHTOKENS,
+                db.delete(CE_TABLE_AUTHTOKENS,
                         AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
                         new String[]{type});
                 ContentValues values = new ContentValues();
                 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
                 values.put(AUTHTOKENS_TYPE, type);
                 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
-                if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
+                if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
                     db.setTransactionSuccessful();
                     writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
                     return true;
@@ -1714,7 +1789,7 @@
             return;
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
             db.beginTransaction();
             try {
                 final ContentValues values = new ContentValues();
@@ -1722,8 +1797,8 @@
                 final long accountId = getAccountIdLocked(db, account);
                 if (accountId >= 0) {
                     final String[] argsAccountId = {String.valueOf(accountId)};
-                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
-                    db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
+                    db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+                    db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
                     accounts.authTokenCache.remove(account);
                     accounts.accountTokenCaches.remove(account);
                     db.setTransactionSuccessful();
@@ -1806,7 +1881,7 @@
             return;
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
             db.beginTransaction();
             try {
                 long accountId = getAccountIdLocked(db, account);
@@ -1822,7 +1897,7 @@
                 } else {
                     ContentValues values = new ContentValues();
                     values.put(EXTRAS_VALUE, value);
-                    if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+                    if (1 != db.update(CE_TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
                         return;
                     }
 
@@ -3307,7 +3382,6 @@
         long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
         final ContentValues values = new ContentValues();
         values.put(ACCOUNTS_NAME, newName);
-        values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
         int r = db.update(
                 TABLE_SHARED_ACCOUNTS,
                 values,
@@ -3347,7 +3421,7 @@
     public Account[] getSharedAccountsAsUser(int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        ArrayList<Account> accountList = new ArrayList<Account>();
+        ArrayList<Account> accountList = new ArrayList<>();
         Cursor cursor = null;
         try {
             cursor = accounts.openHelper.getReadableDatabase()
@@ -3488,7 +3562,7 @@
     }
 
     private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
-        Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
+        Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
                 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
                 new String[]{key}, null, null, null);
         try {
@@ -3729,8 +3803,8 @@
                         if (accountPresent) {
                             lastAuthenticatedTime = DatabaseUtils.longForQuery(
                                     mAccounts.openHelper.getReadableDatabase(),
-                                    "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
-                                            + " from " +
+                                    "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+                                            + " FROM " +
                                             TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
                                             + ACCOUNTS_TYPE + "=?",
                                     new String[] {
@@ -3859,7 +3933,7 @@
                 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
             }
             if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
-                    new UserHandle(mAccounts.userId))) {
+                    UserHandle.of(mAccounts.userId))) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
                 }
@@ -3893,15 +3967,16 @@
         }
     }
 
-    private static String getDatabaseName(int userId) {
+    static String getPreNDatabaseName(int userId) {
         File systemDir = Environment.getDataSystemDirectory();
-        File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
+        File databaseFile = new File(Environment.getUserSystemDirectory(userId),
+                PRE_N_DATABASE_NAME);
         if (userId == 0) {
             // Migrate old file, if it exists, to the new location.
             // Make sure the new file doesn't already exist. A dummy file could have been
             // accidentally created in the old location, causing the new one to become corrupted
             // as well.
-            File oldFile = new File(systemDir, DATABASE_NAME);
+            File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
             if (oldFile.exists() && !databaseFile.exists()) {
                 // Check for use directory; create if it doesn't exist, else renameTo will fail
                 File userDir = Environment.getUserSystemDirectory(userId);
@@ -3918,6 +3993,18 @@
         return databaseFile.getPath();
     }
 
+    static String getDeDatabaseName(int userId) {
+        File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
+                DE_DATABASE_NAME);
+        return databaseFile.getPath();
+    }
+
+    static String getCeDatabaseName(int userId) {
+        File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
+                CE_DATABASE_NAME);
+        return databaseFile.getPath();
+    }
+
     private static class DebugDbHelper{
         private DebugDbHelper() {
         }
@@ -4050,58 +4137,22 @@
         return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
     }
 
-    static class DatabaseHelper extends SQLiteOpenHelper {
+    static class PreNDatabaseHelper extends SQLiteOpenHelper {
 
         private final Context mContext;
         private final int mUserId;
 
-        public DatabaseHelper(Context context, int userId) {
-            super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
+        public PreNDatabaseHelper(Context context, int userId) {
+            super(context, AccountManagerService.getPreNDatabaseName(userId), null,
+                    PRE_N_DATABASE_VERSION);
             mContext = context;
             mUserId = userId;
         }
 
-        /**
-         * This call needs to be made while the mCacheLock is held. The way to
-         * ensure this is to get the lock any time a method is called ont the DatabaseHelper
-         * @param db The database.
-         */
         @Override
         public void onCreate(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
-                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
-                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
-                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
-                    + ACCOUNTS_PASSWORD + " TEXT, "
-                    + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
-                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
-                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
-
-            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
-                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
-                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
-                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
-                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
-                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
-
-            createGrantsTable(db);
-
-            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
-                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
-                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
-                    + EXTRAS_KEY + " TEXT NOT NULL, "
-                    + EXTRAS_VALUE + " TEXT, "
-                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
-
-            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
-                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
-                    + META_VALUE + " TEXT)");
-
-            createSharedAccountsTable(db);
-
-            createAccountsDeletionTrigger(db);
-
-            DebugDbHelper.createDebugTable(db);
+            // We use PreNDatabaseHelper only if pre-N db exists
+            throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
         }
 
         private void createSharedAccountsTable(SQLiteDatabase db) {
@@ -4161,6 +4212,9 @@
             }
         }
 
+        /**
+         * Pre-N database may need an upgrade before splitting
+         */
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
@@ -4222,6 +4276,315 @@
         }
     }
 
+    static class DeDatabaseHelper extends SQLiteOpenHelper {
+
+        private final int mUserId;
+        private volatile boolean mCeAttached;
+
+        private DeDatabaseHelper(Context context, int userId) {
+            super(context, getDeDatabaseName(userId), null, DE_DATABASE_VERSION);
+            mUserId = userId;
+        }
+
+        /**
+         * This call needs to be made while the mCacheLock is held. The way to
+         * ensure this is to get the lock any time a method is called ont the DatabaseHelper
+         * @param db The database.
+         */
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            Log.i(TAG, "Creating DE database for user " + mUserId);
+            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
+                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
+                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+                    + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
+                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
+                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
+                    + META_VALUE + " TEXT)");
+
+            createGrantsTable(db);
+            createSharedAccountsTable(db);
+            createAccountsDeletionTrigger(db);
+            DebugDbHelper.createDebugTable(db);
+        }
+
+        private void createSharedAccountsTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
+                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
+                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+        }
+
+        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+            db.execSQL(""
+                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+                    + " BEGIN"
+                    + "   DELETE FROM " + TABLE_GRANTS
+                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+                    + " END");
+        }
+
+        private void createGrantsTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
+                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
+                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
+                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
+                    +   "," + GRANTS_GRANTEE_UID + "))");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
+
+            if (oldVersion != newVersion) {
+                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+            }
+        }
+
+        public void attachCeDatabase() {
+            File ceDbFile = new File(getCeDatabaseName(mUserId));
+            SQLiteDatabase db = getWritableDatabase();
+            db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
+            mCeAttached = true;
+        }
+
+        public boolean isCeDatabaseAttached() {
+            return mCeAttached;
+        }
+
+
+        public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
+            if(!mCeAttached) {
+                Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user "
+                        + mUserId + " is still locked ", new Throwable());
+            }
+            return super.getReadableDatabase();
+        }
+
+        public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
+            if(!mCeAttached) {
+                Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
+                        + " is still locked ", new Throwable());
+            }
+            return super.getWritableDatabase();
+        }
+
+        @Override
+        public void onOpen(SQLiteDatabase db) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
+        }
+
+        private void migratePreNDbToDe(File preNDbFile) {
+            Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
+            SQLiteDatabase db = getWritableDatabase();
+            db.execSQL("ATTACH DATABASE '" +  preNDbFile.getPath() + "' AS preNDb");
+            db.beginTransaction();
+            // Copy accounts fields
+            db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
+                    + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
+                    + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+                    + ") "
+                    + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
+                    + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+                    + " FROM preNDb." + TABLE_ACCOUNTS);
+            // Copy SHARED_ACCOUNTS
+            db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
+                    + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
+                    "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
+                    + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
+            // Copy DEBUG_TABLE
+            db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
+                    + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
+                    + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
+                    + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
+                    "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
+                    + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
+                    + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
+                    + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
+            // Copy GRANTS
+            db.execSQL("INSERT INTO " + TABLE_GRANTS
+                    + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
+                    + GRANTS_GRANTEE_UID + ") " +
+                    "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
+                    + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
+            // Copy META
+            db.execSQL("INSERT INTO " + TABLE_META
+                    + "(" + META_KEY + "," + META_VALUE + ") "
+                    + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
+            db.setTransactionSuccessful();
+            db.endTransaction();
+
+            db.execSQL("DETACH DATABASE preNDb");
+        }
+
+        static DeDatabaseHelper create(Context context, int userId) {
+            File oldDb = new File(getPreNDatabaseName(userId));
+            File newDb = new File(getDeDatabaseName(userId));
+            boolean newDbExists = newDb.exists();
+            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId);
+            // If the db just created, and there is a legacy db, migrate it
+            if (!newDbExists && oldDb.exists()) {
+                // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
+                PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId);
+                // Open the database to force upgrade if required
+                preNDatabaseHelper.getWritableDatabase();
+                preNDatabaseHelper.close();
+                // Move data without SPII to DE
+                deDatabaseHelper.migratePreNDbToDe(oldDb);
+            }
+            return deDatabaseHelper;
+        }
+    }
+
+    static class CeDatabaseHelper extends SQLiteOpenHelper {
+
+        public CeDatabaseHelper(Context context, int userId) {
+            super(context, getCeDatabaseName(userId), null, CE_DATABASE_VERSION);
+        }
+
+        /**
+         * This call needs to be made while the mCacheLock is held.
+         * @param db The database.
+         */
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            Log.i(TAG, "Creating CE database " + getDatabaseName());
+            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
+                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+                    + ACCOUNTS_PASSWORD + " TEXT, "
+                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
+                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
+                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
+                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
+                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
+
+            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
+                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
+                    + EXTRAS_KEY + " TEXT NOT NULL, "
+                    + EXTRAS_VALUE + " TEXT, "
+                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
+
+            createAccountsDeletionTrigger(db);
+        }
+
+        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+            db.execSQL(""
+                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+                    + " BEGIN"
+                    + "   DELETE FROM " + TABLE_AUTHTOKENS
+                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+                    + "   DELETE FROM " + TABLE_EXTRAS
+                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+                    + " END");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
+
+            if (oldVersion == 9) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "onUpgrade upgrading to v10");
+                }
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
+                // Recreate the trigger, since the old one references the table to be removed
+                db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
+                createAccountsDeletionTrigger(db);
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
+                db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
+                oldVersion ++;
+            }
+
+            if (oldVersion != newVersion) {
+                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+            }
+        }
+
+        @Override
+        public void onOpen(SQLiteDatabase db) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
+        }
+
+        static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
+                String type) {
+            Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
+                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                    new String[]{name, type}, null, null, null);
+            try {
+                if (cursor.moveToNext()) {
+                    return cursor.getString(0);
+                }
+                return null;
+            } finally {
+                cursor.close();
+            }
+        }
+
+        /**
+         * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
+         * it also performs migration to the new CE database.
+         * @param context
+         * @param userId id of the user where the database is located
+         */
+        static CeDatabaseHelper create(Context context, int userId) {
+
+            File oldDatabaseFile = new File(getPreNDatabaseName(userId));
+            File ceDatabaseFile = new File(getCeDatabaseName(userId));
+            boolean newDbExists = ceDatabaseFile.exists();
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
+                        + oldDatabaseFile.exists() + " newDbExists=" + newDbExists);
+            }
+            boolean removeOldDb = false;
+            if (!newDbExists && oldDatabaseFile.exists()) {
+                removeOldDb = migratePreNDbToCe(oldDatabaseFile, ceDatabaseFile);
+            }
+            // Try to open and upgrade if necessary
+            CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, userId);
+            ceHelper.getWritableDatabase();
+            ceHelper.close();
+            if (removeOldDb) {
+                // TODO STOPSHIP - backup file during testing. Remove file before the release
+                Log.i(TAG, "Migration complete - creating backup of old db " + oldDatabaseFile);
+                renameToBakFile(oldDatabaseFile);
+            }
+            return ceHelper;
+        }
+
+        private static void renameToBakFile(File file) {
+            File bakFile = new File(file.getPath() + ".bak");
+            if (!file.renameTo(bakFile)) {
+                Log.e(TAG, "Cannot move file to " + bakFile);
+            }
+        }
+
+        private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
+            Log.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
+            try {
+                FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
+            } catch (IOException e) {
+                Log.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
+                // Try to remove potentially damaged file if I/O error occurred
+                deleteDbFileWarnIfFailed(ceDbFile);
+                return false;
+            }
+            return true;
+        }
+    }
+
     public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
         return asBinder();
     }
@@ -4666,7 +5029,7 @@
                 db.endTransaction();
             }
             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
-                    new UserHandle(accounts.userId));
+                    UserHandle.of(accounts.userId));
         }
     }
 
@@ -4891,7 +5254,7 @@
             HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
             if (authTokensForAccount == null) {
                 // need to populate the cache for this account
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
                 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
                 accounts.authTokenCache.put(account, authTokensForAccount);
             }
@@ -4904,7 +5267,7 @@
             HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
             if (userDataForAccount == null) {
                 // need to populate the cache for this account
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
                 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
                 accounts.userDataCache.put(account, userDataForAccount);
             }
@@ -4914,8 +5277,8 @@
 
     protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
             final SQLiteDatabase db, Account account) {
-        HashMap<String, String> userDataForAccount = new HashMap<String, String>();
-        Cursor cursor = db.query(TABLE_EXTRAS,
+        HashMap<String, String> userDataForAccount = new HashMap<>();
+        Cursor cursor = db.query(CE_TABLE_EXTRAS,
                 COLUMNS_EXTRAS_KEY_AND_VALUE,
                 SELECTION_USERDATA_BY_ACCOUNT,
                 new String[]{account.name, account.type},
@@ -4934,8 +5297,8 @@
 
     protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
             final SQLiteDatabase db, Account account) {
-        HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
-        Cursor cursor = db.query(TABLE_AUTHTOKENS,
+        HashMap<String, String> authTokensForAccount = new HashMap<>();
+        Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
                 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
                 SELECTION_AUTHTOKENS_BY_ACCOUNT,
                 new String[]{account.name, account.type},