Introduced AccountDb API replacing helpers

Refactored AccountsDb to provide unified interface for interacting with
CE/DE database.

Benefits of the new API:
- Better encapsulation - only relevant methods are exposed.
  Implementation details are hidden in private helper classes.
- Clients can now treat CE tables as if they were in  DE database, but
  only available when the user is unlocked.
- Opening database is now implicit - no more getReadableDatabase
  /getWritableDatabase calls. Clients only need to define transaction
  boundaries, when necessary.

Test: Refactoring
Bug: 30639520
Change-Id: I08b1fc95b7a633b036e05e8ef7e8ebd239187aa1
diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
index 63afccc..c3b7e15 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
@@ -142,9 +142,8 @@
         final AccountManagerService.UserAccounts accounts = mAccountManagerService
                 .getUserAccounts(userId);
         synchronized (accounts.cacheLock) {
-            SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-            List<Pair<String, Integer>> allAccountGrants = DeDatabaseHelper.findAllAccountGrants(
-                    db);
+            List<Pair<String, Integer>> allAccountGrants = accounts.accountsDb
+                    .findAllAccountGrants();
             if (allAccountGrants.isEmpty()) {
                 return null;
             }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 575018d..7802576 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -98,9 +98,6 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
-import com.android.server.accounts.AccountsDb.CeDatabaseHelper;
-import com.android.server.accounts.AccountsDb.DeDatabaseHelper;
-import com.android.server.accounts.AccountsDb.DebugDbHelper;
 
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
@@ -161,8 +158,6 @@
         }
     }
 
-    private static final int MAX_DEBUG_DB_SIZE = 64;
-
     final Context mContext;
 
     private final PackageManager mPackageManager;
@@ -193,7 +188,7 @@
 
     static class UserAccounts {
         private final int userId;
-        final DeDatabaseHelper openHelper;
+        final AccountsDb accountsDb;
         private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
                 credentialsPermissionNotificationIds =
                 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
@@ -242,12 +237,12 @@
                 new HashMap<Account, AtomicReference<String>>();
 
         private int debugDbInsertionPoint = -1;
-        private SQLiteStatement statementForLogging;
+        private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
 
         UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
             this.userId = userId;
             synchronized (cacheLock) {
-                openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
+                accountsDb = AccountsDb.create(context, userId, preNDbFile, deDbFile);
             }
         }
     }
@@ -993,7 +988,7 @@
             UserAccounts accounts, boolean invalidateAuthenticatorCache) {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "validateAccountsInternal " + accounts.userId
-                    + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
+                    + " isCeDatabaseAttached=" + accounts.accountsDb.isCeDatabaseAttached()
                     + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
         }
 
@@ -1006,11 +1001,11 @@
         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
 
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
             boolean accountDeleted = false;
 
             // Get a map of stored authenticator types to UID
-            Map<String, Integer> metaAuthUid = DeDatabaseHelper.findMetaAuthUid(db);
+            final AccountsDb accountsDb = accounts.accountsDb;
+            Map<String, Integer> metaAuthUid = accountsDb.findMetaAuthUid();
             // Create a list of authenticator type whose previous uid no longer exists
             HashSet<String> obsoleteAuthType = Sets.newHashSet();
             SparseBooleanArray knownUids = null;
@@ -1047,7 +1042,7 @@
                         // So purge its data from the account databases.
                         obsoleteAuthType.add(type);
                         // And delete it from the TABLE_META
-                        DeDatabaseHelper.deleteMetaByAuthTypeAndUid(db, type, uid);
+                        accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
                     }
                 }
             }
@@ -1056,11 +1051,10 @@
             // been re-enabled (after being updated for example), then we just overwrite the old
             // values.
             for (Entry<String, Integer> entry : knownAuth.entrySet()) {
-                DeDatabaseHelper.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
-                        entry.getValue());
+                accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
             }
 
-            final Map<Long, Account> accountsMap = DeDatabaseHelper.findAllAccounts(db);
+            final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
             try {
                 accounts.accountCache.clear();
                 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
@@ -1070,17 +1064,17 @@
                     if (obsoleteAuthType.contains(account.type)) {
                         Slog.w(TAG, "deleting account " + account.name + " because type "
                                 + account.type + "'s registered authenticator no longer exist.");
-                        db.beginTransaction();
+                        accountsDb.beginTransaction();
                         try {
-                            DeDatabaseHelper.deleteAccount(db, accountId);
+                            accountsDb.deleteDeAccount(accountId);
                             // Also delete from CE table if user is unlocked; if user is currently
                             // locked the account will be removed later by syncDeCeAccountsLocked
                             if (userUnlocked) {
-                                AccountsDb.deleteCeAccount(db, accountId);
+                                accountsDb.deleteCeAccount(accountId);
                             }
-                            db.setTransactionSuccessful();
+                            accountsDb.setTransactionSuccessful();
                         } finally {
-                            db.endTransaction();
+                            accountsDb.endTransaction();
                         }
                         accountDeleted = true;
 
@@ -1163,20 +1157,17 @@
                 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
                 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
                 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
-                initializeDebugDbSizeAndCompileSqlStatementForLogging(
-                        accounts.openHelper.getWritableDatabase(), accounts);
+                initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
                 mUsers.append(userId, accounts);
                 purgeOldGrants(accounts);
                 validateAccounts = true;
             }
             // open CE database if necessary
-            if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
+            if (!accounts.accountsDb.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
                 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
                 synchronized (accounts.cacheLock) {
-                    File preNDatabaseFile = new File(mInjector.getPreNDatabaseName(userId));
                     File ceDatabaseFile = new File(mInjector.getCeDatabaseName(userId));
-                    CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
-                    accounts.openHelper.attachCeDatabase(ceDatabaseFile);
+                    accounts.accountsDb.attachCeDatabase(ceDatabaseFile);
                 }
                 syncDeCeAccountsLocked(accounts);
             }
@@ -1189,8 +1180,7 @@
 
     private void syncDeCeAccountsLocked(UserAccounts accounts) {
         Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
-        final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-        List<Account> accountsToRemove = AccountsDb.findCeAccountsNotInDe(db);
+        List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
         if (!accountsToRemove.isEmpty()) {
             Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
                     + accounts.userId + " was locked. Removing accounts from CE tables");
@@ -1213,8 +1203,7 @@
 
     private void purgeOldGrants(UserAccounts accounts) {
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-            List<Integer> uids = DeDatabaseHelper.findAllUidGrants(db);
+            List<Integer> uids = accounts.accountsDb.findAllUidGrants();
             for (int uid : uids) {
                 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
                 if (packageExists) {
@@ -1222,7 +1211,7 @@
                 }
                 Log.d(TAG, "deleting grants for UID " + uid
                         + " because its package is no longer installed");
-                DeDatabaseHelper.deleteGrantsByUid(db, uid);
+                accounts.accountsDb.deleteGrantsByUid(uid);
             }
         }
     }
@@ -1241,7 +1230,7 @@
         }
         if (accounts != null) {
             synchronized (accounts.cacheLock) {
-                accounts.openHelper.close();
+                accounts.accountsDb.close();
             }
         }
         Log.i(TAG, "Removing database files for user " + userId);
