1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.accounts; 18 19 import android.accounts.Account; 20 import android.annotation.Nullable; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.database.DatabaseUtils; 25 import android.database.sqlite.SQLiteDatabase; 26 import android.database.sqlite.SQLiteException; 27 import android.database.sqlite.SQLiteOpenHelper; 28 import android.database.sqlite.SQLiteStatement; 29 import android.os.FileUtils; 30 import android.text.TextUtils; 31 import android.util.Log; 32 import android.util.Pair; 33 import android.util.Slog; 34 35 import java.io.File; 36 import java.io.IOException; 37 import java.io.PrintWriter; 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.HashMap; 41 import java.util.LinkedHashMap; 42 import java.util.List; 43 import java.util.Map; 44 45 /** 46 * Persistence layer abstraction for accessing accounts_ce/accounts_de databases. 47 * 48 * <p>At first, CE database needs to be {@link #attachCeDatabase(File) attached to DE}, 49 * in order for the tables to be available. All operations with CE database are done through the 50 * connection to the DE database, to which it is attached. This approach allows atomic 51 * transactions across two databases</p> 52 */ 53 class AccountsDb implements AutoCloseable { 54 private static final String TAG = "AccountsDb"; 55 56 private static final String DATABASE_NAME = "accounts.db"; 57 private static final int PRE_N_DATABASE_VERSION = 9; 58 private static final int CE_DATABASE_VERSION = 10; 59 private static final int DE_DATABASE_VERSION = 3; // Added visibility support in O 60 61 static final String TABLE_ACCOUNTS = "accounts"; 62 private static final String ACCOUNTS_ID = "_id"; 63 private static final String ACCOUNTS_NAME = "name"; 64 private static final String ACCOUNTS_TYPE = "type"; 65 private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; 66 private static final String ACCOUNTS_PASSWORD = "password"; 67 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name"; 68 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS = 69 "last_password_entry_time_millis_epoch"; 70 71 private static final String TABLE_AUTHTOKENS = "authtokens"; 72 private static final String AUTHTOKENS_ID = "_id"; 73 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; 74 private static final String AUTHTOKENS_TYPE = "type"; 75 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; 76 77 private static final String TABLE_VISIBILITY = "visibility"; 78 private static final String VISIBILITY_ACCOUNTS_ID = "accounts_id"; 79 private static final String VISIBILITY_PACKAGE = "_package"; 80 private static final String VISIBILITY_VALUE = "value"; 81 82 private static final String TABLE_GRANTS = "grants"; 83 private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; 84 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; 85 private static final String GRANTS_GRANTEE_UID = "uid"; 86 87 private static final String TABLE_EXTRAS = "extras"; 88 private static final String EXTRAS_ID = "_id"; 89 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; 90 private static final String EXTRAS_KEY = "key"; 91 private static final String EXTRAS_VALUE = "value"; 92 93 private static final String TABLE_META = "meta"; 94 private static final String META_KEY = "key"; 95 private static final String META_VALUE = "value"; 96 97 static final String TABLE_SHARED_ACCOUNTS = "shared_accounts"; 98 private static final String SHARED_ACCOUNTS_ID = "_id"; 99 100 private static String TABLE_DEBUG = "debug_table"; 101 102 // Columns for debug_table table 103 private static String DEBUG_TABLE_ACTION_TYPE = "action_type"; 104 private static String DEBUG_TABLE_TIMESTAMP = "time"; 105 private static String DEBUG_TABLE_CALLER_UID = "caller_uid"; 106 private static String DEBUG_TABLE_TABLE_NAME = "table_name"; 107 private static String DEBUG_TABLE_KEY = "primary_key"; 108 109 // These actions correspond to the occurrence of real actions. Since 110 // these are called by the authenticators, the uid associated will be 111 // of the authenticator. 112 static String DEBUG_ACTION_SET_PASSWORD = "action_set_password"; 113 static String DEBUG_ACTION_CLEAR_PASSWORD = "action_clear_password"; 114 static String DEBUG_ACTION_ACCOUNT_ADD = "action_account_add"; 115 static String DEBUG_ACTION_ACCOUNT_REMOVE = "action_account_remove"; 116 static String DEBUG_ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de"; 117 static String DEBUG_ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove"; 118 static String DEBUG_ACTION_ACCOUNT_RENAME = "action_account_rename"; 119 120 // These actions don't necessarily correspond to any action on 121 // accountDb taking place. As an example, there might be a request for 122 // addingAccount, which might not lead to addition of account on grounds 123 // of bad authentication. We will still be logging it to keep track of 124 // who called. 125 static String DEBUG_ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add"; 126 static String DEBUG_ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove"; 127 static String DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts"; 128 129 //This action doesn't add account to accountdb. Account is only 130 // added in finishSession which may be in a different user profile. 131 static String DEBUG_ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add"; 132 static String DEBUG_ACTION_CALLED_ACCOUNT_SESSION_FINISH = 133 "action_called_account_session_finish"; 134 135 static final String CE_DATABASE_NAME = "accounts_ce.db"; 136 static final String DE_DATABASE_NAME = "accounts_de.db"; 137 private static final String CE_DB_PREFIX = "ceDb."; 138 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS; 139 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS; 140 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS; 141 142 static final int MAX_DEBUG_DB_SIZE = 64; 143 144 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = 145 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; 146 147 private static final String COUNT_OF_MATCHING_GRANTS = "" 148 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 149 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 150 + " AND " + GRANTS_GRANTEE_UID + "=?" 151 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" 152 + " AND " + ACCOUNTS_NAME + "=?" 153 + " AND " + ACCOUNTS_TYPE + "=?"; 154 155 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = "" 156 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 157 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 158 + " AND " + GRANTS_GRANTEE_UID + "=?" 159 + " AND " + ACCOUNTS_NAME + "=?" 160 + " AND " + ACCOUNTS_TYPE + "=?"; 161 162 private static final String SELECTION_ACCOUNTS_ID_BY_ACCOUNT = 163 "accounts_id=(select _id FROM accounts WHERE name=? AND type=?)"; 164 165 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = 166 {AUTHTOKENS_TYPE, AUTHTOKENS_AUTHTOKEN}; 167 168 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; 169 170 private static final String ACCOUNT_ACCESS_GRANTS = "" 171 + "SELECT " + AccountsDb.ACCOUNTS_NAME + ", " 172 + AccountsDb.GRANTS_GRANTEE_UID 173 + " FROM " + AccountsDb.TABLE_ACCOUNTS 174 + ", " + AccountsDb.TABLE_GRANTS 175 + " WHERE " + AccountsDb.GRANTS_ACCOUNTS_ID 176 + "=" + AccountsDb.ACCOUNTS_ID; 177 178 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX = 179 "auth_uid_for_type:"; 180 private static final String META_KEY_DELIMITER = ":"; 181 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?"; 182 183 private final DeDatabaseHelper mDeDatabase; 184 private final Context mContext; 185 private final File mPreNDatabaseFile; 186 187 final Object mDebugStatementLock = new Object(); 188 private volatile long mDebugDbInsertionPoint = -1; 189 private volatile SQLiteStatement mDebugStatementForLogging; // not thread safe. 190 AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile)191 AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) { 192 mDeDatabase = deDatabase; 193 mContext = context; 194 mPreNDatabaseFile = preNDatabaseFile; 195 } 196 197 private static class CeDatabaseHelper extends SQLiteOpenHelper { 198 CeDatabaseHelper(Context context, String ceDatabaseName)199 CeDatabaseHelper(Context context, String ceDatabaseName) { 200 super(context, ceDatabaseName, null, CE_DATABASE_VERSION); 201 } 202 203 /** 204 * This call needs to be made while the mCacheLock is held. 205 * @param db The database. 206 */ 207 @Override onCreate(SQLiteDatabase db)208 public void onCreate(SQLiteDatabase db) { 209 Log.i(TAG, "Creating CE database " + getDatabaseName()); 210 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 211 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 212 + ACCOUNTS_NAME + " TEXT NOT NULL, " 213 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 214 + ACCOUNTS_PASSWORD + " TEXT, " 215 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 216 217 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 218 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 219 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 220 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 221 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 222 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 223 224 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 225 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 226 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 227 + EXTRAS_KEY + " TEXT NOT NULL, " 228 + EXTRAS_VALUE + " TEXT, " 229 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 230 231 createAccountsDeletionTrigger(db); 232 } 233 createAccountsDeletionTrigger(SQLiteDatabase db)234 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 235 db.execSQL("" 236 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 237 + " BEGIN" 238 + " DELETE FROM " + TABLE_AUTHTOKENS 239 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 240 + " DELETE FROM " + TABLE_EXTRAS 241 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 242 + " END"); 243 } 244 245 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)246 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 247 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion); 248 249 if (oldVersion == 9) { 250 if (Log.isLoggable(TAG, Log.VERBOSE)) { 251 Log.v(TAG, "onUpgrade upgrading to v10"); 252 } 253 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META); 254 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS); 255 // Recreate the trigger, since the old one references the table to be removed 256 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete"); 257 createAccountsDeletionTrigger(db); 258 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS); 259 db.execSQL("DROP TABLE IF EXISTS " + TABLE_DEBUG); 260 oldVersion++; 261 } 262 263 if (oldVersion != newVersion) { 264 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 265 } 266 } 267 268 @Override onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)269 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 270 Log.e(TAG, "onDowngrade: recreate accounts CE table"); 271 resetDatabase(db); 272 onCreate(db); 273 } 274 275 @Override onOpen(SQLiteDatabase db)276 public void onOpen(SQLiteDatabase db) { 277 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME); 278 } 279 280 281 /** 282 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location, 283 * it also performs migration to the new CE database. 284 */ create( Context context, File preNDatabaseFile, File ceDatabaseFile)285 static CeDatabaseHelper create( 286 Context context, 287 File preNDatabaseFile, 288 File ceDatabaseFile) { 289 boolean newDbExists = ceDatabaseFile.exists(); 290 if (Log.isLoggable(TAG, Log.VERBOSE)) { 291 Log.v(TAG, "CeDatabaseHelper.create ceDatabaseFile=" + ceDatabaseFile 292 + " oldDbExists=" + preNDatabaseFile.exists() 293 + " newDbExists=" + newDbExists); 294 } 295 boolean removeOldDb = false; 296 if (!newDbExists && preNDatabaseFile.exists()) { 297 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile); 298 } 299 // Try to open and upgrade if necessary 300 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath()); 301 ceHelper.getWritableDatabase(); 302 ceHelper.close(); 303 if (removeOldDb) { 304 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile); 305 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) { 306 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile); 307 } 308 } 309 return ceHelper; 310 } 311 migratePreNDbToCe(File oldDbFile, File ceDbFile)312 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) { 313 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile); 314 try { 315 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile); 316 } catch (IOException e) { 317 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e); 318 // Try to remove potentially damaged file if I/O error occurred 319 deleteDbFileWarnIfFailed(ceDbFile); 320 return false; 321 } 322 return true; 323 } 324 } 325 326 /** 327 * Returns information about auth tokens and their account for the specified query 328 * parameters. 329 * Output is in the format: 330 * <pre><code> | AUTHTOKEN_ID | ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre> 331 */ findAuthtokenForAllAccounts(String accountType, String authToken)332 Cursor findAuthtokenForAllAccounts(String accountType, String authToken) { 333 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 334 return db.rawQuery( 335 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID 336 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME 337 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE 338 + " FROM " + CE_TABLE_ACCOUNTS 339 + " JOIN " + CE_TABLE_AUTHTOKENS 340 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID 341 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID 342 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN 343 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", 344 new String[]{authToken, accountType}); 345 } 346 findAuthTokensByAccount(Account account)347 Map<String, String> findAuthTokensByAccount(Account account) { 348 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 349 HashMap<String, String> authTokensForAccount = new HashMap<>(); 350 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS, 351 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 352 SELECTION_ACCOUNTS_ID_BY_ACCOUNT, 353 new String[] {account.name, account.type}, 354 null, null, null); 355 try { 356 while (cursor.moveToNext()) { 357 final String type = cursor.getString(0); 358 final String authToken = cursor.getString(1); 359 authTokensForAccount.put(type, authToken); 360 } 361 } finally { 362 cursor.close(); 363 } 364 return authTokensForAccount; 365 } 366 deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType)367 boolean deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType) { 368 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 369 return db.delete(CE_TABLE_AUTHTOKENS, 370 AUTHTOKENS_ACCOUNTS_ID + "=?" + " AND " + AUTHTOKENS_TYPE + "=?", 371 new String[]{String.valueOf(accountId), authtokenType}) > 0; 372 } 373 deleteAuthToken(String authTokenId)374 boolean deleteAuthToken(String authTokenId) { 375 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 376 return db.delete( 377 CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?", 378 new String[]{authTokenId}) > 0; 379 } 380 insertAuthToken(long accountId, String authTokenType, String authToken)381 long insertAuthToken(long accountId, String authTokenType, String authToken) { 382 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 383 ContentValues values = new ContentValues(); 384 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); 385 values.put(AUTHTOKENS_TYPE, authTokenType); 386 values.put(AUTHTOKENS_AUTHTOKEN, authToken); 387 return db.insert( 388 CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values); 389 } 390 updateCeAccountPassword(long accountId, String password)391 int updateCeAccountPassword(long accountId, String password) { 392 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 393 final ContentValues values = new ContentValues(); 394 values.put(ACCOUNTS_PASSWORD, password); 395 return db.update( 396 CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", 397 new String[] {String.valueOf(accountId)}); 398 } 399 renameCeAccount(long accountId, String newName)400 boolean renameCeAccount(long accountId, String newName) { 401 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 402 final ContentValues values = new ContentValues(); 403 values.put(ACCOUNTS_NAME, newName); 404 final String[] argsAccountId = {String.valueOf(accountId)}; 405 return db.update( 406 CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0; 407 } 408 deleteAuthTokensByAccountId(long accountId)409 boolean deleteAuthTokensByAccountId(long accountId) { 410 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 411 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", 412 new String[] {String.valueOf(accountId)}) > 0; 413 } 414 findExtrasIdByAccountId(long accountId, String key)415 long findExtrasIdByAccountId(long accountId, String key) { 416 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 417 Cursor cursor = db.query( 418 CE_TABLE_EXTRAS, new String[]{EXTRAS_ID}, 419 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 420 new String[]{key}, null, null, null); 421 try { 422 if (cursor.moveToNext()) { 423 return cursor.getLong(0); 424 } 425 return -1; 426 } finally { 427 cursor.close(); 428 } 429 } 430 updateExtra(long extrasId, String value)431 boolean updateExtra(long extrasId, String value) { 432 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 433 ContentValues values = new ContentValues(); 434 values.put(EXTRAS_VALUE, value); 435 int rows = db.update( 436 TABLE_EXTRAS, values, EXTRAS_ID + "=?", 437 new String[]{String.valueOf(extrasId)}); 438 return rows == 1; 439 } 440 insertExtra(long accountId, String key, String value)441 long insertExtra(long accountId, String key, String value) { 442 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 443 ContentValues values = new ContentValues(); 444 values.put(EXTRAS_KEY, key); 445 values.put(EXTRAS_ACCOUNTS_ID, accountId); 446 values.put(EXTRAS_VALUE, value); 447 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values); 448 } 449 findUserExtrasForAccount(Account account)450 Map<String, String> findUserExtrasForAccount(Account account) { 451 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 452 Map<String, String> userExtrasForAccount = new HashMap<>(); 453 String[] selectionArgs = {account.name, account.type}; 454 try (Cursor cursor = db.query(CE_TABLE_EXTRAS, 455 COLUMNS_EXTRAS_KEY_AND_VALUE, 456 SELECTION_ACCOUNTS_ID_BY_ACCOUNT, 457 selectionArgs, 458 null, null, null)) { 459 while (cursor.moveToNext()) { 460 final String tmpkey = cursor.getString(0); 461 final String value = cursor.getString(1); 462 userExtrasForAccount.put(tmpkey, value); 463 } 464 } 465 return userExtrasForAccount; 466 } 467 findCeAccountId(Account account)468 long findCeAccountId(Account account) { 469 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 470 String[] columns = { ACCOUNTS_ID }; 471 String selection = "name=? AND type=?"; 472 String[] selectionArgs = {account.name, account.type}; 473 try (Cursor cursor = db.query(CE_TABLE_ACCOUNTS, columns, selection, selectionArgs, 474 null, null, null)) { 475 if (cursor.moveToNext()) { 476 return cursor.getLong(0); 477 } 478 return -1; 479 } 480 } 481 findAccountPasswordByNameAndType(String name, String type)482 String findAccountPasswordByNameAndType(String name, String type) { 483 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 484 String selection = ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?"; 485 String[] selectionArgs = {name, type}; 486 String[] columns = {ACCOUNTS_PASSWORD}; 487 try (Cursor cursor = db.query(CE_TABLE_ACCOUNTS, columns, selection, selectionArgs, 488 null, null, null)) { 489 if (cursor.moveToNext()) { 490 return cursor.getString(0); 491 } 492 return null; 493 } 494 } 495 insertCeAccount(Account account, String password)496 long insertCeAccount(Account account, String password) { 497 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 498 ContentValues values = new ContentValues(); 499 values.put(ACCOUNTS_NAME, account.name); 500 values.put(ACCOUNTS_TYPE, account.type); 501 values.put(ACCOUNTS_PASSWORD, password); 502 return db.insert( 503 CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values); 504 } 505 506 507 static class DeDatabaseHelper extends SQLiteOpenHelper { 508 509 private final int mUserId; 510 private volatile boolean mCeAttached; 511 DeDatabaseHelper(Context context, int userId, String deDatabaseName)512 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) { 513 super(context, deDatabaseName, null, DE_DATABASE_VERSION); 514 mUserId = userId; 515 } 516 517 /** 518 * This call needs to be made while the mCacheLock is held. The way to 519 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 520 * @param db The database. 521 */ 522 @Override onCreate(SQLiteDatabase db)523 public void onCreate(SQLiteDatabase db) { 524 Log.i(TAG, "Creating DE database for user " + mUserId); 525 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 526 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, " 527 + ACCOUNTS_NAME + " TEXT NOT NULL, " 528 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 529 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 530 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, " 531 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 532 533 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 534 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 535 + META_VALUE + " TEXT)"); 536 537 createGrantsTable(db); 538 createSharedAccountsTable(db); 539 createAccountsDeletionTrigger(db); 540 createDebugTable(db); 541 createAccountsVisibilityTable(db); 542 createAccountsDeletionVisibilityCleanupTrigger(db); 543 } 544 createSharedAccountsTable(SQLiteDatabase db)545 private void createSharedAccountsTable(SQLiteDatabase db) { 546 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 547 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 548 + ACCOUNTS_NAME + " TEXT NOT NULL, " 549 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 550 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 551 } 552 createAccountsDeletionTrigger(SQLiteDatabase db)553 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 554 db.execSQL("" 555 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 556 + " BEGIN" 557 + " DELETE FROM " + TABLE_GRANTS 558 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 559 + " END"); 560 } 561 createGrantsTable(SQLiteDatabase db)562 private void createGrantsTable(SQLiteDatabase db) { 563 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 564 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 565 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 566 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 567 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 568 + "," + GRANTS_GRANTEE_UID + "))"); 569 } 570 createAccountsVisibilityTable(SQLiteDatabase db)571 private void createAccountsVisibilityTable(SQLiteDatabase db) { 572 db.execSQL("CREATE TABLE " + TABLE_VISIBILITY + " ( " 573 + VISIBILITY_ACCOUNTS_ID + " INTEGER NOT NULL, " 574 + VISIBILITY_PACKAGE + " TEXT NOT NULL, " 575 + VISIBILITY_VALUE + " INTEGER, " 576 + "PRIMARY KEY(" + VISIBILITY_ACCOUNTS_ID + "," + VISIBILITY_PACKAGE + "))"); 577 } 578 createDebugTable(SQLiteDatabase db)579 static void createDebugTable(SQLiteDatabase db) { 580 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " 581 + ACCOUNTS_ID + " INTEGER," 582 + DEBUG_TABLE_ACTION_TYPE + " TEXT NOT NULL, " 583 + DEBUG_TABLE_TIMESTAMP + " DATETIME," 584 + DEBUG_TABLE_CALLER_UID + " INTEGER NOT NULL," 585 + DEBUG_TABLE_TABLE_NAME + " TEXT NOT NULL," 586 + DEBUG_TABLE_KEY + " INTEGER PRIMARY KEY)"); 587 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" 588 + DEBUG_TABLE_TIMESTAMP + ")"); 589 } 590 createAccountsDeletionVisibilityCleanupTrigger(SQLiteDatabase db)591 private void createAccountsDeletionVisibilityCleanupTrigger(SQLiteDatabase db) { 592 db.execSQL("" 593 + " CREATE TRIGGER " 594 + TABLE_ACCOUNTS + "DeleteVisibility DELETE ON " + TABLE_ACCOUNTS 595 + " BEGIN" 596 + " DELETE FROM " + TABLE_VISIBILITY 597 + " WHERE " + VISIBILITY_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 598 + " END"); 599 } 600 601 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)602 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 603 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 604 605 if (oldVersion == 1) { 606 createAccountsVisibilityTable(db); 607 createAccountsDeletionVisibilityCleanupTrigger(db); 608 oldVersion = 3; // skip version 2 which had uid based table 609 } 610 611 if (oldVersion == 2) { 612 // Remove uid based table and replace it with packageName based 613 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "DeleteVisibility"); 614 db.execSQL("DROP TABLE IF EXISTS " + TABLE_VISIBILITY); 615 createAccountsVisibilityTable(db); 616 createAccountsDeletionVisibilityCleanupTrigger(db); 617 oldVersion++; 618 } 619 620 if (oldVersion != newVersion) { 621 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 622 } 623 } 624 625 @Override onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)626 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 627 Log.e(TAG, "onDowngrade: recreate accounts DE table"); 628 resetDatabase(db); 629 onCreate(db); 630 } 631 getReadableDatabaseUserIsUnlocked()632 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() { 633 if(!mCeAttached) { 634 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId 635 + " is still locked. CE database is not yet available.", new Throwable()); 636 } 637 return super.getReadableDatabase(); 638 } 639 getWritableDatabaseUserIsUnlocked()640 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() { 641 if(!mCeAttached) { 642 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId 643 + " is still locked. CE database is not yet available.", new Throwable()); 644 } 645 return super.getWritableDatabase(); 646 } 647 648 @Override onOpen(SQLiteDatabase db)649 public void onOpen(SQLiteDatabase db) { 650 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME); 651 } 652 migratePreNDbToDe(File preNDbFile)653 private void migratePreNDbToDe(File preNDbFile) { 654 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile); 655 SQLiteDatabase db = getWritableDatabase(); 656 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb"); 657 db.beginTransaction(); 658 // Copy accounts fields 659 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS 660 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", " 661 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 662 + ") " 663 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", " 664 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 665 + " FROM preNDb." + TABLE_ACCOUNTS); 666 // Copy SHARED_ACCOUNTS 667 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS 668 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " + 669 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE 670 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS); 671 // Copy DEBUG_TABLE 672 db.execSQL("INSERT INTO " + TABLE_DEBUG 673 + "(" + ACCOUNTS_ID + "," + DEBUG_TABLE_ACTION_TYPE + "," 674 + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_CALLER_UID + "," 675 + DEBUG_TABLE_TABLE_NAME + "," + DEBUG_TABLE_KEY + ") " + 676 "SELECT " + ACCOUNTS_ID + "," + DEBUG_TABLE_ACTION_TYPE + "," 677 + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_CALLER_UID + "," 678 + DEBUG_TABLE_TABLE_NAME + "," + DEBUG_TABLE_KEY 679 + " FROM preNDb." + TABLE_DEBUG); 680 // Copy GRANTS 681 db.execSQL("INSERT INTO " + TABLE_GRANTS 682 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + "," 683 + GRANTS_GRANTEE_UID + ") " + 684 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + "," 685 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS); 686 // Copy META 687 db.execSQL("INSERT INTO " + TABLE_META 688 + "(" + META_KEY + "," + META_VALUE + ") " 689 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META); 690 db.setTransactionSuccessful(); 691 db.endTransaction(); 692 693 db.execSQL("DETACH DATABASE preNDb"); 694 } 695 } 696 deleteDeAccount(long accountId)697 boolean deleteDeAccount(long accountId) { 698 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 699 return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0; 700 } 701 insertSharedAccount(Account account)702 long insertSharedAccount(Account account) { 703 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 704 ContentValues values = new ContentValues(); 705 values.put(ACCOUNTS_NAME, account.name); 706 values.put(ACCOUNTS_TYPE, account.type); 707 return db.insert( 708 TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 709 } 710 deleteSharedAccount(Account account)711 boolean deleteSharedAccount(Account account) { 712 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 713 return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 714 new String[]{account.name, account.type}) > 0; 715 } 716 renameSharedAccount(Account account, String newName)717 int renameSharedAccount(Account account, String newName) { 718 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 719 final ContentValues values = new ContentValues(); 720 values.put(ACCOUNTS_NAME, newName); 721 return db.update(TABLE_SHARED_ACCOUNTS, 722 values, 723 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 724 new String[] {account.name, account.type}); 725 } 726 getSharedAccounts()727 List<Account> getSharedAccounts() { 728 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 729 ArrayList<Account> accountList = new ArrayList<>(); 730 Cursor cursor = null; 731 try { 732 cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE}, 733 null, null, null, null, null); 734 if (cursor != null && cursor.moveToFirst()) { 735 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 736 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 737 do { 738 accountList.add(new Account(cursor.getString(nameIndex), 739 cursor.getString(typeIndex))); 740 } while (cursor.moveToNext()); 741 } 742 } finally { 743 if (cursor != null) { 744 cursor.close(); 745 } 746 } 747 return accountList; 748 } 749 findSharedAccountId(Account account)750 long findSharedAccountId(Account account) { 751 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 752 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ 753 ACCOUNTS_ID}, 754 "name=? AND type=?", new String[]{account.name, account.type}, null, null, 755 null); 756 try { 757 if (cursor.moveToNext()) { 758 return cursor.getLong(0); 759 } 760 return -1; 761 } finally { 762 cursor.close(); 763 } 764 } 765 findAccountLastAuthenticatedTime(Account account)766 long findAccountLastAuthenticatedTime(Account account) { 767 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 768 return DatabaseUtils.longForQuery(db, 769 "SELECT " + AccountsDb.ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS 770 + " FROM " + TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND " 771 + ACCOUNTS_TYPE + "=?", 772 new String[] {account.name, account.type}); 773 } 774 updateAccountLastAuthenticatedTime(Account account)775 boolean updateAccountLastAuthenticatedTime(Account account) { 776 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 777 final ContentValues values = new ContentValues(); 778 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis()); 779 int rowCount = db.update(TABLE_ACCOUNTS, 780 values, 781 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?", 782 new String[] { account.name, account.type }); 783 return rowCount > 0; 784 } 785 dumpDeAccountsTable(PrintWriter pw)786 void dumpDeAccountsTable(PrintWriter pw) { 787 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 788 Cursor cursor = db.query( 789 TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 790 null, null, ACCOUNTS_TYPE, null, null); 791 try { 792 while (cursor.moveToNext()) { 793 // print type,count 794 pw.println(cursor.getString(0) + "," + cursor.getString(1)); 795 } 796 } finally { 797 if (cursor != null) { 798 cursor.close(); 799 } 800 } 801 } 802 findDeAccountId(Account account)803 long findDeAccountId(Account account) { 804 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 805 String[] columns = {ACCOUNTS_ID}; 806 String selection = "name=? AND type=?"; 807 String[] selectionArgs = {account.name, account.type}; 808 try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, selection, selectionArgs, 809 null, null, null)) { 810 if (cursor.moveToNext()) { 811 return cursor.getLong(0); 812 } 813 return -1; 814 } 815 } 816 findAllDeAccounts()817 Map<Long, Account> findAllDeAccounts() { 818 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 819 LinkedHashMap<Long, Account> map = new LinkedHashMap<>(); 820 String[] columns = {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}; 821 try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, 822 null, null, null, null, ACCOUNTS_ID)) { 823 while (cursor.moveToNext()) { 824 final long accountId = cursor.getLong(0); 825 final String accountType = cursor.getString(1); 826 final String accountName = cursor.getString(2); 827 828 final Account account = new Account(accountName, accountType); 829 map.put(accountId, account); 830 } 831 } 832 return map; 833 } 834 findDeAccountPreviousName(Account account)835 String findDeAccountPreviousName(Account account) { 836 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 837 String[] columns = {ACCOUNTS_PREVIOUS_NAME}; 838 String selection = ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?"; 839 String[] selectionArgs = {account.name, account.type}; 840 try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, selection, selectionArgs, 841 null, null, null)) { 842 if (cursor.moveToNext()) { 843 return cursor.getString(0); 844 } 845 } 846 return null; 847 } 848 insertDeAccount(Account account, long accountId)849 long insertDeAccount(Account account, long accountId) { 850 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 851 ContentValues values = new ContentValues(); 852 values.put(ACCOUNTS_ID, accountId); 853 values.put(ACCOUNTS_NAME, account.name); 854 values.put(ACCOUNTS_TYPE, account.type); 855 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis()); 856 return db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); 857 } 858 renameDeAccount(long accountId, String newName, String previousName)859 boolean renameDeAccount(long accountId, String newName, String previousName) { 860 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 861 final ContentValues values = new ContentValues(); 862 values.put(ACCOUNTS_NAME, newName); 863 values.put(ACCOUNTS_PREVIOUS_NAME, previousName); 864 final String[] argsAccountId = {String.valueOf(accountId)}; 865 return db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0; 866 } 867 deleteGrantsByAccountIdAuthTokenTypeAndUid(long accountId, String authTokenType, long uid)868 boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(long accountId, 869 String authTokenType, long uid) { 870 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 871 return db.delete(TABLE_GRANTS, 872 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 873 + GRANTS_GRANTEE_UID + "=?", 874 new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0; 875 } 876 findAllUidGrants()877 List<Integer> findAllUidGrants() { 878 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 879 List<Integer> result = new ArrayList<>(); 880 final Cursor cursor = db.query(TABLE_GRANTS, 881 new String[]{GRANTS_GRANTEE_UID}, 882 null, null, GRANTS_GRANTEE_UID, null, null); 883 try { 884 while (cursor.moveToNext()) { 885 final int uid = cursor.getInt(0); 886 result.add(uid); 887 } 888 } finally { 889 cursor.close(); 890 } 891 return result; 892 } 893 findMatchingGrantsCount(int uid, String authTokenType, Account account)894 long findMatchingGrantsCount(int uid, String authTokenType, Account account) { 895 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 896 String[] args = {String.valueOf(uid), authTokenType, account.name, account.type}; 897 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args); 898 } 899 findMatchingGrantsCountAnyToken(int uid, Account account)900 long findMatchingGrantsCountAnyToken(int uid, Account account) { 901 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 902 String[] args = {String.valueOf(uid), account.name, account.type}; 903 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS_ANY_TOKEN, args); 904 } 905 insertGrant(long accountId, String authTokenType, int uid)906 long insertGrant(long accountId, String authTokenType, int uid) { 907 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 908 ContentValues values = new ContentValues(); 909 values.put(GRANTS_ACCOUNTS_ID, accountId); 910 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 911 values.put(GRANTS_GRANTEE_UID, uid); 912 return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 913 } 914 deleteGrantsByUid(int uid)915 boolean deleteGrantsByUid(int uid) { 916 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 917 return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", 918 new String[] {Integer.toString(uid)}) > 0; 919 } 920 setAccountVisibility(long accountId, String packageName, int visibility)921 boolean setAccountVisibility(long accountId, String packageName, int visibility) { 922 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 923 ContentValues values = new ContentValues(); 924 values.put(VISIBILITY_ACCOUNTS_ID, String.valueOf(accountId)); 925 values.put(VISIBILITY_PACKAGE, packageName); 926 values.put(VISIBILITY_VALUE, String.valueOf(visibility)); 927 return (db.replace(TABLE_VISIBILITY, VISIBILITY_VALUE, values) != -1); 928 } 929 findAccountVisibility(Account account, String packageName)930 Integer findAccountVisibility(Account account, String packageName) { 931 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 932 final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE}, 933 SELECTION_ACCOUNTS_ID_BY_ACCOUNT + " AND " + VISIBILITY_PACKAGE + "=? ", 934 new String[] {account.name, account.type, packageName}, null, null, null); 935 try { 936 while (cursor.moveToNext()) { 937 return cursor.getInt(0); 938 } 939 } finally { 940 cursor.close(); 941 } 942 return null; 943 } 944 findAccountVisibility(long accountId, String packageName)945 Integer findAccountVisibility(long accountId, String packageName) { 946 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 947 final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE}, 948 VISIBILITY_ACCOUNTS_ID + "=? AND " + VISIBILITY_PACKAGE + "=? ", 949 new String[] {String.valueOf(accountId), packageName}, null, null, null); 950 try { 951 while (cursor.moveToNext()) { 952 return cursor.getInt(0); 953 } 954 } finally { 955 cursor.close(); 956 } 957 return null; 958 } 959 findDeAccountByAccountId(long accountId)960 Account findDeAccountByAccountId(long accountId) { 961 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 962 final Cursor cursor = db.query(TABLE_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE}, 963 ACCOUNTS_ID + "=? ", new String[] {String.valueOf(accountId)}, null, null, null); 964 try { 965 while (cursor.moveToNext()) { 966 return new Account(cursor.getString(0), cursor.getString(1)); 967 } 968 } finally { 969 cursor.close(); 970 } 971 return null; 972 } 973 974 /** 975 * Returns a map from packageNames to visibility. 976 */ findAllVisibilityValuesForAccount(Account account)977 Map<String, Integer> findAllVisibilityValuesForAccount(Account account) { 978 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 979 Map<String, Integer> result = new HashMap<>(); 980 final Cursor cursor = 981 db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_PACKAGE, VISIBILITY_VALUE}, 982 SELECTION_ACCOUNTS_ID_BY_ACCOUNT, new String[] {account.name, account.type}, 983 null, null, null); 984 try { 985 while (cursor.moveToNext()) { 986 result.put(cursor.getString(0), cursor.getInt(1)); 987 } 988 } finally { 989 cursor.close(); 990 } 991 return result; 992 } 993 994 /** 995 * Returns a map account -> (package -> visibility) 996 */ findAllVisibilityValues()997 Map <Account, Map<String, Integer>> findAllVisibilityValues() { 998 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 999 Map<Account, Map<String, Integer>> result = new HashMap<>(); 1000 Cursor cursor = db.rawQuery( 1001 "SELECT " + TABLE_VISIBILITY + "." + VISIBILITY_PACKAGE 1002 + ", " + TABLE_VISIBILITY + "." + VISIBILITY_VALUE 1003 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME 1004 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE 1005 + " FROM " + TABLE_VISIBILITY 1006 + " JOIN " + TABLE_ACCOUNTS 1007 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID 1008 + " = " + TABLE_VISIBILITY + "." + VISIBILITY_ACCOUNTS_ID, null); 1009 try { 1010 while (cursor.moveToNext()) { 1011 String packageName = cursor.getString(0); 1012 Integer visibility = cursor.getInt(1); 1013 String accountName = cursor.getString(2); 1014 String accountType = cursor.getString(3); 1015 Account account = new Account(accountName, accountType); 1016 Map <String, Integer> accountVisibility = result.get(account); 1017 if (accountVisibility == null) { 1018 accountVisibility = new HashMap<>(); 1019 result.put(account, accountVisibility); 1020 } 1021 accountVisibility.put(packageName, visibility); 1022 } 1023 } finally { 1024 cursor.close(); 1025 } 1026 return result; 1027 } 1028 deleteAccountVisibilityForPackage(String packageName)1029 boolean deleteAccountVisibilityForPackage(String packageName) { 1030 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 1031 return db.delete(TABLE_VISIBILITY, VISIBILITY_PACKAGE + "=? ", 1032 new String[] {packageName}) > 0; 1033 } 1034 insertOrReplaceMetaAuthTypeAndUid(String authenticatorType, int uid)1035 long insertOrReplaceMetaAuthTypeAndUid(String authenticatorType, int uid) { 1036 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 1037 ContentValues values = new ContentValues(); 1038 values.put(META_KEY, 1039 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType); 1040 values.put(META_VALUE, uid); 1041 return db.insertWithOnConflict(TABLE_META, null, values, 1042 SQLiteDatabase.CONFLICT_REPLACE); 1043 } 1044 findMetaAuthUid()1045 Map<String, Integer> findMetaAuthUid() { 1046 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 1047 Cursor metaCursor = db.query( 1048 TABLE_META, 1049 new String[]{META_KEY, META_VALUE}, 1050 SELECTION_META_BY_AUTHENTICATOR_TYPE, 1051 new String[]{META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"}, 1052 null /* groupBy */, 1053 null /* having */, 1054 META_KEY); 1055 Map<String, Integer> map = new LinkedHashMap<>(); 1056 try { 1057 while (metaCursor.moveToNext()) { 1058 String type = TextUtils 1059 .split(metaCursor.getString(0), META_KEY_DELIMITER)[1]; 1060 String uidStr = metaCursor.getString(1); 1061 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) { 1062 // Should never happen. 1063 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type) 1064 + ", uid empty: " + TextUtils.isEmpty(uidStr)); 1065 continue; 1066 } 1067 int uid = Integer.parseInt(metaCursor.getString(1)); 1068 map.put(type, uid); 1069 } 1070 } finally { 1071 metaCursor.close(); 1072 } 1073 return map; 1074 } 1075 deleteMetaByAuthTypeAndUid(String type, int uid)1076 boolean deleteMetaByAuthTypeAndUid(String type, int uid) { 1077 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 1078 return db.delete( 1079 TABLE_META, 1080 META_KEY + "=? AND " + META_VALUE + "=?", 1081 new String[]{ 1082 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type, 1083 String.valueOf(uid)} 1084 ) > 0; 1085 } 1086 1087 /** 1088 * Returns list of all grants as {@link Pair pairs} of account name and UID. 1089 */ findAllAccountGrants()1090 List<Pair<String, Integer>> findAllAccountGrants() { 1091 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 1092 try (Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null)) { 1093 if (cursor == null || !cursor.moveToFirst()) { 1094 return Collections.emptyList(); 1095 } 1096 List<Pair<String, Integer>> results = new ArrayList<>(); 1097 do { 1098 final String accountName = cursor.getString(0); 1099 final int uid = cursor.getInt(1); 1100 results.add(Pair.create(accountName, uid)); 1101 } while (cursor.moveToNext()); 1102 return results; 1103 } 1104 } 1105 1106 private static class PreNDatabaseHelper extends SQLiteOpenHelper { 1107 private final Context mContext; 1108 private final int mUserId; 1109 PreNDatabaseHelper(Context context, int userId, String preNDatabaseName)1110 PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) { 1111 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION); 1112 mContext = context; 1113 mUserId = userId; 1114 } 1115 1116 @Override onCreate(SQLiteDatabase db)1117 public void onCreate(SQLiteDatabase db) { 1118 // We use PreNDatabaseHelper only if pre-N db exists 1119 throw new IllegalStateException("Legacy database cannot be created - only upgraded!"); 1120 } 1121 createSharedAccountsTable(SQLiteDatabase db)1122 private void createSharedAccountsTable(SQLiteDatabase db) { 1123 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 1124 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 1125 + ACCOUNTS_NAME + " TEXT NOT NULL, " 1126 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 1127 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 1128 } 1129 addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db)1130 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) { 1131 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " 1132 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0"); 1133 } 1134 addOldAccountNameColumn(SQLiteDatabase db)1135 private void addOldAccountNameColumn(SQLiteDatabase db) { 1136 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 1137 } 1138 addDebugTable(SQLiteDatabase db)1139 private void addDebugTable(SQLiteDatabase db) { 1140 DeDatabaseHelper.createDebugTable(db); 1141 } 1142 createAccountsDeletionTrigger(SQLiteDatabase db)1143 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 1144 db.execSQL("" 1145 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 1146 + " BEGIN" 1147 + " DELETE FROM " + TABLE_AUTHTOKENS 1148 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 1149 + " DELETE FROM " + TABLE_EXTRAS 1150 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 1151 + " DELETE FROM " + TABLE_GRANTS 1152 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 1153 + " END"); 1154 } 1155 createGrantsTable(SQLiteDatabase db)1156 private void createGrantsTable(SQLiteDatabase db) { 1157 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 1158 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 1159 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 1160 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 1161 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 1162 + "," + GRANTS_GRANTEE_UID + "))"); 1163 } 1164 insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid)1165 static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) { 1166 ContentValues values = new ContentValues(); 1167 values.put(META_KEY, 1168 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType); 1169 values.put(META_VALUE, uid); 1170 return db.insert(TABLE_META, null, values); 1171 } 1172 populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db, Map<String, Integer> authTypeAndUIDMap)1173 private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db, 1174 Map<String, Integer> authTypeAndUIDMap) { 1175 for (Map.Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) { 1176 insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue()); 1177 } 1178 } 1179 1180 /** 1181 * Pre-N database may need an upgrade before splitting 1182 */ 1183 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)1184 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 1185 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 1186 1187 if (oldVersion == 1) { 1188 // no longer need to do anything since the work is done 1189 // when upgrading from version 2 1190 oldVersion++; 1191 } 1192 1193 if (oldVersion == 2) { 1194 createGrantsTable(db); 1195 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 1196 createAccountsDeletionTrigger(db); 1197 oldVersion++; 1198 } 1199 1200 if (oldVersion == 3) { 1201 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 1202 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 1203 oldVersion++; 1204 } 1205 1206 if (oldVersion == 4) { 1207 createSharedAccountsTable(db); 1208 oldVersion++; 1209 } 1210 1211 if (oldVersion == 5) { 1212 addOldAccountNameColumn(db); 1213 oldVersion++; 1214 } 1215 1216 if (oldVersion == 6) { 1217 addLastSuccessfullAuthenticatedTimeColumn(db); 1218 oldVersion++; 1219 } 1220 1221 if (oldVersion == 7) { 1222 addDebugTable(db); 1223 oldVersion++; 1224 } 1225 1226 if (oldVersion == 8) { 1227 populateMetaTableWithAuthTypeAndUID( 1228 db, 1229 AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId)); 1230 oldVersion++; 1231 } 1232 1233 if (oldVersion != newVersion) { 1234 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 1235 } 1236 } 1237 1238 @Override onOpen(SQLiteDatabase db)1239 public void onOpen(SQLiteDatabase db) { 1240 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 1241 } 1242 } 1243 findCeAccountsNotInDe()1244 List<Account> findCeAccountsNotInDe() { 1245 SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked(); 1246 // Select accounts from CE that do not exist in DE 1247 Cursor cursor = db.rawQuery( 1248 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE 1249 + " FROM " + CE_TABLE_ACCOUNTS 1250 + " WHERE NOT EXISTS " 1251 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS 1252 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID 1253 + " )", null); 1254 try { 1255 List<Account> accounts = new ArrayList<>(cursor.getCount()); 1256 while (cursor.moveToNext()) { 1257 String accountName = cursor.getString(0); 1258 String accountType = cursor.getString(1); 1259 accounts.add(new Account(accountName, accountType)); 1260 } 1261 return accounts; 1262 } finally { 1263 cursor.close(); 1264 } 1265 } 1266 deleteCeAccount(long accountId)1267 boolean deleteCeAccount(long accountId) { 1268 SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked(); 1269 return db.delete( 1270 CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0; 1271 } 1272 isCeDatabaseAttached()1273 boolean isCeDatabaseAttached() { 1274 return mDeDatabase.mCeAttached; 1275 } 1276 beginTransaction()1277 void beginTransaction() { 1278 mDeDatabase.getWritableDatabase().beginTransaction(); 1279 } 1280 setTransactionSuccessful()1281 void setTransactionSuccessful() { 1282 mDeDatabase.getWritableDatabase().setTransactionSuccessful(); 1283 } 1284 endTransaction()1285 void endTransaction() { 1286 mDeDatabase.getWritableDatabase().endTransaction(); 1287 } 1288 attachCeDatabase(File ceDbFile)1289 void attachCeDatabase(File ceDbFile) { 1290 CeDatabaseHelper.create(mContext, mPreNDatabaseFile, ceDbFile); 1291 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 1292 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb"); 1293 mDeDatabase.mCeAttached = true; 1294 } 1295 1296 /* 1297 * Finds the row key where the next insertion should take place. Returns number of rows 1298 * if it is less {@link #MAX_DEBUG_DB_SIZE}, otherwise finds the lowest number available. 1299 */ calculateDebugTableInsertionPoint()1300 long calculateDebugTableInsertionPoint() { 1301 try { 1302 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 1303 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG; 1304 int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 1305 if (size < MAX_DEBUG_DB_SIZE) { 1306 return size; 1307 } 1308 1309 // This query finds the smallest timestamp value (and if 2 records have 1310 // same timestamp, the choose the lower id). 1311 queryCountDebugDbRows = 1312 "SELECT " + DEBUG_TABLE_KEY 1313 + " FROM " + TABLE_DEBUG 1314 + " ORDER BY " + DEBUG_TABLE_TIMESTAMP + "," 1315 + DEBUG_TABLE_KEY 1316 + " LIMIT 1"; 1317 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); 1318 } catch (SQLiteException e) { 1319 Log.e(TAG, "Failed to open debug table" + e); 1320 return -1; 1321 } 1322 } 1323 compileSqlStatementForLogging()1324 SQLiteStatement compileSqlStatementForLogging() { 1325 SQLiteDatabase db = mDeDatabase.getWritableDatabase(); 1326 String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG 1327 + " VALUES (?,?,?,?,?,?)"; 1328 return db.compileStatement(sql); 1329 } 1330 1331 /** 1332 * Returns statement for logging or {@code null} on database open failure. 1333 * Returned value must be guarded by {link #debugStatementLock} 1334 */ getStatementForLogging()1335 @Nullable SQLiteStatement getStatementForLogging() { 1336 if (mDebugStatementForLogging != null) { 1337 return mDebugStatementForLogging; 1338 } 1339 try { 1340 mDebugStatementForLogging = compileSqlStatementForLogging(); 1341 return mDebugStatementForLogging; 1342 } catch (SQLiteException e) { 1343 Log.e(TAG, "Failed to open debug table" + e); 1344 return null; 1345 } 1346 } 1347 closeDebugStatement()1348 void closeDebugStatement() { 1349 synchronized (mDebugStatementLock) { 1350 if (mDebugStatementForLogging != null) { 1351 mDebugStatementForLogging.close(); 1352 mDebugStatementForLogging = null; 1353 } 1354 } 1355 } 1356 reserveDebugDbInsertionPoint()1357 long reserveDebugDbInsertionPoint() { 1358 if (mDebugDbInsertionPoint == -1) { 1359 mDebugDbInsertionPoint = calculateDebugTableInsertionPoint(); 1360 return mDebugDbInsertionPoint; 1361 } 1362 mDebugDbInsertionPoint = (mDebugDbInsertionPoint + 1) % MAX_DEBUG_DB_SIZE; 1363 return mDebugDbInsertionPoint; 1364 } 1365 dumpDebugTable(PrintWriter pw)1366 void dumpDebugTable(PrintWriter pw) { 1367 SQLiteDatabase db = mDeDatabase.getReadableDatabase(); 1368 Cursor cursor = db.query(TABLE_DEBUG, null, 1369 null, null, null, null, DEBUG_TABLE_TIMESTAMP); 1370 pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key"); 1371 pw.println("Accounts History"); 1372 try { 1373 while (cursor.moveToNext()) { 1374 // print type,count 1375 pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," + 1376 cursor.getString(2) + "," + cursor.getString(3) + "," 1377 + cursor.getString(4) + "," + cursor.getString(5)); 1378 } 1379 } finally { 1380 cursor.close(); 1381 } 1382 } 1383 1384 @Override close()1385 public void close() { 1386 mDeDatabase.close(); 1387 } 1388 deleteDbFileWarnIfFailed(File dbFile)1389 static void deleteDbFileWarnIfFailed(File dbFile) { 1390 if (!SQLiteDatabase.deleteDatabase(dbFile)) { 1391 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully"); 1392 } 1393 } 1394 create(Context context, int userId, File preNDatabaseFile, File deDatabaseFile)1395 public static AccountsDb create(Context context, int userId, File preNDatabaseFile, 1396 File deDatabaseFile) { 1397 boolean newDbExists = deDatabaseFile.exists(); 1398 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId, 1399 deDatabaseFile.getPath()); 1400 // If the db just created, and there is a legacy db, migrate it 1401 if (!newDbExists && preNDatabaseFile.exists()) { 1402 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION 1403 PreNDatabaseHelper 1404 preNDatabaseHelper = new PreNDatabaseHelper(context, userId, 1405 preNDatabaseFile.getPath()); 1406 // Open the database to force upgrade if required 1407 preNDatabaseHelper.getWritableDatabase(); 1408 preNDatabaseHelper.close(); 1409 // Move data without SPII to DE 1410 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile); 1411 } 1412 return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile); 1413 } 1414 1415 /** 1416 * Removes all tables and triggers created by AccountManager. 1417 */ resetDatabase(SQLiteDatabase db)1418 private static void resetDatabase(SQLiteDatabase db) { 1419 try (Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type ='table'", null)) { 1420 while (c.moveToNext()) { 1421 String name = c.getString(0); 1422 // Skip tables managed by SQLiteDatabase 1423 if ("android_metadata".equals(name) || "sqlite_sequence".equals(name)) { 1424 continue; 1425 } 1426 db.execSQL("DROP TABLE IF EXISTS " + name); 1427 } 1428 } 1429 1430 try (Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type ='trigger'", null)) { 1431 while (c.moveToNext()) { 1432 String name = c.getString(0); 1433 db.execSQL("DROP TRIGGER IF EXISTS " + name); 1434 } 1435 } 1436 } 1437 } 1438