Support enabling WAL using a flag when DB is opened.

Using enableWriteAheadLogging() to enable WAL is inefficient because
we previously disabled WAL mode when the database was opened.
Switching from WAL to PERSIST then back to WAL is inefficient
and could slow down application launch time.  It would be better
to leave the database in WAL mode when we open it to begin with.

To do that, we need to know ahead of time whether we will want to
have WAL enabled for the newly opened database.

Using this flag also reduces the chance that we will encounter
an error enabling WAL mode due to there being other open connections
to the database.

Bug: 6124556
Change-Id: I38ec7a528baeda9f1ef77e25e88b3ca4b6296200
diff --git a/api/current.txt b/api/current.txt
index a175f8a..4398ea3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5204,6 +5204,7 @@
     field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     field public static final java.lang.String LOCATION_SERVICE = "location";
     field public static final int MODE_APPEND = 32768; // 0x8000
+    field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final int MODE_MULTI_PROCESS = 4; // 0x4
     field public static final int MODE_PRIVATE = 0; // 0x0
     field public static final int MODE_WORLD_READABLE = 1; // 0x1
@@ -7334,6 +7335,7 @@
     method public deprecated boolean isDbLockedByOtherThreads();
     method public boolean isOpen();
     method public boolean isReadOnly();
+    method public boolean isWriteAheadLoggingEnabled();
     method public deprecated void markTableSyncable(java.lang.String, java.lang.String);
     method public deprecated void markTableSyncable(java.lang.String, java.lang.String, java.lang.String);
     method public boolean needUpgrade(int);
@@ -7375,6 +7377,7 @@
     field public static final int CONFLICT_REPLACE = 5; // 0x5
     field public static final int CONFLICT_ROLLBACK = 1; // 0x1
     field public static final int CREATE_IF_NECESSARY = 268435456; // 0x10000000
+    field public static final int ENABLE_WRITE_AHEAD_LOGGING = 536870912; // 0x20000000
     field public static final int MAX_SQL_CACHE_SIZE = 100; // 0x64
     field public static final int NO_LOCALIZED_COLLATORS = 16; // 0x10
     field public static final int OPEN_READONLY = 1; // 0x1
@@ -7438,6 +7441,7 @@
     method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
     method public void onOpen(android.database.sqlite.SQLiteDatabase);
     method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int);