@@ -1334,9 +1323,7 @@
         }
 
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-            return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
-                    account.type);
+            return accounts.accountsDb.findAccountPasswordByNameAndType(account.name, account.type);
         }
     }
 
@@ -1365,8 +1352,7 @@
         synchronized (accounts.cacheLock) {
             AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
             if (previousNameRef == null) {
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-                String previousName = DeDatabaseHelper.findAccountPreviousName(db, account);
+                String previousName = accounts.accountsDb.findDeAccountPreviousName(account);
                 previousNameRef = new AtomicReference<>(previousName);
                 accounts.previousNameCache.put(account, previousNameRef);
                 return previousName;
@@ -1602,8 +1588,7 @@
     private boolean updateLastAuthenticatedTime(Account account) {
         final UserAccounts accounts = getUserAccountsForCaller();
         synchronized (accounts.cacheLock) {
-            return DeDatabaseHelper.updateAccountLastAuthenticatedTime(
-                    accounts.openHelper.getWritableDatabase(), account);
+            return accounts.accountsDb.updateAccountLastAuthenticatedTime(account);
         }
     }
 
@@ -1672,22 +1657,21 @@
             return false;
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                if (CeDatabaseHelper.findAccountId(db, account) >= 0) {
+                if (accounts.accountsDb.findCeAccountId(account) >= 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping since the account already exists");
                     return false;
                 }
-                long accountId = CeDatabaseHelper.insertAccount(db, account, password);
+                long accountId = accounts.accountsDb.insertCeAccount(account, password);
                 if (accountId < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping the DB insert failed");
                     return false;
                 }
                 // Insert into DE table
-                if (DeDatabaseHelper.insertAccount(db, account, accountId) < 0) {
+                if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping the DB insert failed");
                     return false;
@@ -1695,21 +1679,21 @@
                 if (extras != null) {
                     for (String key : extras.keySet()) {
                         final String value = extras.getString(key);
-                        if (CeDatabaseHelper.insertExtra(db, accountId, key, value) < 0) {
+                        if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
                             Log.w(TAG, "insertAccountIntoDatabase: " + account
                                     + ", skipping since insertExtra failed for key " + key);
                             return false;
                         }
                     }
                 }
-                db.setTransactionSuccessful();
+                accounts.accountsDb.setTransactionSuccessful();
 
                 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
                         accountId, accounts, callingUid);
 
                 insertAccountIntoCacheLocked(accounts, account);
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
         }
         if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
@@ -1892,17 +1876,16 @@
             }
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             Account renamedAccount = new Account(newName, accountToRename.type);
             try {
-                final long accountId = DeDatabaseHelper.findAccountId(db, accountToRename);
+                final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
                 if (accountId >= 0) {
-                    CeDatabaseHelper.renameAccount(db, accountId, newName);
-                    DeDatabaseHelper.renameAccount(db, accountId, newName, accountToRename.name);
+                    accounts.accountsDb.renameCeAccount(accountId, newName);
+                    accounts.accountsDb.renameDeAccount(accountId, newName, accountToRename.name);
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
             /*
              * Database transaction was successful. Clean up cached
@@ -2025,8 +2008,7 @@
                 }
             }
         }
-        SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-        final long accountId = DeDatabaseHelper.findAccountId(db, account);
+        final long accountId = accounts.accountsDb.findDeAccountId(account);
         logRecord(
                 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
                 AccountsDb.TABLE_ACCOUNTS,
@@ -2065,8 +2047,7 @@
         }
         removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
         UserAccounts accounts = getUserAccountsForCaller();
-        SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-        final long accountId = DeDatabaseHelper.findAccountId(db, account);
+        final long accountId = accounts.accountsDb.findDeAccountId(account);
         logRecord(
                 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
                 AccountsDb.TABLE_ACCOUNTS,
@@ -2143,26 +2124,23 @@
                     + " is still locked. CE data will be removed later");
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = userUnlocked
-                    ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
-                    : accounts.openHelper.getWritableDatabase();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             // Set to a dummy value, this will only be used if the database
             // transaction succeeds.
             long accountId = -1;
             try {
-                accountId = DeDatabaseHelper.findAccountId(db, account);
+                accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    DeDatabaseHelper.deleteAccount(db, accountId);
+                    accounts.accountsDb.deleteDeAccount(accountId);
                     if (userUnlocked) {
                         // Delete from CE table
-                        AccountsDb.deleteCeAccount(db, accountId);
+                        accounts.accountsDb.deleteCeAccount(accountId);
                     }
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.setTransactionSuccessful();
                     isChanged = true;
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
             if (isChanged) {
                 removeAccountFromCacheLocked(accounts, account);
@@ -2221,14 +2199,13 @@
         try {
             UserAccounts accounts = getUserAccounts(userId);
             synchronized (accounts.cacheLock) {
-                final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-                db.beginTransaction();
+                accounts.accountsDb.beginTransaction();
                 try {
-                    invalidateAuthTokenLocked(accounts, db, accountType, authToken);
+                    invalidateAuthTokenLocked(accounts, accountType, authToken);
                     invalidateCustomTokenLocked(accounts, accountType, authToken);
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.setTransactionSuccessful();
                 } finally {
-                    db.endTransaction();
+                    accounts.accountsDb.endTransaction();
                 }
             }
         } finally {
@@ -2247,21 +2224,20 @@
         accounts.accountTokenCaches.remove(accountType, authToken);
     }
 
-    private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
-            String accountType, String authToken) {
+    private void invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
+            String authToken) {
         if (authToken == null || accountType == null) {
             return;
         }
-        Cursor cursor = CeDatabaseHelper.findAuthtokenForAllAccounts(db, accountType, authToken);
+        Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken);
         try {
             while (cursor.moveToNext()) {
                 String authTokenId = cursor.getString(0);
                 String accountName = cursor.getString(1);
                 String authTokenType = cursor.getString(2);
-                CeDatabaseHelper.deleteAuthToken(db, authTokenId);
+                accounts.accountsDb.deleteAuthToken(authTokenId);
                 writeAuthTokenIntoCacheLocked(
                         accounts,
-                        db,
                         new Account(accountName, accountType),
                         authTokenType,
                         null);
@@ -2299,22 +2275,21 @@
         cancelNotification(getSigninRequiredNotificationId(accounts, account),
                 UserHandle.of(accounts.userId));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                long accountId = DeDatabaseHelper.findAccountId(db, account);
+                long accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId < 0) {
                     return false;
                 }
-                CeDatabaseHelper.deleteAuthtokensByAccountIdAndType(db, accountId, type);
-                if (CeDatabaseHelper.insertAuthToken(db, accountId, type, authToken) >= 0) {
-                    db.setTransactionSuccessful();
-                    writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
+                accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
+                if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
+                    accounts.accountsDb.setTransactionSuccessful();
+                    writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
                     return true;
                 }
                 return false;
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
         }
     }
@@ -2413,16 +2388,15 @@
         }
         boolean isChanged = false;
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                final long accountId = DeDatabaseHelper.findAccountId(db, account);
+                final long accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    CeDatabaseHelper.updateAccountPassword(db, accountId, password);
-                    CeDatabaseHelper.deleteAuthTokensByAccountId(db, accountId);
+                    accounts.accountsDb.updateCeAccountPassword(accountId, password);
+                    accounts.accountsDb.deleteAuthTokensByAccountId(accountId);
                     accounts.authTokenCache.remove(account);
                     accounts.accountTokenCaches.remove(account);
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.setTransactionSuccessful();
                     // If there is an account whose password will be updated and the database
                     // transactions succeed, then we say that a change has occured. Even if the
                     // new password is the same as the old and there were no authtokens to delete.
@@ -2433,7 +2407,7 @@
                     logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, callingUid);
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
                 if (isChanged) {
                     // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
                     sendAccountsChangedBroadcast(accounts.userId);
@@ -2523,26 +2497,25 @@
         if (account == null || key == null) {
             return;
         }
-        final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        db.beginTransaction();
+        accounts.accountsDb.beginTransaction();
         try {
-            long accountId = DeDatabaseHelper.findAccountId(db, account);
+            long accountId = accounts.accountsDb.findDeAccountId(account);
             if (accountId < 0) {
                 return;
             }
-            long extrasId = CeDatabaseHelper.findExtrasIdByAccountId(db, accountId, key);
+            long extrasId = accounts.accountsDb.findExtrasIdByAccountId(accountId, key);
             if (extrasId < 0) {
-                extrasId = CeDatabaseHelper.insertExtra(db, accountId, key, value);
+                extrasId = accounts.accountsDb.insertExtra(accountId, key, value);
                 if (extrasId < 0) {
                     return;
                 }
-            } else if (!CeDatabaseHelper.updateExtra(db, extrasId, value)) {
+            } else if (!accounts.accountsDb.updateExtra(extrasId, value)) {
                 return;
             }
-            writeUserDataIntoCacheLocked(accounts, db, account, key, value);
-            db.setTransactionSuccessful();
+            writeUserDataIntoCacheLocked(accounts, account, key, value);
+            accounts.accountsDb.setTransactionSuccessful();
         } finally {
-            db.endTransaction();
+            accounts.accountsDb.endTransaction();
         }
     }
 
@@ -4171,9 +4144,8 @@
     private boolean addSharedAccountAsUser(Account account, int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        DeDatabaseHelper.deleteSharedAccount(db, account);
-        long accountId = DeDatabaseHelper.insertSharedAccount(db, account);
+        accounts.accountsDb.deleteSharedAccount(account);
+        long accountId = accounts.accountsDb.insertSharedAccount(account);
         if (accountId < 0) {
             Log.w(TAG, "insertAccountIntoDatabase: " + account
                     + ", skipping the DB insert failed");
@@ -4188,9 +4160,8 @@
     public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        long sharedTableAccountId = DeDatabaseHelper.findSharedAccountId(db, account);
-        int r = DeDatabaseHelper.renameSharedAccount(db, account, newName);
+        long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
+        int r = accounts.accountsDb.renameSharedAccount(account, newName);
         if (r > 0) {
             int callingUid = getCallingUid();
             logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS,
@@ -4209,9 +4180,8 @@
     private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        long sharedTableAccountId = DeDatabaseHelper.findSharedAccountId(db, account);
-        boolean deleted = DeDatabaseHelper.deleteSharedAccount(db, account);
+        long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
+        boolean deleted = accounts.accountsDb.deleteSharedAccount(account);
         if (deleted) {
             logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS,
                     sharedTableAccountId, accounts, callingUid);
@@ -4223,8 +4193,8 @@
     @Override
     public Account[] getSharedAccountsAsUser(int userId) {
         userId = handleIncomingUser(userId);
-        SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
-        List<Account> accountList = DeDatabaseHelper.getSharedAccounts(db);
+        UserAccounts accounts = getUserAccounts(userId);
+        List<Account> accountList = accounts.accountsDb.getSharedAccounts();
         Account[] accountArray = new Account[accountList.size()];
         accountList.toArray(accountArray);
         return accountArray;
@@ -4575,9 +4545,8 @@
                     if (mAuthDetailsRequired) {
                         long lastAuthenticatedTime = -1;
                         if (accountPresent) {
-                            lastAuthenticatedTime = DeDatabaseHelper
+                            lastAuthenticatedTime = mAccounts.accountsDb
                                     .findAccountLastAuthenticatedTime(
-                                            mAccounts.openHelper.getReadableDatabase(),
                                             new Account(mAccountName, mAccountType));
                         }
                         result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
@@ -4795,7 +4764,7 @@
         LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
                 callingUid, userAccount.debugDbInsertionPoint);
         userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
-                % MAX_DEBUG_DB_SIZE;
+                % AccountsDb.MAX_DEBUG_DB_SIZE;
         mHandler.post(logTask);
     }
 
@@ -4803,17 +4772,10 @@
      * This should only be called once to compile the sql statement for logging
      * and to find the insertion point.
      */
-    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
-            UserAccounts userAccount) {
-        // Initialize the count if not done earlier.
-        int size = DebugDbHelper.getDebugTableRowCount(db);
-        if (size >= MAX_DEBUG_DB_SIZE) {
-            // Table is full, and we need to find the point where to insert.
-            userAccount.debugDbInsertionPoint = DebugDbHelper.getDebugTableInsertionPoint(db);
-        } else {
-            userAccount.debugDbInsertionPoint = size;
-        }
-        userAccount.statementForLogging = DebugDbHelper.compileSqlStatementForLogging(db);
+    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
+        userAccount.debugDbInsertionPoint = userAccount.accountsDb
+                .calculateDebugTableInsertionPoint();
+        userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
     }
 
     public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
@@ -4862,11 +4824,9 @@
     private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
             String[] args, boolean isCheckinRequest) {
         synchronized (userAccounts.cacheLock) {
-            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
-
             if (isCheckinRequest) {
                 // This is a checkin request. *Only* upload the account types and the count of each.
-                DeDatabaseHelper.dumpAccountsTable(db, fout);
+                userAccounts.accountsDb.dumpDeAccountsTable(fout);
             } else {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
                         Process.myUid(), null);
@@ -4877,7 +4837,7 @@
 
                 // Add debug information.
                 fout.println();
-                DebugDbHelper.dumpDebugTable(db, fout);
+                userAccounts.accountsDb.dumpDebugTable(fout);
                 fout.println();
                 synchronized (mSessions) {
                     final long now = SystemClock.elapsedRealtime();
@@ -5139,13 +5099,12 @@
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
             long grantsCount;
             if (authTokenType != null) {
-                grantsCount = DeDatabaseHelper.findMatchingGrantsCount(db, callerUid, authTokenType,
+                grantsCount = accounts.accountsDb.findMatchingGrantsCount(callerUid, authTokenType,
                         account);
             } else {
-                grantsCount = DeDatabaseHelper.findMatchingGrantsCountAnyToken(db, callerUid,
+                grantsCount = accounts.accountsDb.findMatchingGrantsCountAnyToken(callerUid,
                         account);
             }
             final boolean permissionGranted = grantsCount > 0;
@@ -5273,10 +5232,9 @@
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-            long accountId = DeDatabaseHelper.findAccountId(db, account);
+            long accountId = accounts.accountsDb.findDeAccountId(account);
             if (accountId >= 0) {
-                DeDatabaseHelper.insertGrant(db, accountId, authTokenType, uid);
+                accounts.accountsDb.insertGrant(accountId, authTokenType, uid);
             }
             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
                     UserHandle.of(accounts.userId));
@@ -5306,17 +5264,16 @@
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                long accountId = DeDatabaseHelper.findAccountId(db, account);
+                long accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    DeDatabaseHelper.deleteGrantsByAccountIdAuthTokenTypeAndUid(
-                            db, accountId, authTokenType, uid);
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.deleteGrantsByAccountIdAuthTokenTypeAndUid(
+                            accountId, authTokenType, uid);
+                    accounts.accountsDb.setTransactionSuccessful();
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
 
             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
@@ -5472,11 +5429,11 @@
         }
     }
 
-    protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
+    protected void writeUserDataIntoCacheLocked(UserAccounts accounts,
             Account account, String key, String value) {
         Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
         if (userDataForAccount == null) {
-            userDataForAccount = CeDatabaseHelper.findUserExtrasForAccount(db, account);
+            userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
             accounts.userDataCache.put(account, userDataForAccount);
         }
         if (value == null) {
@@ -5498,11 +5455,11 @@
         }
     }
 
-    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
+    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
             Account account, String key, String value) {
         Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
         if (authTokensForAccount == null) {
-            authTokensForAccount = CeDatabaseHelper.findAuthTokensByAccount(db, account);
+            authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
             accounts.authTokenCache.put(account, authTokensForAccount);
         }
         if (value == null) {
@@ -5518,8 +5475,7 @@
             Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
             if (authTokensForAccount == null) {
                 // need to populate the cache for this account
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-                authTokensForAccount = CeDatabaseHelper.findAuthTokensByAccount(db, account);
+                authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
                 accounts.authTokenCache.put(account, authTokensForAccount);
             }
             return authTokensForAccount.get(authTokenType);
@@ -5531,8 +5487,7 @@
         Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
         if (userDataForAccount == null) {
             // need to populate the cache for this account
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-            userDataForAccount = CeDatabaseHelper.findUserExtrasForAccount(db, account);
+            userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
             accounts.userDataCache.put(account, userDataForAccount);
         }
         return userDataForAccount.get(key);
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 6ef521e..1adcf34 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -42,8 +42,13 @@
 
 /**
  * Persistence layer abstraction for accessing accounts_ce/accounts_de databases.
+ *
+ * <p>At first, CE database needs to be {@link #attachCeDatabase(File) attached to DE},
+ * in order for the tables to be available. All operations with CE database are done through the
+ * connection to the DE database, to which it is attached. This approach allows atomic
+ * transactions across two databases</p>
  */
-class AccountsDb {
+class AccountsDb implements AutoCloseable {
     private static final String TAG = "AccountsDb";
 
     private static final String DATABASE_NAME = "accounts.db";
@@ -128,6 +133,8 @@
     private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
     private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
 
+    static final int MAX_DEBUG_DB_SIZE = 64;
+
     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
 
@@ -169,7 +176,17 @@
     private static final String META_KEY_DELIMITER = ":";
     private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
 
-    static class CeDatabaseHelper extends SQLiteOpenHelper {
+    private final DeDatabaseHelper mDeDatabase;
+    private final Context mContext;
+    private final File mPreNDatabaseFile;
+
+    AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) {
+        mDeDatabase = deDatabase;
+        mContext = context;
+        mPreNDatabaseFile = preNDatabaseFile;
+    }
+
+    private static class CeDatabaseHelper extends SQLiteOpenHelper {
 
         CeDatabaseHelper(Context context, String ceDatabaseName) {
             super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
@@ -249,17 +266,16 @@
         /**
          * 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 userId id of the user where the database is located
          */
         static CeDatabaseHelper create(
                 Context context,
-                int userId,
                 File preNDatabaseFile,
                 File ceDatabaseFile) {
             boolean newDbExists = ceDatabaseFile.exists();
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
-                        + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
+                Log.v(TAG, "CeDatabaseHelper.create ceDatabaseFile=" + ceDatabaseFile
+                        + " oldDbExists=" + preNDatabaseFile.exists()
+                        + " newDbExists=" + newDbExists);
             }
             boolean removeOldDb = false;
             if (!newDbExists && preNDatabaseFile.exists()) {
@@ -290,187 +306,189 @@
             }
             return true;
         }
-
-        /**
-         * Returns information about auth tokens and their account for the specified query
-         * parameters.
-         * Output is in the format:
-         * <pre><code> | AUTHTOKEN_ID |  ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
-         */
-        static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
-                String authToken) {
-            return db.rawQuery(
-                    "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});
-        }
-
-        static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
-                String authtokenType) {
-            return db.delete(CE_TABLE_AUTHTOKENS,
-                    AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
-                    new String[]{String.valueOf(accountId), authtokenType}) > 0;
-        }
-
-        static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
-            return db.delete(
-                    CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
-                    new String[]{authTokenId}) > 0;
-        }
-
-        static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
-                String authToken) {
-            ContentValues values = new ContentValues();
-            values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
-            values.put(AUTHTOKENS_TYPE, authTokenType);
-            values.put(AUTHTOKENS_AUTHTOKEN, authToken);
-            return db.insert(
-                    CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
-        }
-
-        static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db,
-                Account account) {
-            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},
-                    null, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    final String type = cursor.getString(0);
-                    final String authToken = cursor.getString(1);
-                    authTokensForAccount.put(type, authToken);
-                }
-            } finally {
-                cursor.close();
-            }
-            return authTokensForAccount;
-        }
-
-        static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_PASSWORD, password);
-            return db.update(
-                    CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
-                    new String[]{String.valueOf(accountId)});
-        }
-
-        static boolean renameAccount(SQLiteDatabase db, long accountId, String newName) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, newName);
-            final String[] argsAccountId = {String.valueOf(accountId)};
-            return db.update(
-                    CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
-        }
-
-        static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
-            return db.delete(
-                    CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
-                    new String[]{String.valueOf(accountId)}) > 0;
-        }
-
-        static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
-            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 {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
-            ContentValues values = new ContentValues();
-            values.put(EXTRAS_VALUE, value);
-            int rows = db.update(
-                    TABLE_EXTRAS, values, EXTRAS_ID + "=?",
-                    new String[]{String.valueOf(extrasId)});
-            return rows == 1;
-        }
-
-        static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
-            ContentValues values = new ContentValues();
-            values.put(EXTRAS_KEY, key);
-            values.put(EXTRAS_ACCOUNTS_ID, accountId);
-            values.put(EXTRAS_VALUE, value);
-            return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
-        }
-
-        static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
-            Map<String, String> userExtrasForAccount = new HashMap<>();
-            Cursor cursor = db.query(CE_TABLE_EXTRAS,
-                    COLUMNS_EXTRAS_KEY_AND_VALUE,
-                    SELECTION_USERDATA_BY_ACCOUNT,
-                    new String[]{account.name, account.type},
-                    null, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    final String tmpkey = cursor.getString(0);
-                    final String value = cursor.getString(1);
-                    userExtrasForAccount.put(tmpkey, value);
-                }
-            } finally {
-                cursor.close();
-            }
-            return userExtrasForAccount;
-        }
-
-        static long findAccountId(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(
-                    CE_TABLE_ACCOUNTS, new String[]{
-                            ACCOUNTS_ID},
-                    "name=? AND type=?", new String[]{account.name, account.type}, null, null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        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();
-            }
-        }
-
-        static long insertAccount(SQLiteDatabase db, Account account, String password) {
-            ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, account.name);
-            values.put(ACCOUNTS_TYPE, account.type);
-            values.put(ACCOUNTS_PASSWORD, password);
-            return db.insert(
-                    CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
-        }
-
     }
 
