summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java140
-rw-r--r--core/java/android/database/sqlite/flags.aconfig9
2 files changed, 94 insertions, 55 deletions
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 78c8954cfe5f..88d69b665c87 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -25,10 +25,15 @@ import android.database.DatabaseErrorHandler;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.FileUtils;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.File;
+import java.io.IOException;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
/**
* A helper class to manage database creation and version management.
@@ -54,6 +59,13 @@ import java.util.Objects;
public abstract class SQLiteOpenHelper implements AutoCloseable {
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
+ // Every database file has a lock, saved in this map. The lock is held while the database is
+ // opened.
+ private static final ConcurrentHashMap<String, Object> sDbLock = new ConcurrentHashMap<>();
+
+ // The lock that this open helper instance must hold when the database is opened.
+ private final Object mLock;
+
private final Context mContext;
@UnsupportedAppUsage
private final String mName;
@@ -168,6 +180,21 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
mNewVersion = version;
mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
setOpenParamsBuilder(openParamsBuilder);
+
+ Object lock = null;
+ if (mName == null || !Flags.concurrentOpenHelper()) {
+ lock = new Object();
+ } else {
+ try {
+ final String path = mContext.getDatabasePath(mName).getCanonicalPath();
+ lock = sDbLock.computeIfAbsent(path, (String k) -> new Object());
+ } catch (IOException e) {
+ Log.d(TAG, "failed to construct db path for " + mName);
+ // Ensure the lock is not null.
+ lock = new Object();
+ }
+ }
+ mLock = lock;
}
/**
@@ -358,74 +385,77 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
SQLiteDatabase db = mDatabase;
try {
- mIsInitializing = true;
+ synchronized (mLock) {
+ mIsInitializing = true;
- if (db != null) {
- if (writable && db.isReadOnly()) {
- db.reopenReadWrite();
- }
- } else if (mName == null) {
- db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
- } else {
- final File filePath = mContext.getDatabasePath(mName);
- SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
- try {
- db = SQLiteDatabase.openDatabase(filePath, params);
- // Keep pre-O-MR1 behavior by resetting file permissions to 660
- setFilePermissionsForDb(filePath.getPath());
- } catch (SQLException ex) {
- if (writable) {
- throw ex;
+ if (db != null) {
+ if (writable && db.isReadOnly()) {
+ db.reopenReadWrite();
+ }
+ } else if (mName == null) {
+ db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
+ } else {
+ final File filePath = mContext.getDatabasePath(mName);
+ SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
+ try {
+ db = SQLiteDatabase.openDatabase(filePath, params);
+ // Keep pre-O-MR1 behavior by resetting file permissions to 660
+ setFilePermissionsForDb(filePath.getPath());
+ } catch (SQLException ex) {
+ if (writable) {
+ throw ex;
+ }
+ Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
+ params = params.toBuilder()
+ .addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
+ db = SQLiteDatabase.openDatabase(filePath, params);
}
- Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
- params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
- db = SQLiteDatabase.openDatabase(filePath, params);
}
- }
-
- onConfigure(db);
- final int version = db.getVersion();
- if (version != mNewVersion) {
- if (db.isReadOnly()) {
- throw new SQLiteException("Can't upgrade read-only database from version " +
- db.getVersion() + " to " + mNewVersion + ": " + mName);
- }
+ onConfigure(db);
- if (version > 0 && version < mMinimumSupportedVersion) {
- File databaseFile = new File(db.getPath());
- onBeforeDelete(db);
- db.close();
- if (SQLiteDatabase.deleteDatabase(databaseFile)) {
- mIsInitializing = false;
- return getDatabaseLocked(writable);
- } else {
- throw new IllegalStateException("Unable to delete obsolete database "
- + mName + " with version " + version);
+ final int version = db.getVersion();
+ if (version != mNewVersion) {
+ if (db.isReadOnly()) {
+ throw new SQLiteException("Can't upgrade read-only database from version "
+ + db.getVersion() + " to " + mNewVersion + ": " + mName);
}
- } else {
- db.beginTransaction();
- try {
- if (version == 0) {
- onCreate(db);
+
+ if (version > 0 && version < mMinimumSupportedVersion) {
+ File databaseFile = new File(db.getPath());
+ onBeforeDelete(db);
+ db.close();
+ if (SQLiteDatabase.deleteDatabase(databaseFile)) {
+ mIsInitializing = false;
+ return getDatabaseLocked(writable);
} else {
- if (version > mNewVersion) {
- onDowngrade(db, version, mNewVersion);
+ throw new IllegalStateException("Unable to delete obsolete database "
+ + mName + " with version " + version);
+ }
+ } else {
+ db.beginTransaction();
+ try {
+ if (version == 0) {
+ onCreate(db);
} else {
- onUpgrade(db, version, mNewVersion);
+ if (version > mNewVersion) {
+ onDowngrade(db, version, mNewVersion);
+ } else {
+ onUpgrade(db, version, mNewVersion);
+ }
}
+ db.setVersion(mNewVersion);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
}
- db.setVersion(mNewVersion);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
}
}
- }
- onOpen(db);
- mDatabase = db;
- return db;
+ onOpen(db);
+ mDatabase = db;
+ return db;
+ }
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index d43a66904af8..1d17a51f3653 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -17,3 +17,12 @@ flag {
description: "SQLite APIs held back for Android 15"
bug: "279043253"
}
+
+flag {
+ name: "concurrent_open_helper"
+ is_exported: true
+ namespace: "system_performance"
+ is_fixed_read_only: false
+ description: "Make SQLiteOpenHelper thread-safe"
+ bug: "335904370"
+}