Loading core/java/android/database/sqlite/SQLiteDatabase.java +0 −107 Original line number Diff line number Diff line Loading @@ -40,15 +40,12 @@ import android.os.Looper; import android.os.OperationCanceledException; import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Printer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import dalvik.annotation.optimization.NeverCompile; Loading Loading @@ -106,14 +103,8 @@ public final class SQLiteDatabase extends SQLiteClosable { // Stores reference to all databases opened in the current process. // (The referent Object is not used at this time.) // INVARIANT: Guarded by sActiveDatabases. @GuardedBy("sActiveDatabases") private static WeakHashMap<SQLiteDatabase, Object> sActiveDatabases = new WeakHashMap<>(); // Tracks which database files are currently open. If a database file is opened more than // once at any given moment, the associated databases are marked as "concurrent". @GuardedBy("sActiveDatabases") private static final OpenTracker sOpenTracker = new OpenTracker(); // Thread-local for database sessions that belong to this database. // Each thread has its own database session. // INVARIANT: Immutable. Loading Loading @@ -519,7 +510,6 @@ public final class SQLiteDatabase extends SQLiteClosable { private void dispose(boolean finalized) { final SQLiteConnectionPool pool; final String path; synchronized (mLock) { if (mCloseGuardLocked != null) { if (finalized) { Loading @@ -530,12 +520,10 @@ public final class SQLiteDatabase extends SQLiteClosable { pool = mConnectionPoolLocked; mConnectionPoolLocked = null; path = isInMemoryDatabase() ? null : getPath(); } if (!finalized) { synchronized (sActiveDatabases) { sOpenTracker.close(path); sActiveDatabases.remove(this); } Loading Loading @@ -1144,74 +1132,6 @@ public final class SQLiteDatabase extends SQLiteClosable { } } /** * Track the number of times a database file has been opened. There is a primary connection * associated with every open database, and these can contend with each other, leading to * unexpected SQLiteDatabaseLockedException exceptions. The tracking here is only advisory: * multiply-opened databases are logged but no other action is taken. * * This class is not thread-safe. */ private static class OpenTracker { // The list of currently-open databases. This maps the database file to the number of // currently-active opens. private final ArrayMap<String, Integer> mOpens = new ArrayMap<>(); // The maximum number of concurrently open database paths that will be stored. Once this // many paths have been recorded, further paths are logged but not saved. private static final int MAX_RECORDED_PATHS = 20; // The list of databases that were ever concurrently opened. private final ArraySet<String> mConcurrent = new ArraySet<>(); /** Return the canonical path. On error, just return the input path. */ private static String normalize(String path) { try { return new File(path).toPath().toRealPath().toString(); } catch (Exception e) { // If there is an IO or security exception, just continue, using the input path. return path; } } /** Return true if the path is currently open in another SQLiteDatabase instance. */ void open(@Nullable String path) { if (path == null) return; path = normalize(path); Integer count = mOpens.get(path); if (count == null || count == 0) { mOpens.put(path, 1); return; } else { mOpens.put(path, count + 1); if (mConcurrent.size() < MAX_RECORDED_PATHS) { mConcurrent.add(path); } Log.w(TAG, "multiple primary connections on " + path); return; } } void close(@Nullable String path) { if (path == null) return; path = normalize(path); Integer count = mOpens.get(path); if (count == null || count <= 0) { Log.e(TAG, "open database counting failure on " + path); } else if (count == 1) { // Implicitly set the count to zero, and make mOpens smaller. mOpens.remove(path); } else { mOpens.put(path, count - 1); } } ArraySet<String> getConcurrentDatabasePaths() { return new ArraySet<>(mConcurrent); } } private void open() { try { try { Loading @@ -1233,17 +1153,14 @@ public final class SQLiteDatabase extends SQLiteClosable { } private void openInner() { final String path; synchronized (mLock) { assert mConnectionPoolLocked == null; mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked); mCloseGuardLocked.open("close"); path = isInMemoryDatabase() ? null : getPath(); } synchronized (sActiveDatabases) { sActiveDatabases.put(this, null); sOpenTracker.open(path); } } Loading Loading @@ -2427,17 +2344,6 @@ public final class SQLiteDatabase extends SQLiteClosable { } } /** * Return list of databases that have been concurrently opened. * @hide */ @VisibleForTesting public static ArraySet<String> getConcurrentDatabasePaths() { synchronized (sActiveDatabases) { return sOpenTracker.getConcurrentDatabasePaths(); } } /** * Returns true if the new version code is greater than the current database version. * Loading Loading @@ -2860,19 +2766,6 @@ public final class SQLiteDatabase extends SQLiteClosable { dumpDatabaseDirectory(printer, new File(dir), isSystem); } } // Dump concurrently-opened database files, if any final ArraySet<String> concurrent; synchronized (sActiveDatabases) { concurrent = sOpenTracker.getConcurrentDatabasePaths(); } if (concurrent.size() > 0) { printer.println(""); printer.println("Concurrently opened database files"); for (String f : concurrent) { printer.println(" " + f); } } } private static void dumpDatabaseDirectory(Printer pw, File dir, boolean isSystem) { Loading core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +0 −37 Original line number Diff line number Diff line Loading @@ -403,41 +403,4 @@ public class SQLiteDatabaseTest { } assertFalse(allowed); } /** Return true if the path is in the list of strings. */ private boolean isConcurrent(String path) throws Exception { path = new File(path).toPath().toRealPath().toString(); return SQLiteDatabase.getConcurrentDatabasePaths().contains(path); } @Test public void testDuplicateDatabases() throws Exception { // The two database paths in this test are assumed not to have been opened earlier in this // process. // A database path that will be opened twice. final String dbName = "never-used-db.db"; final File dbFile = mContext.getDatabasePath(dbName); final String dbPath = dbFile.getPath(); // A database path that will be opened only once. final String okName = "never-used-ok.db"; final File okFile = mContext.getDatabasePath(okName); final String okPath = okFile.getPath(); SQLiteDatabase db1 = SQLiteDatabase.openOrCreateDatabase(dbFile, null); assertFalse(isConcurrent(dbPath)); SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase(dbFile, null); assertTrue(isConcurrent(dbPath)); db1.close(); assertTrue(isConcurrent(dbPath)); db2.close(); assertTrue(isConcurrent(dbPath)); SQLiteDatabase db3 = SQLiteDatabase.openOrCreateDatabase(okFile, null); db3.close(); db3 = SQLiteDatabase.openOrCreateDatabase(okFile, null); assertFalse(isConcurrent(okPath)); db3.close(); } } Loading
core/java/android/database/sqlite/SQLiteDatabase.java +0 −107 Original line number Diff line number Diff line Loading @@ -40,15 +40,12 @@ import android.os.Looper; import android.os.OperationCanceledException; import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Printer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import dalvik.annotation.optimization.NeverCompile; Loading Loading @@ -106,14 +103,8 @@ public final class SQLiteDatabase extends SQLiteClosable { // Stores reference to all databases opened in the current process. // (The referent Object is not used at this time.) // INVARIANT: Guarded by sActiveDatabases. @GuardedBy("sActiveDatabases") private static WeakHashMap<SQLiteDatabase, Object> sActiveDatabases = new WeakHashMap<>(); // Tracks which database files are currently open. If a database file is opened more than // once at any given moment, the associated databases are marked as "concurrent". @GuardedBy("sActiveDatabases") private static final OpenTracker sOpenTracker = new OpenTracker(); // Thread-local for database sessions that belong to this database. // Each thread has its own database session. // INVARIANT: Immutable. Loading Loading @@ -519,7 +510,6 @@ public final class SQLiteDatabase extends SQLiteClosable { private void dispose(boolean finalized) { final SQLiteConnectionPool pool; final String path; synchronized (mLock) { if (mCloseGuardLocked != null) { if (finalized) { Loading @@ -530,12 +520,10 @@ public final class SQLiteDatabase extends SQLiteClosable { pool = mConnectionPoolLocked; mConnectionPoolLocked = null; path = isInMemoryDatabase() ? null : getPath(); } if (!finalized) { synchronized (sActiveDatabases) { sOpenTracker.close(path); sActiveDatabases.remove(this); } Loading Loading @@ -1144,74 +1132,6 @@ public final class SQLiteDatabase extends SQLiteClosable { } } /** * Track the number of times a database file has been opened. There is a primary connection * associated with every open database, and these can contend with each other, leading to * unexpected SQLiteDatabaseLockedException exceptions. The tracking here is only advisory: * multiply-opened databases are logged but no other action is taken. * * This class is not thread-safe. */ private static class OpenTracker { // The list of currently-open databases. This maps the database file to the number of // currently-active opens. private final ArrayMap<String, Integer> mOpens = new ArrayMap<>(); // The maximum number of concurrently open database paths that will be stored. Once this // many paths have been recorded, further paths are logged but not saved. private static final int MAX_RECORDED_PATHS = 20; // The list of databases that were ever concurrently opened. private final ArraySet<String> mConcurrent = new ArraySet<>(); /** Return the canonical path. On error, just return the input path. */ private static String normalize(String path) { try { return new File(path).toPath().toRealPath().toString(); } catch (Exception e) { // If there is an IO or security exception, just continue, using the input path. return path; } } /** Return true if the path is currently open in another SQLiteDatabase instance. */ void open(@Nullable String path) { if (path == null) return; path = normalize(path); Integer count = mOpens.get(path); if (count == null || count == 0) { mOpens.put(path, 1); return; } else { mOpens.put(path, count + 1); if (mConcurrent.size() < MAX_RECORDED_PATHS) { mConcurrent.add(path); } Log.w(TAG, "multiple primary connections on " + path); return; } } void close(@Nullable String path) { if (path == null) return; path = normalize(path); Integer count = mOpens.get(path); if (count == null || count <= 0) { Log.e(TAG, "open database counting failure on " + path); } else if (count == 1) { // Implicitly set the count to zero, and make mOpens smaller. mOpens.remove(path); } else { mOpens.put(path, count - 1); } } ArraySet<String> getConcurrentDatabasePaths() { return new ArraySet<>(mConcurrent); } } private void open() { try { try { Loading @@ -1233,17 +1153,14 @@ public final class SQLiteDatabase extends SQLiteClosable { } private void openInner() { final String path; synchronized (mLock) { assert mConnectionPoolLocked == null; mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked); mCloseGuardLocked.open("close"); path = isInMemoryDatabase() ? null : getPath(); } synchronized (sActiveDatabases) { sActiveDatabases.put(this, null); sOpenTracker.open(path); } } Loading Loading @@ -2427,17 +2344,6 @@ public final class SQLiteDatabase extends SQLiteClosable { } } /** * Return list of databases that have been concurrently opened. * @hide */ @VisibleForTesting public static ArraySet<String> getConcurrentDatabasePaths() { synchronized (sActiveDatabases) { return sOpenTracker.getConcurrentDatabasePaths(); } } /** * Returns true if the new version code is greater than the current database version. * Loading Loading @@ -2860,19 +2766,6 @@ public final class SQLiteDatabase extends SQLiteClosable { dumpDatabaseDirectory(printer, new File(dir), isSystem); } } // Dump concurrently-opened database files, if any final ArraySet<String> concurrent; synchronized (sActiveDatabases) { concurrent = sOpenTracker.getConcurrentDatabasePaths(); } if (concurrent.size() > 0) { printer.println(""); printer.println("Concurrently opened database files"); for (String f : concurrent) { printer.println(" " + f); } } } private static void dumpDatabaseDirectory(Printer pw, File dir, boolean isSystem) { Loading
core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +0 −37 Original line number Diff line number Diff line Loading @@ -403,41 +403,4 @@ public class SQLiteDatabaseTest { } assertFalse(allowed); } /** Return true if the path is in the list of strings. */ private boolean isConcurrent(String path) throws Exception { path = new File(path).toPath().toRealPath().toString(); return SQLiteDatabase.getConcurrentDatabasePaths().contains(path); } @Test public void testDuplicateDatabases() throws Exception { // The two database paths in this test are assumed not to have been opened earlier in this // process. // A database path that will be opened twice. final String dbName = "never-used-db.db"; final File dbFile = mContext.getDatabasePath(dbName); final String dbPath = dbFile.getPath(); // A database path that will be opened only once. final String okName = "never-used-ok.db"; final File okFile = mContext.getDatabasePath(okName); final String okPath = okFile.getPath(); SQLiteDatabase db1 = SQLiteDatabase.openOrCreateDatabase(dbFile, null); assertFalse(isConcurrent(dbPath)); SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase(dbFile, null); assertTrue(isConcurrent(dbPath)); db1.close(); assertTrue(isConcurrent(dbPath)); db2.close(); assertTrue(isConcurrent(dbPath)); SQLiteDatabase db3 = SQLiteDatabase.openOrCreateDatabase(okFile, null); db3.close(); db3 = SQLiteDatabase.openOrCreateDatabase(okFile, null); assertFalse(isConcurrent(okPath)); db3.close(); } }