+    /**
+     * Returns information about auth tokens and their account for the specified query
+     * parameters.
+     * Output is in the format:
+     * <pre><code> | AUTHTOKEN_ID |  ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
+     */
+    Cursor findAuthtokenForAllAccounts(String accountType, String authToken) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        return db.rawQuery(
+                "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});
+    }
+
+    Map<String, String> findAuthTokensByAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        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},
+                null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String type = cursor.getString(0);
+                final String authToken = cursor.getString(1);
+                authTokensForAccount.put(type, authToken);
+            }
+        } finally {
+            cursor.close();
+        }
+        return authTokensForAccount;
+    }
+
+    boolean deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        return db.delete(CE_TABLE_AUTHTOKENS,
+                AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+                new String[]{String.valueOf(accountId), authtokenType}) > 0;
+    }
+
+    boolean deleteAuthToken(String authTokenId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        return db.delete(
+                CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
+                new String[]{authTokenId}) > 0;
+    }
+
+    long insertAuthToken(long accountId, String authTokenType, String authToken) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
+        values.put(AUTHTOKENS_TYPE, authTokenType);
+        values.put(AUTHTOKENS_AUTHTOKEN, authToken);
+        return db.insert(
+                CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
+    }
+
+    int updateCeAccountPassword(long accountId, String password) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_PASSWORD, password);
+        return db.update(
+                CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
+                new String[] {String.valueOf(accountId)});
+    }
+
+    boolean renameCeAccount(long accountId, String newName) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, newName);
+        final String[] argsAccountId = {String.valueOf(accountId)};
+        return db.update(
+                CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
+    }
+
+    boolean deleteAuthTokensByAccountId(long accountId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
+                new String[] {String.valueOf(accountId)}) > 0;
+    }
+
+    long findExtrasIdByAccountId(long accountId, String key) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        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 {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    boolean updateExtra(long extrasId, String value) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(EXTRAS_VALUE, value);
+        int rows = db.update(
+                TABLE_EXTRAS, values, EXTRAS_ID + "=?",
+                new String[]{String.valueOf(extrasId)});
+        return rows == 1;
+    }
+
+    long insertExtra(long accountId, String key, String value) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(EXTRAS_KEY, key);
+        values.put(EXTRAS_ACCOUNTS_ID, accountId);
+        values.put(EXTRAS_VALUE, value);
+        return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
+    }
+
+    Map<String, String> findUserExtrasForAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        Map<String, String> userExtrasForAccount = new HashMap<>();
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(CE_TABLE_EXTRAS,
+                COLUMNS_EXTRAS_KEY_AND_VALUE,
+                SELECTION_USERDATA_BY_ACCOUNT,
+                selectionArgs,
+                null, null, null)) {
+            while (cursor.moveToNext()) {
+                final String tmpkey = cursor.getString(0);
+                final String value = cursor.getString(1);
+                userExtrasForAccount.put(tmpkey, value);
+            }
+        }
+        return userExtrasForAccount;
+    }
+
+    long findCeAccountId(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        String[] columns = { ACCOUNTS_ID };
+        String selection = "name=? AND type=?";
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(CE_TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        }
+    }
+
+    String findAccountPasswordByNameAndType(String name, String type) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        String selection = ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?";
+        String[] selectionArgs = {name, type};
+        String[] columns = {ACCOUNTS_PASSWORD};
+        try (Cursor cursor = db.query(CE_TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getString(0);
+            }
+            return null;
+        }
+    }
+
+    long insertCeAccount(Account account, String password) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, account.name);
+        values.put(ACCOUNTS_TYPE, account.type);
+        values.put(ACCOUNTS_PASSWORD, password);
+        return db.insert(
+                CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+    }
+
+
     static class DeDatabaseHelper extends SQLiteOpenHelper {
 
         private final int mUserId;
@@ -504,7 +522,7 @@
             createGrantsTable(db);
             createSharedAccountsTable(db);
             createAccountsDeletionTrigger(db);
-            DebugDbHelper.createDebugTable(db);
+            createDebugTable(db);
         }
 
         private void createSharedAccountsTable(SQLiteDatabase db) {
@@ -533,6 +551,18 @@
                     +   "," + GRANTS_GRANTEE_UID + "))");
         }
 
