Merge "Lazely initialize AccountManager debug table."

am: 18cccec624

Change-Id: If7e12145dfa8e104777956bdab9a743f3f860e24
This commit is contained in:
Dmitry Dementyev
2019-02-19 12:23:26 -08:00
committed by android-build-merger
3 changed files with 135 additions and 59 deletions

View File

@@ -241,9 +241,6 @@ public class AccountManagerService
private final HashMap<Account, AtomicReference<String>> previousNameCache = private final HashMap<Account, AtomicReference<String>> previousNameCache =
new HashMap<Account, AtomicReference<String>>(); new HashMap<Account, AtomicReference<String>>();
private int debugDbInsertionPoint = -1;
private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) { UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
this.userId = userId; this.userId = userId;
synchronized (dbLock) { synchronized (dbLock) {
@@ -1299,7 +1296,6 @@ public class AccountManagerService
File preNDbFile = new File(mInjector.getPreNDatabaseName(userId)); File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
File deDbFile = new File(mInjector.getDeDatabaseName(userId)); File deDbFile = new File(mInjector.getDeDatabaseName(userId));
accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile); accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
mUsers.append(userId, accounts); mUsers.append(userId, accounts);
purgeOldGrants(accounts); purgeOldGrants(accounts);
validateAccounts = true; validateAccounts = true;
@@ -1399,7 +1395,7 @@ public class AccountManagerService
if (accounts != null) { if (accounts != null) {
synchronized (accounts.dbLock) { synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) { synchronized (accounts.cacheLock) {
accounts.statementForLogging.close(); accounts.accountsDb.closeDebugStatement();
accounts.accountsDb.close(); accounts.accountsDb.close();
} }
} }
@@ -5121,7 +5117,11 @@ public class AccountManagerService
@Override @Override
public void run() { public void run() {
SQLiteStatement logStatement = userAccount.statementForLogging; synchronized (userAccount.accountsDb.mDebugStatementLock) {
SQLiteStatement logStatement = userAccount.accountsDb.getStatementForLogging();
if (logStatement == null) {
return; // Can't log.
}
logStatement.bindLong(1, accountId); logStatement.bindLong(1, accountId);
logStatement.bindString(2, action); logStatement.bindString(2, action);
logStatement.bindString(3, mDateFormat.format(new Date())); logStatement.bindString(3, mDateFormat.format(new Date()));
@@ -5140,22 +5140,13 @@ public class AccountManagerService
} }
} }
} }
}
long insertionPoint = userAccount.accountsDb.reserveDebugDbInsertionPoint();
if (insertionPoint != -1) {
LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount, LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
callingUid, userAccount.debugDbInsertionPoint); callingUid, insertionPoint);
userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
% AccountsDb.MAX_DEBUG_DB_SIZE;
mHandler.post(logTask); mHandler.post(logTask);
} }
/*
* This should only be called once to compile the sql statement for logging
* and to find the insertion point.
*/
private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
userAccount.debugDbInsertionPoint = userAccount.accountsDb
.calculateDebugTableInsertionPoint();
userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
} }
public IBinder onBind(@SuppressWarnings("unused") Intent intent) { public IBinder onBind(@SuppressWarnings("unused") Intent intent) {

View File

@@ -17,11 +17,13 @@
package com.android.server.accounts; package com.android.server.accounts;
import android.accounts.Account; import android.accounts.Account;
import android.annotation.Nullable;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils; import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement; import android.database.sqlite.SQLiteStatement;
import android.os.FileUtils; import android.os.FileUtils;
@@ -56,7 +58,6 @@ class AccountsDb implements AutoCloseable {
private static final int CE_DATABASE_VERSION = 10; private static final int CE_DATABASE_VERSION = 10;
private static final int DE_DATABASE_VERSION = 3; // Added visibility support in O private static final int DE_DATABASE_VERSION = 3; // Added visibility support in O
static final String TABLE_ACCOUNTS = "accounts"; static final String TABLE_ACCOUNTS = "accounts";
private static final String ACCOUNTS_ID = "_id"; private static final String ACCOUNTS_ID = "_id";
private static final String ACCOUNTS_NAME = "name"; private static final String ACCOUNTS_NAME = "name";
@@ -183,6 +184,10 @@ class AccountsDb implements AutoCloseable {
private final Context mContext; private final Context mContext;
private final File mPreNDatabaseFile; private final File mPreNDatabaseFile;
final Object mDebugStatementLock = new Object();
private volatile long mDebugDbInsertionPoint = -1;
private volatile SQLiteStatement mDebugStatementForLogging; // not thread safe.
AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) { AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) {
mDeDatabase = deDatabase; mDeDatabase = deDatabase;
mContext = context; mContext = context;
@@ -1278,7 +1283,8 @@ class AccountsDb implements AutoCloseable {
* Finds the row key where the next insertion should take place. Returns number of rows * 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. * if it is less {@link #MAX_DEBUG_DB_SIZE}, otherwise finds the lowest number available.
*/ */
int calculateDebugTableInsertionPoint() { long calculateDebugTableInsertionPoint() {
try {
SQLiteDatabase db = mDeDatabase.getReadableDatabase(); SQLiteDatabase db = mDeDatabase.getReadableDatabase();
String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG; String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
@@ -1288,21 +1294,61 @@ class AccountsDb implements AutoCloseable {
// This query finds the smallest timestamp value (and if 2 records have // This query finds the smallest timestamp value (and if 2 records have
// same timestamp, the choose the lower id). // same timestamp, the choose the lower id).
queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY + queryCountDebugDbRows =
" FROM " + TABLE_DEBUG + "SELECT " + DEBUG_TABLE_KEY
" ORDER BY " + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY + + " FROM " + TABLE_DEBUG
" LIMIT 1"; + " ORDER BY " + DEBUG_TABLE_TIMESTAMP + ","
return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null); + DEBUG_TABLE_KEY
+ " LIMIT 1";
return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
} catch (SQLiteException e) {
Log.e(TAG, "Failed to open debug table" + e);
return -1;
}
} }
SQLiteStatement compileSqlStatementForLogging() { SQLiteStatement compileSqlStatementForLogging() {
// TODO b/31708085 Fix debug logging - it eagerly opens database for write without a need
SQLiteDatabase db = mDeDatabase.getWritableDatabase(); SQLiteDatabase db = mDeDatabase.getWritableDatabase();
String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
+ " VALUES (?,?,?,?,?,?)"; + " VALUES (?,?,?,?,?,?)";
return db.compileStatement(sql); return db.compileStatement(sql);
} }
/**
* Returns statement for logging or {@code null} on database open failure.
* Returned value must be guarded by {link #debugStatementLock}
*/
@Nullable SQLiteStatement getStatementForLogging() {
if (mDebugStatementForLogging != null) {
return mDebugStatementForLogging;
}
try {
mDebugStatementForLogging = compileSqlStatementForLogging();
return mDebugStatementForLogging;
} catch (SQLiteException e) {
Log.e(TAG, "Failed to open debug table" + e);
return null;
}
}
void closeDebugStatement() {
synchronized (mDebugStatementLock) {
if (mDebugStatementForLogging != null) {
mDebugStatementForLogging.close();
mDebugStatementForLogging = null;
}
}
}
long reserveDebugDbInsertionPoint() {
if (mDebugDbInsertionPoint == -1) {
mDebugDbInsertionPoint = calculateDebugTableInsertionPoint();
return mDebugDbInsertionPoint;
}
mDebugDbInsertionPoint = (mDebugDbInsertionPoint + 1) % MAX_DEBUG_DB_SIZE;
return mDebugDbInsertionPoint;
}
void dumpDebugTable(PrintWriter pw) { void dumpDebugTable(PrintWriter pw) {
SQLiteDatabase db = mDeDatabase.getReadableDatabase(); SQLiteDatabase db = mDeDatabase.getReadableDatabase();
Cursor cursor = db.query(TABLE_DEBUG, null, Cursor cursor = db.query(TABLE_DEBUG, null,

View File

@@ -17,12 +17,21 @@
package com.android.server.accounts; package com.android.server.accounts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.accounts.Account; import android.accounts.Account;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteStatement;
import android.os.Build;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.os.Build;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair; import android.util.Pair;
@@ -30,17 +39,15 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File; import java.io.File;
import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; 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;
/** /**
* Tests for {@link AccountsDb}. * Tests for {@link AccountsDb}.
* <p>Run with:<pre> * <p>Run with:<pre>
@@ -63,8 +70,11 @@ public class AccountsDbTest {
private File deDb; private File deDb;
private File ceDb; private File ceDb;
@Mock private PrintWriter mMockWriter;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext(); Context context = InstrumentationRegistry.getContext();
preNDb = new File(context.getCacheDir(), PREN_DB); preNDb = new File(context.getCacheDir(), PREN_DB);
ceDb = new File(context.getCacheDir(), CE_DB); ceDb = new File(context.getCacheDir(), CE_DB);
@@ -443,4 +453,33 @@ public class AccountsDbTest {
assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility. assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility.
assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); assertNull(mAccountsDb.findAccountVisibility(account, packageName1));
} }
@Test
public void testDumpDebugTable() {
long accId = 10;
long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint();
SQLiteStatement logStatement = mAccountsDb.getStatementForLogging();
logStatement.bindLong(1, accId);
logStatement.bindString(2, "action");
logStatement.bindString(3, "date");
logStatement.bindLong(4, 10);
logStatement.bindString(5, "table");
logStatement.bindLong(6, insertionPoint);
logStatement.execute();
mAccountsDb.dumpDebugTable(mMockWriter);
verify(mMockWriter, times(3)).println(anyString());
}
@Test
public void testReserveDebugDbInsertionPoint() {
long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint();
long insertionPoint2 = mAccountsDb.reserveDebugDbInsertionPoint();
assertEquals(0, insertionPoint);
assertEquals(1, insertionPoint2);
}
} }