Loading services/core/java/com/android/server/accounts/AccountsDb.java +117 −10 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ class AccountsDb implements AutoCloseable { private static final String DATABASE_NAME = "accounts.db"; private static final int PRE_N_DATABASE_VERSION = 9; private static final int CE_DATABASE_VERSION = 10; private static final int DE_DATABASE_VERSION = 1; private static final int DE_DATABASE_VERSION = 2; // Added visibility support in O. static final String TABLE_ACCOUNTS = "accounts"; Loading @@ -73,6 +73,11 @@ class AccountsDb implements AutoCloseable { private static final String AUTHTOKENS_TYPE = "type"; private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; private static final String TABLE_VISIBILITY = "visibility"; private static final String VISIBILITY_ACCOUNTS_ID = "accounts_id"; private static final String VISIBILITY_UID = "_uid"; private static final String VISIBILITY_VALUE = "value"; private static final String TABLE_GRANTS = "grants"; private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; Loading Loading @@ -153,14 +158,12 @@ class AccountsDb implements AutoCloseable { + " AND " + ACCOUNTS_NAME + "=?" + " AND " + ACCOUNTS_TYPE + "=?"; private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; private static final String SELECTION_ACCOUNTS_ID_BY_ACCOUNT = "accounts_id=(select _id FROM accounts WHERE name=? AND type=?)"; private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, AUTHTOKENS_AUTHTOKEN}; private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, AUTHTOKENS_AUTHTOKEN}; private static final String SELECTION_USERDATA_BY_ACCOUNT = EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; private static final String ACCOUNT_ACCESS_GRANTS = "" Loading Loading @@ -334,7 +337,7 @@ class AccountsDb implements AutoCloseable { HashMap<String, String> authTokensForAccount = new HashMap<>(); Cursor cursor = db.query(CE_TABLE_AUTHTOKENS, COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, SELECTION_AUTHTOKENS_BY_ACCOUNT, SELECTION_ACCOUNTS_ID_BY_ACCOUNT, new String[] {account.name, account.type}, null, null, null); try { Loading Loading @@ -438,7 +441,7 @@ class AccountsDb implements AutoCloseable { String[] selectionArgs = {account.name, account.type}; try (Cursor cursor = db.query(CE_TABLE_EXTRAS, COLUMNS_EXTRAS_KEY_AND_VALUE, SELECTION_USERDATA_BY_ACCOUNT, SELECTION_ACCOUNTS_ID_BY_ACCOUNT, selectionArgs, null, null, null)) { while (cursor.moveToNext()) { Loading Loading @@ -523,6 +526,8 @@ class AccountsDb implements AutoCloseable { createSharedAccountsTable(db); createAccountsDeletionTrigger(db); createDebugTable(db); createAccountsVisibilityTable(db); createAccountsDeletionVisibilityCleanupTrigger(db); } private void createSharedAccountsTable(SQLiteDatabase db) { Loading Loading @@ -551,6 +556,14 @@ class AccountsDb implements AutoCloseable { + "," + GRANTS_GRANTEE_UID + "))"); } private void createAccountsVisibilityTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_VISIBILITY + " ( " + VISIBILITY_ACCOUNTS_ID + " INTEGER NOT NULL, " + VISIBILITY_UID + " TEXT NOT NULL, " + VISIBILITY_VALUE + " INTEGER, " + "PRIMARY KEY(" + VISIBILITY_ACCOUNTS_ID + "," + VISIBILITY_UID + "))"); } static void createDebugTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " + ACCOUNTS_ID + " INTEGER," Loading @@ -563,10 +576,26 @@ class AccountsDb implements AutoCloseable { + DEBUG_TABLE_TIMESTAMP + ")"); } private void createAccountsDeletionVisibilityCleanupTrigger(SQLiteDatabase db) { db.execSQL("" + " CREATE TRIGGER " + TABLE_ACCOUNTS + "DeleteVisibility DELETE ON " + TABLE_ACCOUNTS + " BEGIN" + " DELETE FROM " + TABLE_VISIBILITY + " WHERE " + VISIBILITY_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" + " END"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); if (oldVersion == 1) { createAccountsVisibilityTable(db); createAccountsDeletionVisibilityCleanupTrigger(db); ++oldVersion; } if (oldVersion != newVersion) { Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); } Loading Loading @@ -861,6 +890,84 @@ class AccountsDb implements AutoCloseable { new String[] {Integer.toString(uid)}) > 0; } boolean setAccountVisibility(long accountId, int uid, int visibility) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(VISIBILITY_ACCOUNTS_ID, String.valueOf(accountId)); values.put(VISIBILITY_UID, String.valueOf(uid)); values.put(VISIBILITY_VALUE, String.valueOf(visibility)); return (db.replace(TABLE_VISIBILITY, VISIBILITY_VALUE, values) != -1); } Integer findAccountVisibility(Account account, int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE}, SELECTION_ACCOUNTS_ID_BY_ACCOUNT + " AND " + VISIBILITY_UID + "=? ", new String[] {account.name, account.type, String.valueOf(uid)}, null, null, null); try { while (cursor.moveToNext()) { return cursor.getInt(0); } } finally { cursor.close(); } return null; } Integer findAccountVisibility(long accountId, int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? AND " + VISIBILITY_UID + "=? ", new String[] {String.valueOf(accountId), String.valueOf(uid)}, null, null, null); try { while (cursor.moveToNext()) { return cursor.getInt(0); } } finally { cursor.close(); } return null; } Account findDeAccountByAccountId(long accountId) { SQLiteDatabase db = mDeDatabase.getReadableDatabase(); final Cursor cursor = db.query(TABLE_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE}, ACCOUNTS_ID + "=? ", new String[] {String.valueOf(accountId)}, null, null, null); try { while (cursor.moveToNext()) { return new Account(cursor.getString(0), cursor.getString(1)); } } finally { cursor.close(); } return null; } /** * Returns a map from uid to visibility value. */ Map<Integer, Integer> findAccountVisibilityForAccountId(long accountId) { SQLiteDatabase db = mDeDatabase.getReadableDatabase(); Map<Integer, Integer> result = new HashMap<>(); final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_UID, VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? ", new String[] {String.valueOf(accountId)}, null, null, null); try { while (cursor.moveToNext()) { result.put(cursor.getInt(0), cursor.getInt(1)); } } finally { cursor.close(); } return result; } boolean deleteAccountVisibilityForUid(int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); return db.delete(TABLE_VISIBILITY, VISIBILITY_UID + "=? ", new String[] {Integer.toString(uid)}) > 0; } long insertOrReplaceMetaAuthTypeAndUid(String authenticatorType, int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); ContentValues values = new ContentValues(); Loading services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java +62 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** Loading Loading @@ -338,4 +339,65 @@ public class AccountsDbTest { actualId = mAccountsDb.findDeAccountId(account); assertEquals(-1, actualId); } @Test public void testFindDeAccountByAccountId() { long accId = 10; Account account = new Account("name", "example.com"); assertNull(mAccountsDb.findDeAccountByAccountId(accId)); mAccountsDb.insertDeAccount(account, accId); Account foundAccount = mAccountsDb.findDeAccountByAccountId(accId); assertEquals(account, foundAccount); } @Test public void testVisibilityFindSetDelete() { long accId = 10; int uid1 = 100500; int uid2 = 100501; Account account = new Account("name", "example.com"); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); mAccountsDb.insertDeAccount(account, accId); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); assertNull(mAccountsDb.findAccountVisibility(accId, uid1)); mAccountsDb.setAccountVisibility(accId, uid1, 1); assertEquals(mAccountsDb.findAccountVisibility(account, uid1), Integer.valueOf(1)); assertEquals(mAccountsDb.findAccountVisibility(accId, uid1), Integer.valueOf(1)); mAccountsDb.setAccountVisibility(accId, uid2, 2); assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(2)); mAccountsDb.setAccountVisibility(accId, uid2, 3); assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(3)); Map<Integer, Integer> vis = mAccountsDb.findAccountVisibilityForAccountId(accId); assertEquals(vis.size(), 2); assertEquals(vis.get(uid1), Integer.valueOf(1)); assertEquals(vis.get(uid2), Integer.valueOf(3)); assertTrue(mAccountsDb.deleteAccountVisibilityForUid(uid1)); assertNull(mAccountsDb.findAccountVisibility(accId, uid1)); assertFalse(mAccountsDb.deleteAccountVisibilityForUid(uid1)); // Already deleted. } @Test public void testVisibilityCleanupTrigger() { long accId = 10; int uid1 = 100500; Account account = new Account("name", "example.com"); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); mAccountsDb.insertDeAccount(account, accId); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); mAccountsDb.setAccountVisibility(accId, uid1, 1); assertEquals(mAccountsDb.findAccountVisibility(accId, uid1), Integer.valueOf(1)); assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility. assertNull(mAccountsDb.findAccountVisibility(account, uid1)); } } Loading
services/core/java/com/android/server/accounts/AccountsDb.java +117 −10 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ class AccountsDb implements AutoCloseable { private static final String DATABASE_NAME = "accounts.db"; private static final int PRE_N_DATABASE_VERSION = 9; private static final int CE_DATABASE_VERSION = 10; private static final int DE_DATABASE_VERSION = 1; private static final int DE_DATABASE_VERSION = 2; // Added visibility support in O. static final String TABLE_ACCOUNTS = "accounts"; Loading @@ -73,6 +73,11 @@ class AccountsDb implements AutoCloseable { private static final String AUTHTOKENS_TYPE = "type"; private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; private static final String TABLE_VISIBILITY = "visibility"; private static final String VISIBILITY_ACCOUNTS_ID = "accounts_id"; private static final String VISIBILITY_UID = "_uid"; private static final String VISIBILITY_VALUE = "value"; private static final String TABLE_GRANTS = "grants"; private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; Loading Loading @@ -153,14 +158,12 @@ class AccountsDb implements AutoCloseable { + " AND " + ACCOUNTS_NAME + "=?" + " AND " + ACCOUNTS_TYPE + "=?"; private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; private static final String SELECTION_ACCOUNTS_ID_BY_ACCOUNT = "accounts_id=(select _id FROM accounts WHERE name=? AND type=?)"; private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, AUTHTOKENS_AUTHTOKEN}; private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, AUTHTOKENS_AUTHTOKEN}; private static final String SELECTION_USERDATA_BY_ACCOUNT = EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; private static final String ACCOUNT_ACCESS_GRANTS = "" Loading Loading @@ -334,7 +337,7 @@ class AccountsDb implements AutoCloseable { HashMap<String, String> authTokensForAccount = new HashMap<>(); Cursor cursor = db.query(CE_TABLE_AUTHTOKENS, COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, SELECTION_AUTHTOKENS_BY_ACCOUNT, SELECTION_ACCOUNTS_ID_BY_ACCOUNT, new String[] {account.name, account.type}, null, null, null); try { Loading Loading @@ -438,7 +441,7 @@ class AccountsDb implements AutoCloseable { String[] selectionArgs = {account.name, account.type}; try (Cursor cursor = db.query(CE_TABLE_EXTRAS, COLUMNS_EXTRAS_KEY_AND_VALUE, SELECTION_USERDATA_BY_ACCOUNT, SELECTION_ACCOUNTS_ID_BY_ACCOUNT, selectionArgs, null, null, null)) { while (cursor.moveToNext()) { Loading Loading @@ -523,6 +526,8 @@ class AccountsDb implements AutoCloseable { createSharedAccountsTable(db); createAccountsDeletionTrigger(db); createDebugTable(db); createAccountsVisibilityTable(db); createAccountsDeletionVisibilityCleanupTrigger(db); } private void createSharedAccountsTable(SQLiteDatabase db) { Loading Loading @@ -551,6 +556,14 @@ class AccountsDb implements AutoCloseable { + "," + GRANTS_GRANTEE_UID + "))"); } private void createAccountsVisibilityTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_VISIBILITY + " ( " + VISIBILITY_ACCOUNTS_ID + " INTEGER NOT NULL, " + VISIBILITY_UID + " TEXT NOT NULL, " + VISIBILITY_VALUE + " INTEGER, " + "PRIMARY KEY(" + VISIBILITY_ACCOUNTS_ID + "," + VISIBILITY_UID + "))"); } static void createDebugTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( " + ACCOUNTS_ID + " INTEGER," Loading @@ -563,10 +576,26 @@ class AccountsDb implements AutoCloseable { + DEBUG_TABLE_TIMESTAMP + ")"); } private void createAccountsDeletionVisibilityCleanupTrigger(SQLiteDatabase db) { db.execSQL("" + " CREATE TRIGGER " + TABLE_ACCOUNTS + "DeleteVisibility DELETE ON " + TABLE_ACCOUNTS + " BEGIN" + " DELETE FROM " + TABLE_VISIBILITY + " WHERE " + VISIBILITY_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" + " END"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); if (oldVersion == 1) { createAccountsVisibilityTable(db); createAccountsDeletionVisibilityCleanupTrigger(db); ++oldVersion; } if (oldVersion != newVersion) { Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); } Loading Loading @@ -861,6 +890,84 @@ class AccountsDb implements AutoCloseable { new String[] {Integer.toString(uid)}) > 0; } boolean setAccountVisibility(long accountId, int uid, int visibility) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(VISIBILITY_ACCOUNTS_ID, String.valueOf(accountId)); values.put(VISIBILITY_UID, String.valueOf(uid)); values.put(VISIBILITY_VALUE, String.valueOf(visibility)); return (db.replace(TABLE_VISIBILITY, VISIBILITY_VALUE, values) != -1); } Integer findAccountVisibility(Account account, int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE}, SELECTION_ACCOUNTS_ID_BY_ACCOUNT + " AND " + VISIBILITY_UID + "=? ", new String[] {account.name, account.type, String.valueOf(uid)}, null, null, null); try { while (cursor.moveToNext()) { return cursor.getInt(0); } } finally { cursor.close(); } return null; } Integer findAccountVisibility(long accountId, int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? AND " + VISIBILITY_UID + "=? ", new String[] {String.valueOf(accountId), String.valueOf(uid)}, null, null, null); try { while (cursor.moveToNext()) { return cursor.getInt(0); } } finally { cursor.close(); } return null; } Account findDeAccountByAccountId(long accountId) { SQLiteDatabase db = mDeDatabase.getReadableDatabase(); final Cursor cursor = db.query(TABLE_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE}, ACCOUNTS_ID + "=? ", new String[] {String.valueOf(accountId)}, null, null, null); try { while (cursor.moveToNext()) { return new Account(cursor.getString(0), cursor.getString(1)); } } finally { cursor.close(); } return null; } /** * Returns a map from uid to visibility value. */ Map<Integer, Integer> findAccountVisibilityForAccountId(long accountId) { SQLiteDatabase db = mDeDatabase.getReadableDatabase(); Map<Integer, Integer> result = new HashMap<>(); final Cursor cursor = db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_UID, VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? ", new String[] {String.valueOf(accountId)}, null, null, null); try { while (cursor.moveToNext()) { result.put(cursor.getInt(0), cursor.getInt(1)); } } finally { cursor.close(); } return result; } boolean deleteAccountVisibilityForUid(int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); return db.delete(TABLE_VISIBILITY, VISIBILITY_UID + "=? ", new String[] {Integer.toString(uid)}) > 0; } long insertOrReplaceMetaAuthTypeAndUid(String authenticatorType, int uid) { SQLiteDatabase db = mDeDatabase.getWritableDatabase(); ContentValues values = new ContentValues(); Loading
services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java +62 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** Loading Loading @@ -338,4 +339,65 @@ public class AccountsDbTest { actualId = mAccountsDb.findDeAccountId(account); assertEquals(-1, actualId); } @Test public void testFindDeAccountByAccountId() { long accId = 10; Account account = new Account("name", "example.com"); assertNull(mAccountsDb.findDeAccountByAccountId(accId)); mAccountsDb.insertDeAccount(account, accId); Account foundAccount = mAccountsDb.findDeAccountByAccountId(accId); assertEquals(account, foundAccount); } @Test public void testVisibilityFindSetDelete() { long accId = 10; int uid1 = 100500; int uid2 = 100501; Account account = new Account("name", "example.com"); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); mAccountsDb.insertDeAccount(account, accId); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); assertNull(mAccountsDb.findAccountVisibility(accId, uid1)); mAccountsDb.setAccountVisibility(accId, uid1, 1); assertEquals(mAccountsDb.findAccountVisibility(account, uid1), Integer.valueOf(1)); assertEquals(mAccountsDb.findAccountVisibility(accId, uid1), Integer.valueOf(1)); mAccountsDb.setAccountVisibility(accId, uid2, 2); assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(2)); mAccountsDb.setAccountVisibility(accId, uid2, 3); assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(3)); Map<Integer, Integer> vis = mAccountsDb.findAccountVisibilityForAccountId(accId); assertEquals(vis.size(), 2); assertEquals(vis.get(uid1), Integer.valueOf(1)); assertEquals(vis.get(uid2), Integer.valueOf(3)); assertTrue(mAccountsDb.deleteAccountVisibilityForUid(uid1)); assertNull(mAccountsDb.findAccountVisibility(accId, uid1)); assertFalse(mAccountsDb.deleteAccountVisibilityForUid(uid1)); // Already deleted. } @Test public void testVisibilityCleanupTrigger() { long accId = 10; int uid1 = 100500; Account account = new Account("name", "example.com"); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); mAccountsDb.insertDeAccount(account, accId); assertNull(mAccountsDb.findAccountVisibility(account, uid1)); mAccountsDb.setAccountVisibility(accId, uid1, 1); assertEquals(mAccountsDb.findAccountVisibility(accId, uid1), Integer.valueOf(1)); assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility. assertNull(mAccountsDb.findAccountVisibility(account, uid1)); } }