+        static void createDebugTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
+                    + ACCOUNTS_ID + " INTEGER,"
+                    + DEBUG_TABLE_ACTION_TYPE + " TEXT NOT NULL, "
+                    + DEBUG_TABLE_TIMESTAMP + " DATETIME,"
+                    + DEBUG_TABLE_CALLER_UID + " INTEGER NOT NULL,"
+                    + DEBUG_TABLE_TABLE_NAME + " TEXT NOT NULL,"
+                    + DEBUG_TABLE_KEY + " INTEGER PRIMARY KEY)");
+            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " ("
+                    + DEBUG_TABLE_TIMESTAMP + ")");
+        }
+
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
@@ -542,17 +572,6 @@
             }
         }
 
-        public void attachCeDatabase(File ceDbFile) {
-            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
@@ -616,343 +635,305 @@
 
             db.execSQL("DETACH DATABASE preNDb");
         }
+    }
 
-        static boolean deleteAccount(SQLiteDatabase db, long accountId) {
-            return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
-        }
+    boolean deleteDeAccount(long accountId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
+    }
 
-        static long insertSharedAccount(SQLiteDatabase db, Account account) {
-            ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, account.name);
-            values.put(ACCOUNTS_TYPE, account.type);
-            return db.insert(
-                    TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
-        }
+    long insertSharedAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, account.name);
+        values.put(ACCOUNTS_TYPE, account.type);
+        return db.insert(
+                TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
+    }
 