+    method public void setWriteAheadLoggingEnabled(boolean);
   }
 
   public class SQLiteOutOfMemoryException extends android.database.sqlite.SQLiteException {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7043a73..d758ecae 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -766,17 +766,18 @@
 
     @Override
     public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
-        File f = validateFilePath(name, true);
-        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory);
-        setFilePermissionsFromMode(f.getPath(), mode, 0);
-        return db;
+        return openOrCreateDatabase(name, mode, factory, null);
     }
 
     @Override
     public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
             DatabaseErrorHandler errorHandler) {
         File f = validateFilePath(name, true);
-        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(), factory, errorHandler);
+        int flags = SQLiteDatabase.CREATE_IF_NECESSARY;
+        if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+            flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
+        }
+        SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
         setFilePermissionsFromMode(f.getPath(), mode, 0);
         return db;
     }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 741a6e9..2902504 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -99,6 +99,16 @@
     public static final int MODE_MULTI_PROCESS = 0x0004;
 
     /**
+     * Database open flag: when set, the database is opened with write-ahead
+     * logging enabled by default.
+     *
+     * @see #openOrCreateDatabase(String, int, CursorFactory)
+     * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
+     * @see SQLiteDatabase#enableWriteAheadLogging
+     */
+    public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;
+
+    /**
      * Flag for {@link #bindService}: automatically create the service as long
      * as the binding exists.  Note that while this will create the service,
      * its {@link android.app.Service#onStartCommand}
@@ -691,6 +701,7 @@
      * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
      *     default operation, {@link #MODE_WORLD_READABLE}
      *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+     *     Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
      * @param factory An optional factory class that is called to instantiate a
      *     cursor when query is called.
      *
@@ -700,6 +711,7 @@
      * @see #MODE_PRIVATE
      * @see #MODE_WORLD_READABLE
      * @see #MODE_WORLD_WRITEABLE
+     * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
      * @see #deleteDatabase
      */
     public abstract SQLiteDatabase openOrCreateDatabase(String name,
@@ -716,6 +728,7 @@
      * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
      *     default operation, {@link #MODE_WORLD_READABLE}
      *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+     *     Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
      * @param factory An optional factory class that is called to instantiate a
      *     cursor when query is called.
      * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
@@ -726,6 +739,7 @@
      * @see #MODE_PRIVATE
      * @see #MODE_WORLD_READABLE
      * @see #MODE_WORLD_WRITEABLE
+     * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
      * @see #deleteDatabase
      */
     public abstract SQLiteDatabase openOrCreateDatabase(String name,
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index bf10bcb..e999316 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -269,7 +269,7 @@
 
     private void setWalModeFromConfiguration() {
         if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
-            if (mConfiguration.walEnabled) {
+            if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
                 setJournalMode("WAL");
                 setSyncMode(SQLiteGlobal.getWALSyncMode());
             } else {
@@ -389,7 +389,8 @@
         }
 
         // Remember what changed.
-        boolean walModeChanged = configuration.walEnabled != mConfiguration.walEnabled;
+        boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
+                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 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 27c9ee5..0538ce4 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -81,6 +81,7 @@
     private final Object mLock = new Object();
     private final AtomicBoolean mConnectionLeaked = new AtomicBoolean();
     private final SQLiteDatabaseConfiguration mConfiguration;
+    private int mMaxConnectionPoolSize;
     private boolean mIsOpen;
     private int mNextConnectionId;
 
@@ -146,6 +147,7 @@
 
     private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
         mConfiguration = new SQLiteDatabaseConfiguration(configuration);
+        setMaxConnectionPoolSizeLocked();
     }
 
     @Override
@@ -257,8 +259,9 @@
         synchronized (mLock) {
             throwIfClosedLocked();
 
-            boolean restrictToOneConnection = false;
-            if (mConfiguration.walEnabled != configuration.walEnabled) {
+            boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
+                    & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+            if (walModeChanged) {
                 // WAL mode can only be changed if there are no acquired connections
                 // because we need to close all but the primary connection first.
                 if (!mAcquiredConnections.isEmpty()) {
@@ -272,15 +275,13 @@
                 // because none of them are in use.
                 closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
                 assert mAvailableNonPrimaryConnections.isEmpty();
-
-                restrictToOneConnection = true;
             }
 
             if (mConfiguration.openFlags != configuration.openFlags) {
                 // If we are changing open flags and WAL mode at the same time, then
                 // we have no choice but to close the primary connection beforehand
                 // because there can only be one connection open when we change WAL mode.
-                if (restrictToOneConnection) {
+                if (walModeChanged) {
                     closeAvailableConnectionsAndLogExceptionsLocked();
                 }
 
@@ -296,9 +297,11 @@
 
                 mAvailablePrimaryConnection = newPrimaryConnection;
                 mConfiguration.updateParametersFrom(configuration);
+                setMaxConnectionPoolSizeLocked();
             } else {
                 // Reconfigure the database connections in place.
                 mConfiguration.updateParametersFrom(configuration);
+                setMaxConnectionPoolSizeLocked();
 
                 closeExcessConnectionsAndLogExceptionsLocked();
                 reconfigureAllConnectionsLocked();
@@ -360,8 +363,7 @@
                     mAvailablePrimaryConnection = connection;
                 }
                 wakeConnectionWaitersLocked();
-            } else if (mAvailableNonPrimaryConnections.size() >=
-                    mConfiguration.maxConnectionPoolSize - 1) {
+            } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
                 closeConnectionAndLogExceptionsLocked(connection);
             } else {
                 if (recycleConnectionLocked(connection, status)) {
@@ -499,7 +501,7 @@
     // Can't throw.
     private void closeExcessConnectionsAndLogExceptionsLocked() {
         int availableCount = mAvailableNonPrimaryConnections.size();
-        while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
+        while (availableCount-- > mMaxConnectionPoolSize - 1) {
             SQLiteConnection connection =
                     mAvailableNonPrimaryConnections.remove(availableCount);
             closeConnectionAndLogExceptionsLocked(connection);
@@ -874,7 +876,7 @@
         if (mAvailablePrimaryConnection != null) {
             openConnections += 1;
         }
-        if (openConnections >= mConfiguration.maxConnectionPoolSize) {
+        if (openConnections >= mMaxConnectionPoolSize) {
             return null;
         }
         connection = openConnectionLocked(mConfiguration,
@@ -926,6 +928,18 @@
         return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
     }
 
+    private void setMaxConnectionPoolSizeLocked() {
+        if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+            mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
+        } else {
+            // TODO: We don't actually need to restrict the connection pool size to 1
+            // for non-WAL databases.  There might be reasons to use connection pooling
+            // with other journal modes.  For now, enabling connection pooling and
+            // using WAL are the same thing in the API.
+            mMaxConnectionPoolSize = 1;
+        }
+    }
+
     private void throwIfClosedLocked() {
         if (!mIsOpen) {
             throw new IllegalStateException("Cannot perform this operation "
@@ -972,7 +986,7 @@
         synchronized (mLock) {
             printer.println("Connection pool for " + mConfiguration.path + ":");
             printer.println("  Open: " + mIsOpen);
-            printer.println("  Max connections: " + mConfiguration.maxConnectionPoolSize);
+            printer.println("  Max connections: " + mMaxConnectionPoolSize);
 
             printer.println("  Available primary connection:");
             if (mAvailablePrimaryConnection != null) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 0ecce4d..049a615 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -232,6 +232,18 @@
     public static final int CREATE_IF_NECESSARY = 0x10000000;     // update native code if changing
 
     /**
+     * Open flag: Flag for {@link #openDatabase} to open the database file with
+     * write-ahead logging enabled by default.  Using this flag is more efficient
+     * than calling {@link #enableWriteAheadLogging}.
+     *
+     * Write-ahead logging cannot be used with read-only databases so the value of
+     * this flag is ignored if the database is opened read-only.
+     *
+     * @see #enableWriteAheadLogging
+     */
+    public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
+
+    /**
      * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
      *
      * Each prepared-statement is between 1K - 6K, depending on the complexity of the
@@ -654,7 +666,7 @@
      * @throws SQLiteException if the database cannot be opened
      */
     public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
-        return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler());
+        return openDatabase(path, factory, flags, null);
     }
 
     /**
@@ -694,7 +706,7 @@
      * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
      */
     public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
-        return openDatabase(path, factory, CREATE_IF_NECESSARY);
+        return openDatabase(path, factory, CREATE_IF_NECESSARY, null);
     }
 
     /**
@@ -1781,56 +1793,84 @@
     }
 
     /**
-     * This method enables parallel execution of queries from multiple threads on the same database.
-     * It does this by opening multiple handles to the database and using a different
-     * database handle for each query.
+     * This method enables parallel execution of queries from multiple threads on the
+     * same database.  It does this by opening multiple connections to the database
+     * and using a different database connection for each query.  The database
+     * journal mode is also changed to enable writes to proceed concurrently with reads.
      * <p>
-     * If a transaction is in progress on one connection handle and say, a table is updated in the
-     * transaction, then query on the same table on another connection handle will block for the
-     * transaction to complete. But this method enables such queries to execute by having them
-     * return old version of the data from the table. Most often it is the data that existed in the
-     * table prior to the above transaction updates on that table.
-     * <p>
-     * Maximum number of simultaneous handles used to execute queries in parallel is
+     * When write-ahead logging is not enabled (the default), it is not possible for
+     * reads and writes to occur on the database at the same time.  Before modifying the
+     * database, the writer implicitly acquires an exclusive lock on the database which
+     * prevents readers from accessing the database until the write is completed.
+     * </p><p>
+     * In contrast, when write-ahead logging is enabled (by calling this method), write
+     * operations occur in a separate log file which allows reads to proceed concurrently.
+     * While a write is in progress, readers on other threads will perceive the state
+     * of the database as it was before the write began.  When the write completes, readers
+     * on other threads will then perceive the new state of the database.
+     * </p><p>
+     * It is a good idea to enable write-ahead logging whenever a database will be
+     * concurrently accessed and modified by multiple threads at the same time.
+     * However, write-ahead logging uses significantly more memory than ordinary
+     * journaling because there are multiple connections to the same database.
+     * So if a database will only be used by a single thread, or if optimizing
+     * concurrency is not very important, then write-ahead logging should be disabled.
+     * </p><p>
+     * After calling this method, execution of queries in parallel is enabled as long as
+     * the database remains open.  To disable execution of queries in parallel, either
+     * call {@link #disableWriteAheadLogging} or close the database and reopen it.
+     * </p><p>
+     * The maximum number of connections used to execute queries in parallel is
      * dependent upon the device memory and possibly other properties.
-     * <p>
-     * After calling this method, execution of queries in parallel is enabled as long as this
-     * database handle is open. To disable execution of queries in parallel, database should
-     * be closed and reopened.
-     * <p>
+     * </p><p>
      * If a query is part of a transaction, then it is executed on the same database handle the
      * transaction was begun.
-     * <p>
-     * If the database has any attached databases, then execution of queries in paralel is NOT
-     * possible. In such cases, a message is printed to logcat and false is returned.
-     * <p>
-     * This feature is not available for :memory: databases. In such cases,
-     * a message is printed to logcat and false is returned.
-     * <p>
-     * A typical way to use this method is the following:
-     * <pre>
-     *     SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
-     *             CREATE_IF_NECESSARY, myDatabaseErrorHandler);
-     *     db.enableWriteAheadLogging();
-     * </pre>
-     * <p>
+     * </p><p>
      * Writers should use {@link #beginTransactionNonExclusive()} or
      * {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
-     * to start a trsnsaction.
-     * Non-exclusive mode allows database file to be in readable by threads executing queries.
+     * to start a transaction.  Non-exclusive mode allows database file to be in readable
+     * by other threads executing queries.
+     * </p><p>
+     * If the database has any attached databases, then execution of queries in parallel is NOT
+     * possible.  Likewise, write-ahead logging is not supported for read-only databases
+     * or memory databases.  In such cases, {@link #enableWriteAheadLogging} returns false.
+     * </p><p>
+     * The best way to enable write-ahead logging is to pass the
+     * {@link #ENABLE_WRITE_AHEAD_LOGGING} flag to {@link #openDatabase}.  This is
+     * more efficient than calling {@link #enableWriteAheadLogging}.
+     * <code><pre>
+     *     SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+     *             SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
+     *             myDatabaseErrorHandler);
+     *     db.enableWriteAheadLogging();
+     * </pre></code>
+     * </p><p>
+     * Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging}
+     * after opening the database.
+     * <code><pre>
+     *     SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+     *             SQLiteDatabase.CREATE_IF_NECESSARY, myDatabaseErrorHandler);
+     *     db.enableWriteAheadLogging();
+     * </pre></code>
+     * </p><p>
+     * See also <a href="http://sqlite.org/wal.html">SQLite Write-Ahead Logging</a> for
+     * more details about how write-ahead logging works.
      * </p>
      *
-     * @return true if write-ahead-logging is set. false otherwise
+     * @return True if write-ahead logging is enabled.
      *
      * @throws IllegalStateException if there are transactions in progress at the
      * time this method is called.  WAL mode can only be changed when there are no
      * transactions in progress.
+     *
+     * @see #ENABLE_WRITE_AHEAD_LOGGING
+     * @see #disableWriteAheadLogging
      */
     public boolean enableWriteAheadLogging() {
         synchronized (mLock) {
             throwIfNotOpenLocked();
 
-            if (mConfigurationLocked.walEnabled) {
+            if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0) {
                 return true;
             }
 
@@ -1855,14 +1895,11 @@
                 return false;
             }
 
-            final int oldMaxConnectionPoolSize = mConfigurationLocked.maxConnectionPoolSize;
-            mConfigurationLocked.maxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
-            mConfigurationLocked.walEnabled = true;
+            mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
             try {
                 mConnectionPoolLocked.reconfigure(mConfigurationLocked);
             } catch (RuntimeException ex) {
-                mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
-                mConfigurationLocked.walEnabled = false;
+                mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
                 throw ex;
             }
         }
@@ -1875,29 +1912,44 @@
      * @throws IllegalStateException if there are transactions in progress at the
      * time this method is called.  WAL mode can only be changed when there are no
      * transactions in progress.
+     *
+     * @see #enableWriteAheadLogging
      */
     public void disableWriteAheadLogging() {
         synchronized (mLock) {
             throwIfNotOpenLocked();
 
-            if (!mConfigurationLocked.walEnabled) {
+            if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
                 return;
             }
 
-            final int oldMaxConnectionPoolSize = mConfigurationLocked.maxConnectionPoolSize;
-            mConfigurationLocked.maxConnectionPoolSize = 1;
-            mConfigurationLocked.walEnabled = false;
+            mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
             try {
                 mConnectionPoolLocked.reconfigure(mConfigurationLocked);
             } catch (RuntimeException ex) {
-                mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
-                mConfigurationLocked.walEnabled = true;
+                mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
                 throw ex;
             }
         }
     }
 
     /**
+     * Returns true if write-ahead logging has been enabled for this database.
+     *
+     * @return True if write-ahead logging has been enabled for this database.
+     *
+     * @see #enableWriteAheadLogging
+     * @see #ENABLE_WRITE_AHEAD_LOGGING
+     */
+    public boolean isWriteAheadLoggingEnabled() {
+        synchronized (mLock) {
+            throwIfNotOpenLocked();
+
+            return (mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0;
+        }
+    }
+
+    /**
      * Collect statistics about all open databases in the current process.
      * Used by bug report.
      */
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index e06a5ee..123c2c6 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -62,14 +62,6 @@
     public int openFlags;
 
     /**
-     * The maximum number of connections to retain in the connection pool.
-     * Must be at least 1.
-     *
-     * Default is 1.
-     */
-    public int maxConnectionPoolSize;
-
-    /**
      * The maximum size of the prepared statement cache for each database connection.
      * Must be non-negative.
      *
@@ -85,13 +77,6 @@
     public Locale locale;
 
     /**
-     * True if WAL mode is enabled.
-     *
-     * Default is false.
-     */
-    public boolean walEnabled;
-
-    /**
      * The custom functions to register.
      */
     public final ArrayList<SQLiteCustomFunction> customFunctions =
@@ -114,7 +99,6 @@
         this.openFlags = openFlags;
 
         // Set default values for optional parameters.
-        maxConnectionPoolSize = 1;
         maxSqlCacheSize = 25;
         locale = Locale.getDefault();
     }
@@ -150,10 +134,8 @@
         }
 
         openFlags = other.openFlags;
-        maxConnectionPoolSize = other.maxConnectionPoolSize;
         maxSqlCacheSize = other.maxSqlCacheSize;
         locale = other.locale;
-        walEnabled = other.walEnabled;
         customFunctions.clear();
         customFunctions.addAll(other.customFunctions);
     }
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index ffa4663..fe37b8f 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -58,6 +58,7 @@
 
     private SQLiteDatabase mDatabase;
     private boolean mIsInitializing;
+    private boolean mEnableWriteAheadLogging;
     private final DatabaseErrorHandler mErrorHandler;
 
     /**
@@ -74,7 +75,7 @@
      *     newer, {@link #onDowngrade} will be used to downgrade the database
      */
     public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
-        this(context, name, factory, version, new DefaultDatabaseErrorHandler());
+        this(context, name, factory, version, null);
     }
 
     /**
@@ -92,14 +93,11 @@
      *     {@link #onUpgrade} will be used to upgrade the database; if the database is
      *     newer, {@link #onDowngrade} will be used to downgrade the database
      * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
-     * corruption.
+     * corruption, or null to use the default error handler.
      */
     public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
             DatabaseErrorHandler errorHandler) {
         if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
-        if (errorHandler == null) {
-            throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
-        }
 
         mContext = context;
         mName = name;
@@ -117,6 +115,32 @@
     }
 
     /**
+     * Enables or disables the use of write-ahead logging for the database.
+     *
+     * Write-ahead logging cannot be used with read-only databases so the value of
+     * this flag is ignored if the database is opened read-only.
+     *
+     * @param enabled True if write-ahead logging should be enabled, false if it
+     * should be disabled.
+     *
+     * @see SQLiteDatabase#enableWriteAheadLogging()
+     */
+    public void setWriteAheadLoggingEnabled(boolean enabled) {
+        synchronized (this) {
+            if (mEnableWriteAheadLogging != enabled) {
+                if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
+                    if (enabled) {
+                        mDatabase.enableWriteAheadLogging();
+                    } else {
+                        mDatabase.disableWriteAheadLogging();
+                    }
+                }
+                mEnableWriteAheadLogging = enabled;
+            }
+        }
+    }
+
+    /**
      * Create and/or open a database that will be used for reading and writing.
      * The first time this is called, the database will be opened and
      * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
@@ -197,7 +221,9 @@
                         db = SQLiteDatabase.openDatabase(path, mFactory,
                                 SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                     } else {
-                        db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
+                        db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
+                                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
+                                mFactory, mErrorHandler);
                     }
                 } catch (SQLiteException ex) {
                     if (writable) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 330a189..abf713b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -84,6 +84,7 @@
     public DatabaseHelper(Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
         mContext = context;
+        setWriteAheadLoggingEnabled(true);
     }
 
     public static boolean isValidTable(String name) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 95fd62d..1fa3695 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -260,7 +260,6 @@
         // Watch for external modifications to the database file,
         // keeping our cache in sync.
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.enableWriteAheadLogging();
         sObserverInstance = new SettingsFileObserver(db.getPath());
         sObserverInstance.startWatching();
         startAsyncCachePopulation();