diff --git a/api/test-current.txt b/api/test-current.txt index 06e755027a382..5a6262d8d43bd 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -684,7 +684,6 @@ package android.database.sqlite { method public static int getWALAutoCheckpoint(); method public static int getWALConnectionPoolSize(); method public static String getWALSyncMode(); - method public static boolean isCompatibilityWalSupported(); method public static int releaseMemory(); } diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java index 8ea1db25a9a74..a2690728d9bf7 100644 --- a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java +++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java @@ -40,8 +40,7 @@ public class SQLiteCompatibilityWalFlags { private static final String TAG = "SQLiteCompatibilityWalFlags"; private static volatile boolean sInitialized; - private static volatile boolean sFlagsSet; - private static volatile boolean sCompatibilityWalSupported; + private static volatile boolean sLegacyCompatibilityWalEnabled; private static volatile String sWALSyncMode; private static volatile long sTruncateSize = -1; // This flag is used to avoid recursive initialization due to circular dependency on Settings @@ -54,18 +53,9 @@ public class SQLiteCompatibilityWalFlags { * @hide */ @VisibleForTesting - public static boolean areFlagsSet() { + public static boolean isLegacyCompatibilityWalEnabled() { initIfNeeded(); - return sFlagsSet; - } - - /** - * @hide - */ - @VisibleForTesting - public static boolean isCompatibilityWalSupported() { - initIfNeeded(); - return sCompatibilityWalSupported; + return sLegacyCompatibilityWalEnabled; } /** @@ -74,6 +64,14 @@ public class SQLiteCompatibilityWalFlags { @VisibleForTesting public static String getWALSyncMode() { initIfNeeded(); + // The configurable WAL sync mode should only ever be used if the legacy compatibility + // WAL is enabled. It should *not* have any effect if app developers explicitly turn on + // WAL for their database using setWriteAheadLoggingEnabled. Throwing an exception here + // adds an extra layer of checking that we never use it in the wrong place. + if (!sLegacyCompatibilityWalEnabled) { + throw new IllegalStateException("isLegacyCompatibilityWalEnabled() == false"); + } + return sWALSyncMode; } @@ -131,13 +129,12 @@ public class SQLiteCompatibilityWalFlags { sInitialized = true; return; } - sCompatibilityWalSupported = parser.getBoolean("compatibility_wal_supported", - SQLiteGlobal.isCompatibilityWalSupported()); + sLegacyCompatibilityWalEnabled = parser.getBoolean( + "legacy_compatibility_wal_enabled", false); sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode()); sTruncateSize = parser.getInt("truncate_size", -1); - Log.i(TAG, "Read compatibility WAL flags: compatibility_wal_supported=" - + sCompatibilityWalSupported + ", wal_syncmode=" + sWALSyncMode); - sFlagsSet = true; + Log.i(TAG, "Read compatibility WAL flags: legacy_compatibility_wal_enabled=" + + sLegacyCompatibilityWalEnabled + ", wal_syncmode=" + sWALSyncMode); sInitialized = true; } @@ -148,8 +145,7 @@ public class SQLiteCompatibilityWalFlags { @TestApi public static void reset() { sInitialized = false; - sFlagsSet = false; - sCompatibilityWalSupported = false; + sLegacyCompatibilityWalEnabled = false; sWALSyncMode = null; } } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 3c8e236a78931..3844794da980c 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -300,12 +300,13 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; // Use compatibility WAL unless an app explicitly set journal/synchronous mode // or DISABLE_COMPATIBILITY_WAL flag is set - final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal(); - if (walEnabled || useCompatibilityWal) { + final boolean isCompatibilityWalEnabled = + mConfiguration.isLegacyCompatibilityWalEnabled(); + if (walEnabled || isCompatibilityWalEnabled) { setJournalMode("WAL"); if (mConfiguration.syncMode != null) { setSyncMode(mConfiguration.syncMode); - } else if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) { + } else if (isCompatibilityWalEnabled) { setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode()); } else { setSyncMode(SQLiteGlobal.getWALSyncMode()); @@ -504,7 +505,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen != mConfiguration.foreignKeyConstraintsEnabled; boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags) & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING - | SQLiteDatabase.DISABLE_COMPATIBILITY_WAL)) != 0; + | SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL)) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); // Update configuration parameters. diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index dbc176614daf7..852f8f2a4204a 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -321,7 +321,7 @@ public final class SQLiteConnectionPool implements Closeable { // We should do in-place switching when transitioning from compatibility WAL // to rollback journal. Otherwise transient connection state will be lost boolean onlyCompatWalChanged = (mConfiguration.openFlags ^ configuration.openFlags) - == SQLiteDatabase.DISABLE_COMPATIBILITY_WAL; + == SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL; if (!onlyCompatWalChanged && mConfiguration.openFlags != configuration.openFlags) { // If we are changing open flags and WAL mode at the same time, then @@ -1113,19 +1113,18 @@ public final class SQLiteConnectionPool implements Closeable { if (directories != null) { directories.add(new File(mConfiguration.path).getParent()); } + boolean isCompatibilityWalEnabled = mConfiguration.isLegacyCompatibilityWalEnabled(); printer.println("Connection pool for " + mConfiguration.path + ":"); printer.println(" Open: " + mIsOpen); printer.println(" Max connections: " + mMaxConnectionPoolSize); printer.println(" Total execution time: " + mTotalExecutionTimeCounter); printer.println(" Configuration: openFlags=" + mConfiguration.openFlags - + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal() + + ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode) + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode)); - if (SQLiteCompatibilityWalFlags.areFlagsSet()) { - printer.println(" Compatibility WAL settings: compatibility_wal_supported=" - + SQLiteCompatibilityWalFlags - .isCompatibilityWalSupported() + ", wal_syncmode=" + if (isCompatibilityWalEnabled) { + printer.println(" Compatibility WAL enabled: wal_syncmode=" + SQLiteCompatibilityWalFlags.getWALSyncMode()); } if (mConfiguration.isLookasideConfigSet()) { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index ae456afdc252f..dffbd89c33d2a 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -266,12 +266,17 @@ public final class SQLiteDatabase extends SQLiteClosable { */ public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; + + // Note: The below value was only used on Android Pie. + // public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000; + /** - * Open flag: Flag for {@link #openDatabase} to disable Compatibility WAL when opening database. + * Open flag: Flag for {@link #openDatabase} to enable the legacy Compatibility WAL when opening + * database. * * @hide */ - public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000; + public static final int ENABLE_LEGACY_COMPATIBILITY_WAL = 0x80000000; /** * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}. @@ -309,10 +314,8 @@ public final class SQLiteDatabase extends SQLiteClosable { mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs; mConfigurationLocked.journalMode = journalMode; mConfigurationLocked.syncMode = syncMode; - if (!SQLiteGlobal.isCompatibilityWalSupported() || ( - SQLiteCompatibilityWalFlags.areFlagsSet() && !SQLiteCompatibilityWalFlags - .isCompatibilityWalSupported())) { - mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; + if (SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()) { + mConfigurationLocked.openFlags |= ENABLE_LEGACY_COMPATIBILITY_WAL; } } @@ -2123,15 +2126,18 @@ public final class SQLiteDatabase extends SQLiteClosable { throwIfNotOpenLocked(); final int oldFlags = mConfigurationLocked.openFlags; - final boolean walDisabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0; - final boolean compatibilityWalDisabled = (oldFlags & DISABLE_COMPATIBILITY_WAL) != 0; - if (walDisabled && compatibilityWalDisabled) { + final boolean walEnabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0; + final boolean compatibilityWalEnabled = + (oldFlags & ENABLE_LEGACY_COMPATIBILITY_WAL) != 0; + // WAL was never enabled for this database, so there's nothing left to do. + if (!walEnabled && !compatibilityWalEnabled) { return; } + // If an app explicitly disables WAL, it takes priority over any directive + // to use the legacy "compatibility WAL" mode. mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING; - // If an app explicitly disables WAL, compatibility mode should be disabled too - mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; + mConfigurationLocked.openFlags &= ~ENABLE_LEGACY_COMPATIBILITY_WAL; try { mConnectionPoolLocked.reconfigure(mConfigurationLocked); diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 48f10219921bd..fcdaf0abafcb4 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -17,6 +17,7 @@ package android.database.sqlite; import android.annotation.UnsupportedAppUsage; + import java.util.ArrayList; import java.util.Locale; import java.util.regex.Pattern; @@ -197,9 +198,9 @@ public final class SQLiteDatabaseConfiguration { return path.equalsIgnoreCase(MEMORY_DB_PATH); } - boolean useCompatibilityWal() { + boolean isLegacyCompatibilityWalEnabled() { return journalMode == null && syncMode == null - && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0; + && (openFlags & SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL) != 0; } private static String stripPathForLogs(String path) { diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java index ff286fdb42df2..d796003395f58 100644 --- a/core/java/android/database/sqlite/SQLiteGlobal.java +++ b/core/java/android/database/sqlite/SQLiteGlobal.java @@ -90,16 +90,6 @@ public final class SQLiteGlobal { com.android.internal.R.string.db_default_journal_mode)); } - /** - * Returns true if compatibility WAL mode is supported. In this mode, only - * database journal mode is changed. Connection pool will use at most one connection. - */ - public static boolean isCompatibilityWalSupported() { - return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported", - Resources.getSystem().getBoolean( - com.android.internal.R.bool.db_compatibility_wal_supported)); - } - /** * Gets the journal size limit in bytes. */ diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 0e869c8c19fd1..8163c4d412a77 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -202,8 +202,9 @@ public abstract class SQLiteOpenHelper implements AutoCloseable { } mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled); } + // Compatibility WAL is disabled if an app disables or enables WAL - mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL); + mOpenParamsBuilder.removeOpenFlags(SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8acdc8c1255f3..10e4ebe430c1e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14200,10 +14200,24 @@ public final class Settings { * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF * - * Supported keys: - * compatibility_wal_supported (boolean) - * wal_syncmode (String) - * truncate_size (int) + * Supported keys:
+ *
  • + * + * + * + * + *
  • * * @hide */ diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index eb80423aa6a44..910af4c63630c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2008,11 +2008,6 @@ a transaction, so it interacts poorly with SECURE_DELETE. --> TRUNCATE - - true - diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a617aecc1ce64..426d813885fe1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -752,7 +752,6 @@ - diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java index 5dbcb3c46b501..82bd588ea9ffd 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java @@ -19,6 +19,7 @@ package android.database.sqlite; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; import android.database.DatabaseUtils; @@ -54,44 +55,52 @@ public class SQLiteCompatibilityWalFlagsTest { @Test public void testParseConfig() { SQLiteCompatibilityWalFlags.init(""); - assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); - SQLiteCompatibilityWalFlags.init(null); - assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); - SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=false,wal_syncmode=OFF"); - assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); - assertFalse(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); - assertEquals("OFF", SQLiteCompatibilityWalFlags.getWALSyncMode()); + // Ensure that legacy compatibility wal isn't turned on by the old flag. + SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=OFF"); + assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); + try { + SQLiteCompatibilityWalFlags.getWALSyncMode(); + fail(); + } catch (IllegalStateException expected) { + } assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize()); + SQLiteCompatibilityWalFlags.init("wal_syncmode=VALUE"); - assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); - assertEquals(SQLiteGlobal.isCompatibilityWalSupported(), - SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); - assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode()); + assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize()); + try { + SQLiteCompatibilityWalFlags.getWALSyncMode(); + fail(); + } catch (IllegalStateException expected) { + } - SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true"); - assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); + SQLiteCompatibilityWalFlags.init("legacy_compatibility_wal_enabled=true"); + assertTrue(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); assertEquals(SQLiteGlobal.getWALSyncMode(), SQLiteCompatibilityWalFlags.getWALSyncMode()); - assertTrue(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); - assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize()); + + SQLiteCompatibilityWalFlags.init( + "legacy_compatibility_wal_enabled=true,wal_syncmode=VALUE"); + assertTrue(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); + assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode()); SQLiteCompatibilityWalFlags.init("truncate_size=1024"); assertEquals(1024, SQLiteCompatibilityWalFlags.getTruncateSize()); SQLiteCompatibilityWalFlags.reset(); SQLiteCompatibilityWalFlags.init("Invalid value"); - assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); + assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); } @Test public void testApplyFlags() { Context ctx = InstrumentationRegistry.getContext(); - SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=NORMAL"); + SQLiteCompatibilityWalFlags.init( + "legacy_compatibility_wal_enabled=true,wal_syncmode=NORMAL"); mDatabase = SQLiteDatabase .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null); String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null); @@ -100,5 +109,22 @@ public class SQLiteCompatibilityWalFlagsTest { assertEquals("Normal mode (1) is expected", "1", syncMode); } + @Test + public void testApplyFlags_thenDisableWriteAheadLogging() { + Context ctx = InstrumentationRegistry.getContext(); + SQLiteCompatibilityWalFlags.init( + "legacy_compatibility_wal_enabled=true,wal_syncmode=FULL"); + mDatabase = SQLiteDatabase + .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null); + + mDatabase.disableWriteAheadLogging(); + String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null); + assertEquals(SQLiteGlobal.getDefaultJournalMode(), journalMode.toUpperCase()); + String syncMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA synchronous", null); + // TODO: This is the old behaviour and seems incorrect. The specified wal_syncmode was only + // intended to be used if the database is in WAL mode, and we should revert to the global + // default sync mode if WAL is disabled. + assertEquals("Normal mode (2) is expected", "2", syncMode); + } }