-        static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
-            return db
-                    .delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
-                            new String[]{account.name, account.type}) > 0;
-        }
+    boolean deleteSharedAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                new String[]{account.name, account.type}) > 0;
+    }
 
-        static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, newName);
-            return db.update(TABLE_SHARED_ACCOUNTS,
-                    values,
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
-                            + "=?",
-                    new String[]{account.name, account.type});
-        }
+    int renameSharedAccount(Account account, String newName) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, newName);
+        return db.update(TABLE_SHARED_ACCOUNTS,
+                values,
+                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                new String[] {account.name, account.type});
+    }
 
-        static List<Account> getSharedAccounts(SQLiteDatabase db) {
-            ArrayList<Account> accountList = new ArrayList<>();
-            Cursor cursor = null;
-            try {
-                cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{
-                                ACCOUNTS_NAME, ACCOUNTS_TYPE},
-                        null, null, null, null, null);
-                if (cursor != null && cursor.moveToFirst()) {
-                    int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
-                    int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
-                    do {
-                        accountList.add(new Account(cursor.getString(nameIndex),
-                                cursor.getString(typeIndex)));
-                    } while (cursor.moveToNext());
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-            return accountList;
-        }
-
-        static long findSharedAccountId(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{
-                            ACCOUNTS_ID},
-                    "name=? AND type=?", new String[]{account.name, account.type}, null, null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static long findAccountLastAuthenticatedTime(SQLiteDatabase db, Account account) {
-            return DatabaseUtils.longForQuery(
-                    db,
-                    "SELECT " + AccountsDb.ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
-                            + " FROM " +
-                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
-                            + ACCOUNTS_TYPE + "=?",
-                    new String[] {account.name, account.type});
-        }
-
-        static boolean updateAccountLastAuthenticatedTime(SQLiteDatabase db, Account account) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
-            int rowCount = db.update(
-                    TABLE_ACCOUNTS,
-                    values,
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
-                    new String[] {
-                            account.name, account.type
-                    });
-            return rowCount > 0;
-        }
-
-
-        static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
-            Cursor cursor = db.query(
-                    TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
-                    null, null, ACCOUNTS_TYPE, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    // print type,count
-                    pw.println(cursor.getString(0) + "," + cursor.getString(1));
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-
-        static long findAccountId(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(
-                    TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
-                    "name=? AND type=?", new String[]{account.name, account.type}, null, null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
-            LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
-            Cursor cursor = db.query(TABLE_ACCOUNTS,
-                    new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
-                    null, null, null, null, ACCOUNTS_ID);
-            try {
-                while (cursor.moveToNext()) {
-                    final long accountId = cursor.getLong(0);
-                    final String accountType = cursor.getString(1);
-                    final String accountName = cursor.getString(2);
-
-                    final Account account = new Account(accountName, accountType);
-                    map.put(accountId, account);
-                }
-            } finally {
-                cursor.close();
-            }
-            return map;
-        }
-
-        static String findAccountPreviousName(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(
-                    TABLE_ACCOUNTS,
-                    new String[]{ACCOUNTS_PREVIOUS_NAME},
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
-                            + "=?",
-                    new String[]{account.name, account.type},
-                    null,
-                    null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getString(0);
-                }
-            } finally {
-                cursor.close();
-            }
-            return null;
-        }
-
-        static long insertAccount(SQLiteDatabase db, Account account, long accountId) {
-            ContentValues 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());
-            return db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
-        }
-
-        static boolean renameAccount(SQLiteDatabase db, long accountId, String newName,
-                String previousName) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, newName);
-            values.put(ACCOUNTS_PREVIOUS_NAME, previousName);
-            final String[] argsAccountId = {String.valueOf(accountId)};
-            return db.update(
-                    TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
-        }
-
-        static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId,
-                String authTokenType, long uid) {
-            return db.delete(TABLE_GRANTS,
-                    GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
-                            + GRANTS_GRANTEE_UID + "=?",
-                    new String[]{String.valueOf(accountId), authTokenType, String.valueOf(uid)})
-                    > 0;
-        }
-
-        static List<Integer> findAllUidGrants(SQLiteDatabase db) {
-            List<Integer> result = new ArrayList<>();
-            final Cursor cursor = db.query(TABLE_GRANTS,
-                    new String[]{GRANTS_GRANTEE_UID},
-                    null, null, GRANTS_GRANTEE_UID, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    final int uid = cursor.getInt(0);
-                    result.add(uid);
-                }
-            } finally {
-                cursor.close();
-            }
-            return result;
-        }
-
-        static long findMatchingGrantsCount(SQLiteDatabase db,
-                int uid, String authTokenType, Account account) {
-            String[] args = {String.valueOf(uid), authTokenType,
-                    account.name, account.type};
-            return DatabaseUtils
-                    .longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
-        }
-
-        static long findMatchingGrantsCountAnyToken(SQLiteDatabase db,
-                int uid, Account account) {
-            String[] args = {String.valueOf(uid), account.name, account.type};
-            return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS_ANY_TOKEN, args);
-        }
-
-        static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
-            ContentValues values = new ContentValues();
-            values.put(GRANTS_ACCOUNTS_ID, accountId);
-            values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
-            values.put(GRANTS_GRANTEE_UID, uid);
-            return db.insert(
-                    TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
-        }
-
-        static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
-            return db.delete(
-                    TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
-                    new String[]{Integer.toString(uid)}) > 0;
-        }
-
-        static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
-            ContentValues values = new ContentValues();
-            values.put(META_KEY,
-                    META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
-            values.put(META_VALUE, uid);
-            return db.insert(TABLE_META, null, values);
-        }
-
-        static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
-                int uid) {
-            ContentValues values = new ContentValues();
-            values.put(META_KEY,
-                    META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
-            values.put(META_VALUE, uid);
-            return db.insertWithOnConflict(TABLE_META, null, values,
-                    SQLiteDatabase.CONFLICT_REPLACE);
-        }
-
-        static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
-            Cursor metaCursor = db.query(
-                    TABLE_META,
-                    new String[]{META_KEY, META_VALUE},
-                    SELECTION_META_BY_AUTHENTICATOR_TYPE,
-                    new String[]{META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
-                    null /* groupBy */,
-                    null /* having */,
-                    META_KEY);
-            Map<String, Integer> map = new LinkedHashMap<>();
-            try {
-                while (metaCursor.moveToNext()) {
-                    String type = TextUtils
-                            .split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
-                    String uidStr = metaCursor.getString(1);
-                    if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
-                        // Should never happen.
-                        Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
-                                + ", uid empty: " + TextUtils.isEmpty(uidStr));
-                        continue;
-                    }
-                    int uid = Integer.parseInt(metaCursor.getString(1));
-                    map.put(type, uid);
-                }
-            } finally {
-                metaCursor.close();
-            }
-            return map;
-        }
-
-        static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
-            return db.delete(
-                    TABLE_META,
-                    META_KEY + "=? AND " + META_VALUE + "=?",
-                    new String[]{
-                            META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
-                            String.valueOf(uid)}
-            ) > 0;
-        }
-
-        static List<Pair<String, Integer>> findAllAccountGrants(SQLiteDatabase db) {
-            try (Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null)) {
-                if (cursor == null || !cursor.moveToFirst()) {
-                    return Collections.emptyList();
-                }
-                List<Pair<String, Integer>> results = new ArrayList<>();
+    List<Account> getSharedAccounts() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        ArrayList<Account> accountList = new ArrayList<>();
+        Cursor cursor = null;
+        try {
+            cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
+                    null, null, null, null, null);
+            if (cursor != null && cursor.moveToFirst()) {
+                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
+                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
                 do {
-                    final String accountName = cursor.getString(0);
-                    final int uid = cursor.getInt(1);
-                    results.add(Pair.create(accountName, uid));
+                    accountList.add(new Account(cursor.getString(nameIndex),
+                            cursor.getString(typeIndex)));
                 } while (cursor.moveToNext());
-                return results;
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
             }
         }
+        return accountList;
+    }
 
-        static DeDatabaseHelper create(
-                Context context,
-                int userId,
-                File preNDatabaseFile,
-                File deDatabaseFile) {
-            boolean newDbExists = deDatabaseFile.exists();
-            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
-                    deDatabaseFile.getPath());
-            // If the db just created, and there is a legacy db, migrate it
-            if (!newDbExists && preNDatabaseFile.exists()) {
-                // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
-                PreNDatabaseHelper
-                        preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
-                        preNDatabaseFile.getPath());
-                // Open the database to force upgrade if required
-                preNDatabaseHelper.getWritableDatabase();
-                preNDatabaseHelper.close();
-                // Move data without SPII to DE
-                deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
+    long findSharedAccountId(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{
+                        ACCOUNTS_ID},
+                "name=? AND type=?", new String[]{account.name, account.type}, null, null,
+                null);
+        try {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
             }
-            return deDatabaseHelper;
+            return -1;
+        } finally {
+            cursor.close();
         }
     }
 
-    static class PreNDatabaseHelper extends SQLiteOpenHelper {
+    long findAccountLastAuthenticatedTime(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        return DatabaseUtils.longForQuery(db,
+                "SELECT " + AccountsDb.ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+                        + " FROM " + TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+                        + ACCOUNTS_TYPE + "=?",
+                new String[] {account.name, account.type});
+    }
+
+    boolean updateAccountLastAuthenticatedTime(Account account) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
+        int rowCount = db.update(TABLE_ACCOUNTS,
+                values,
+                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                new String[] { account.name, account.type });
+        return rowCount > 0;
+    }
+
+    void dumpDeAccountsTable(PrintWriter pw) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor cursor = db.query(
+                TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
+                null, null, ACCOUNTS_TYPE, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                // print type,count
+                pw.println(cursor.getString(0) + "," + cursor.getString(1));
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    long findDeAccountId(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] columns = {ACCOUNTS_ID};
+        String selection = "name=? AND type=?";
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        }
+    }
+
+    Map<Long, Account> findAllDeAccounts() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
+        String[] columns = {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME};
+        try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns,
+                null, null, null, null, ACCOUNTS_ID)) {
+            while (cursor.moveToNext()) {
+                final long accountId = cursor.getLong(0);
+                final String accountType = cursor.getString(1);
+                final String accountName = cursor.getString(2);
+
+                final Account account = new Account(accountName, accountType);
+                map.put(accountId, account);
+            }
+        }
+        return map;
+    }
+
+    String findDeAccountPreviousName(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] columns = {ACCOUNTS_PREVIOUS_NAME};
+        String selection = ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?";
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getString(0);
+            }
+        }
+        return null;
+    }
+
+    long insertDeAccount(Account account, long accountId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues 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());
+        return db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+    }
+
+    boolean renameDeAccount(long accountId, String newName, String previousName) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, newName);
+        values.put(ACCOUNTS_PREVIOUS_NAME, previousName);
+        final String[] argsAccountId = {String.valueOf(accountId)};
+        return db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
+    }
+
+    boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(long accountId,
+            String authTokenType, long uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_GRANTS,
+                GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
+                        + GRANTS_GRANTEE_UID + "=?",
+                new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
+    }
+
+    List<Integer> findAllUidGrants() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        List<Integer> result = new ArrayList<>();
+        final Cursor cursor = db.query(TABLE_GRANTS,
+                new String[]{GRANTS_GRANTEE_UID},
+                null, null, GRANTS_GRANTEE_UID, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final int uid = cursor.getInt(0);
+                result.add(uid);
+            }
+        } finally {
+            cursor.close();
+        }
+        return result;
+    }
+
+    long findMatchingGrantsCount(int uid, String authTokenType, Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] args = {String.valueOf(uid), authTokenType, account.name, account.type};
+        return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
+    }
+
+    long findMatchingGrantsCountAnyToken(int uid, Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] args = {String.valueOf(uid), account.name, account.type};
+        return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS_ANY_TOKEN, args);
+    }
+
+    long insertGrant(long accountId, String authTokenType, int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(GRANTS_ACCOUNTS_ID, accountId);
+        values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
+        values.put(GRANTS_GRANTEE_UID, uid);
+        return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
+    }
+
+    boolean deleteGrantsByUid(int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
+                new String[] {Integer.toString(uid)}) > 0;
+    }
+
+    long insertOrReplaceMetaAuthTypeAndUid(String authenticatorType, int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(META_KEY,
+                META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
+        values.put(META_VALUE, uid);
+        return db.insertWithOnConflict(TABLE_META, null, values,
+                SQLiteDatabase.CONFLICT_REPLACE);
+    }
+
+    Map<String, Integer> findMetaAuthUid() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor metaCursor = db.query(
+                TABLE_META,
+                new String[]{META_KEY, META_VALUE},
+                SELECTION_META_BY_AUTHENTICATOR_TYPE,
+                new String[]{META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
+                null /* groupBy */,
+                null /* having */,
+                META_KEY);
+        Map<String, Integer> map = new LinkedHashMap<>();
+        try {
+            while (metaCursor.moveToNext()) {
+                String type = TextUtils
+                        .split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
+                String uidStr = metaCursor.getString(1);
+                if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
+                    // Should never happen.
+                    Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
+                            + ", uid empty: " + TextUtils.isEmpty(uidStr));
+                    continue;
+                }
+                int uid = Integer.parseInt(metaCursor.getString(1));
+                map.put(type, uid);
+            }
+        } finally {
+            metaCursor.close();
+        }
+        return map;
+    }
+
+    boolean deleteMetaByAuthTypeAndUid(String type, int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(
+                TABLE_META,
+                META_KEY + "=? AND " + META_VALUE + "=?",
+                new String[]{
+                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
+                        String.valueOf(uid)}
+        ) > 0;
+    }
+
+    List<Pair<String, Integer>> findAllAccountGrants() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        try (Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null)) {
+            if (cursor == null || !cursor.moveToFirst()) {
+                return Collections.emptyList();
+            }
+            List<Pair<String, Integer>> results = new ArrayList<>();
+            do {
+                final String accountName = cursor.getString(0);
+                final int uid = cursor.getInt(1);
+                results.add(Pair.create(accountName, uid));
+            } while (cursor.moveToNext());
+            return results;
+        }
+    }
+
+    private static class PreNDatabaseHelper extends SQLiteOpenHelper {
         private final Context mContext;
         private final int mUserId;
 
-        public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
+        PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
             super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
             mContext = context;
             mUserId = userId;
@@ -982,7 +963,7 @@
         }
 
         private void addDebugTable(SQLiteDatabase db) {
-            DebugDbHelper.createDebugTable(db);
+            DeDatabaseHelper.createDebugTable(db);
         }
 
         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
@@ -1007,10 +988,18 @@
                     +   "," + GRANTS_GRANTEE_UID + "))");
         }
 
+        static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
+            ContentValues values = new ContentValues();
+            values.put(META_KEY,
+                    META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
+            values.put(META_VALUE, uid);
+            return db.insert(TABLE_META, null, values);
+        }
+
         private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
                 Map<String, Integer> authTypeAndUIDMap) {
             for (Map.Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
-                DeDatabaseHelper.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
+                insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
             }
         }
 
@@ -1078,68 +1067,8 @@
         }
     }
 
-    static class DebugDbHelper{
-        private DebugDbHelper() {
-        }
-
-
-        private static void createDebugTable(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
-                    + ACCOUNTS_ID + " INTEGER,"
-                    + DEBUG_TABLE_ACTION_TYPE + " TEXT NOT NULL, "
-                    + DEBUG_TABLE_TIMESTAMP + " DATETIME,"
-                    + DEBUG_TABLE_CALLER_UID + " INTEGER NOT NULL,"
-                    + DEBUG_TABLE_TABLE_NAME + " TEXT NOT NULL,"
-                    + DEBUG_TABLE_KEY + " INTEGER PRIMARY KEY)");
-            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " ("
-                    + DEBUG_TABLE_TIMESTAMP + ")");
-        }
-
-        static SQLiteStatement compileSqlStatementForLogging(SQLiteDatabase db) {
-            String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
-                    + " VALUES (?,?,?,?,?,?)";
-            return db.compileStatement(sql);
-        }
-
-        static int getDebugTableRowCount(SQLiteDatabase db) {
-            String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
-            return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
-        }
-
-        /*
-         * Finds the row key where the next insertion should take place. This should
-         * be invoked only if the table has reached its full capacity.
-         */
-        static int getDebugTableInsertionPoint(SQLiteDatabase db) {
-            // This query finds the smallest timestamp value (and if 2 records have
-            // same timestamp, the choose the lower id).
-            String queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY +
-                    " FROM " + TABLE_DEBUG +
-                    " ORDER BY "  + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY +
-                    " LIMIT 1";
-            return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
-        }
-
-        static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
-            Cursor cursor = db.query(TABLE_DEBUG, null,
-                    null, null, null, null, DEBUG_TABLE_TIMESTAMP);
-            pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
-            pw.println("Accounts History");
-            try {
-                while (cursor.moveToNext()) {
-                    // print type,count
-                    pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
-                            cursor.getString(2) + "," + cursor.getString(3) + ","
-                            + cursor.getString(4) + "," + cursor.getString(5));
-                }
-            } finally {
-                cursor.close();
-            }
-        }
-
-    }
-
-    static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
+    List<Account> findCeAccountsNotInDe() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
         // Select accounts from CE that do not exist in DE
         Cursor cursor = db.rawQuery(
                 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
@@ -1161,14 +1090,110 @@
         }
     }
 
-    static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
+    boolean deleteCeAccount(long accountId) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
         return db.delete(
                 CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
     }
 
+    boolean isCeDatabaseAttached() {
+        return mDeDatabase.mCeAttached;
+    }
+
+    void beginTransaction() {
+        mDeDatabase.getWritableDatabase().beginTransaction();
+    }
+
+    void setTransactionSuccessful() {
+        mDeDatabase.getWritableDatabase().setTransactionSuccessful();
+    }
+
+    void endTransaction() {
+        mDeDatabase.getWritableDatabase().endTransaction();
+    }
+
+    void attachCeDatabase(File ceDbFile) {
+        CeDatabaseHelper.create(mContext, mPreNDatabaseFile, ceDbFile);
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
+        mDeDatabase.mCeAttached = true;
+    }
+
+    /*
+     * Finds the row key where the next insertion should take place. Returns number of rows
+     * if it is less {@link #MAX_DEBUG_DB_SIZE}, otherwise finds the lowest number available.
+     */
+    int calculateDebugTableInsertionPoint() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
+        int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+        if (size < MAX_DEBUG_DB_SIZE) {
+            return size;
+        }
+
+        // This query finds the smallest timestamp value (and if 2 records have
+        // same timestamp, the choose the lower id).
+        queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY +
+                " FROM " + TABLE_DEBUG +
+                " ORDER BY "  + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY +
+                " LIMIT 1";
+        return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+    }
+
+    SQLiteStatement compileSqlStatementForLogging() {
+        // TODO b/31708085 Fix debug logging - it eagerly opens database for write without a need
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
+                + " VALUES (?,?,?,?,?,?)";
+        return db.compileStatement(sql);
+    }
+
+    void dumpDebugTable(PrintWriter pw) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor cursor = db.query(TABLE_DEBUG, null,
+                null, null, null, null, DEBUG_TABLE_TIMESTAMP);
+        pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
+        pw.println("Accounts History");
+        try {
+            while (cursor.moveToNext()) {
+                // print type,count
+                pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
+                        cursor.getString(2) + "," + cursor.getString(3) + ","
+                        + cursor.getString(4) + "," + cursor.getString(5));
+            }
+        } finally {
+            cursor.close();
+        }
+    }
+
+    public void close() {
+        mDeDatabase.close();
+    }
+
     static void deleteDbFileWarnIfFailed(File dbFile) {
         if (!SQLiteDatabase.deleteDatabase(dbFile)) {
             Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
         }
     }
+
+    public static AccountsDb create(Context context, int userId, File preNDatabaseFile,
+            File deDatabaseFile) {
+        boolean newDbExists = deDatabaseFile.exists();
+        DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
+                deDatabaseFile.getPath());
+        // If the db just created, and there is a legacy db, migrate it
+        if (!newDbExists && preNDatabaseFile.exists()) {
+            // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
+            PreNDatabaseHelper
+                    preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
+                    preNDatabaseFile.getPath());
+            // Open the database to force upgrade if required
+            preNDatabaseHelper.getWritableDatabase();
+            preNDatabaseHelper.close();
+            // Move data without SPII to DE
+            deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
+        }
+        return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
+    }
+
 }