From 4d01eeaffaa720e4458a118baa137a11614f00f7 Mon Sep 17 00:00:00 2001 From: Justin Klaassen Date: Tue, 3 Apr 2018 23:21:57 -0400 Subject: Import Android SDK Platform P [4697573] /google/data/ro/projects/android/fetch_artifact \ --bid 4697573 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4697573.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077 --- android/database/AbstractCursor.java | 2 +- android/database/CursorWindow.java | 30 ++++++++++--- android/database/SQLiteDatabasePerfTest.java | 52 +++++++++++++++++++++- android/database/sqlite/SQLiteConnection.java | 8 ++-- android/database/sqlite/SQLiteConnectionPool.java | 25 ++++++++++- android/database/sqlite/SQLiteDatabase.java | 26 ++++++----- .../sqlite/SQLiteDatabaseConfiguration.java | 15 +++---- android/database/sqlite/SQLiteOpenHelper.java | 31 +++++++++++-- android/database/sqlite/SQLiteQueryBuilder.java | 2 +- 9 files changed, 153 insertions(+), 38 deletions(-) (limited to 'android/database') diff --git a/android/database/AbstractCursor.java b/android/database/AbstractCursor.java index fdb702f0..76fa0087 100644 --- a/android/database/AbstractCursor.java +++ b/android/database/AbstractCursor.java @@ -395,7 +395,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { */ @Override public void setNotificationUri(ContentResolver cr, Uri notifyUri) { - setNotificationUri(cr, notifyUri, UserHandle.myUserId()); + setNotificationUri(cr, notifyUri, cr.getUserId()); } /** @hide - set the notification uri but with an observer for a particular user's view */ diff --git a/android/database/CursorWindow.java b/android/database/CursorWindow.java index f84ec65f..a748f4d2 100644 --- a/android/database/CursorWindow.java +++ b/android/database/CursorWindow.java @@ -28,6 +28,7 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.SparseIntArray; +import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; /** @@ -62,28 +63,43 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private static native void nativeDispose(long windowPtr); private static native void nativeWriteToParcel(long windowPtr, Parcel parcel); + private static native String nativeGetName(long windowPtr); + private static native byte[] nativeGetBlob(long windowPtr, int row, int column); + private static native String nativeGetString(long windowPtr, int row, int column); + private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column, + CharArrayBuffer buffer); + private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column); + private static native boolean nativePutString(long windowPtr, String value, + int row, int column); + + // Below native methods don't do unconstrained work, so are FastNative for performance + + @FastNative private static native void nativeClear(long windowPtr); + @FastNative private static native int nativeGetNumRows(long windowPtr); + @FastNative private static native boolean nativeSetNumColumns(long windowPtr, int columnNum); + @FastNative private static native boolean nativeAllocRow(long windowPtr); + @FastNative private static native void nativeFreeLastRow(long windowPtr); + @FastNative private static native int nativeGetType(long windowPtr, int row, int column); - private static native byte[] nativeGetBlob(long windowPtr, int row, int column); - private static native String nativeGetString(long windowPtr, int row, int column); + @FastNative private static native long nativeGetLong(long windowPtr, int row, int column); + @FastNative private static native double nativeGetDouble(long windowPtr, int row, int column); - private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column, - CharArrayBuffer buffer); - private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column); - private static native boolean nativePutString(long windowPtr, String value, int row, int column); + @FastNative private static native boolean nativePutLong(long windowPtr, long value, int row, int column); + @FastNative private static native boolean nativePutDouble(long windowPtr, double value, int row, int column); + @FastNative private static native boolean nativePutNull(long windowPtr, int row, int column); - private static native String nativeGetName(long windowPtr); /** * Creates a new empty cursor window and gives it a name. diff --git a/android/database/SQLiteDatabasePerfTest.java b/android/database/SQLiteDatabasePerfTest.java index 7a32c0cc..e2b75c3f 100644 --- a/android/database/SQLiteDatabasePerfTest.java +++ b/android/database/SQLiteDatabasePerfTest.java @@ -117,6 +117,52 @@ public class SQLiteDatabasePerfTest { } } + @Test + public void testCursorIterateForward() { + // A larger dataset is needed to exceed default CursorWindow size + int datasetSize = DEFAULT_DATASET_SIZE * 50; + insertT1TestDataSet(datasetSize); + + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + try (Cursor cursor = mDatabase + .rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 ORDER BY _ID", null)) { + int i = 0; + while(cursor.moveToNext()) { + assertEquals(i, cursor.getInt(0)); + assertEquals(i, cursor.getInt(1)); + assertEquals("T1Value" + i, cursor.getString(2)); + assertEquals(1.1 * i, cursor.getDouble(3), 0.0000001d); + i++; + } + assertEquals(datasetSize, i); + } + } + } + + @Test + public void testCursorIterateBackwards() { + // A larger dataset is needed to exceed default CursorWindow size + int datasetSize = DEFAULT_DATASET_SIZE * 50; + insertT1TestDataSet(datasetSize); + + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + try (Cursor cursor = mDatabase + .rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 ORDER BY _ID", null)) { + int i = datasetSize - 1; + while(cursor.moveToPosition(i)) { + assertEquals(i, cursor.getInt(0)); + assertEquals(i, cursor.getInt(1)); + assertEquals("T1Value" + i, cursor.getString(2)); + assertEquals(1.1 * i, cursor.getDouble(3), 0.0000001d); + i--; + } + assertEquals(-1, i); + } + } + } + @Test public void testInnerJoin() { mDatabase.setForeignKeyConstraintsEnabled(true); @@ -201,8 +247,12 @@ public class SQLiteDatabasePerfTest { } private void insertT1TestDataSet() { + insertT1TestDataSet(DEFAULT_DATASET_SIZE); + } + + private void insertT1TestDataSet(int size) { mDatabase.beginTransaction(); - for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) { + for (int i = 0; i < size; i++) { mDatabase.execSQL("INSERT INTO T1 VALUES (?, ?, ?, ?)", new Object[]{i, i, "T1Value" + i, i * 1.1}); } diff --git a/android/database/sqlite/SQLiteConnection.java b/android/database/sqlite/SQLiteConnection.java index 7717b8d3..316c796f 100644 --- a/android/database/sqlite/SQLiteConnection.java +++ b/android/database/sqlite/SQLiteConnection.java @@ -292,8 +292,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen final boolean walEnabled = (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; // Use compatibility WAL unless an app explicitly set journal/synchronous mode - final boolean useCompatibilityWal = mConfiguration.journalMode == null - && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal; + // or DISABLE_COMPATIBILITY_WAL flag is set + final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal(); if (walEnabled || useCompatibilityWal) { setJournalMode("WAL"); if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) { @@ -423,8 +423,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled != mConfiguration.foreignKeyConstraintsEnabled; boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags) - & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0 - || configuration.useCompatibilityWal != mConfiguration.useCompatibilityWal; + & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING + | SQLiteDatabase.DISABLE_COMPATIBILITY_WAL)) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); // Update configuration parameters. diff --git a/android/database/sqlite/SQLiteConnectionPool.java b/android/database/sqlite/SQLiteConnectionPool.java index b2117003..e5193023 100644 --- a/android/database/sqlite/SQLiteConnectionPool.java +++ b/android/database/sqlite/SQLiteConnectionPool.java @@ -23,6 +23,7 @@ import android.os.Looper; import android.os.Message; import android.os.OperationCanceledException; import android.os.SystemClock; +import android.text.TextUtils; import android.util.Log; import android.util.PrefixPrinter; import android.util.Printer; @@ -315,7 +316,12 @@ public final class SQLiteConnectionPool implements Closeable { } } - if (mConfiguration.openFlags != configuration.openFlags) { + // 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; + + if (!onlyCompatWalChanged && 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. @@ -422,6 +428,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private boolean recycleConnectionLocked(SQLiteConnection connection, AcquiredConnectionStatus status) { if (status == AcquiredConnectionStatus.RECONFIGURE) { @@ -531,6 +538,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeAvailableConnectionsAndLogExceptionsLocked() { closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked(); @@ -541,6 +549,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private boolean closeAvailableConnectionLocked(int connectionId) { final int count = mAvailableNonPrimaryConnections.size(); for (int i = count - 1; i >= 0; i--) { @@ -562,6 +571,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() { final int count = mAvailableNonPrimaryConnections.size(); for (int i = 0; i < count; i++) { @@ -581,6 +591,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeExcessConnectionsAndLogExceptionsLocked() { int availableCount = mAvailableNonPrimaryConnections.size(); while (availableCount-- > mMaxConnectionPoolSize - 1) { @@ -591,6 +602,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) { try { connection.close(); // might throw @@ -609,6 +621,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void reconfigureAllConnectionsLocked() { if (mAvailablePrimaryConnection != null) { try { @@ -776,6 +789,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) { if (waiter.mAssignedConnection != null || waiter.mException != null) { // Waiter is done waiting but has not woken up yet. @@ -848,6 +862,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Can't throw. + @GuardedBy("mLock") private void wakeConnectionWaitersLocked() { // Unpark all waiters that have requests that we can fulfill. // This method is designed to not throw runtime exceptions, although we might send @@ -910,6 +925,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Might throw. + @GuardedBy("mLock") private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) { // If the primary connection is available, acquire it now. SQLiteConnection connection = mAvailablePrimaryConnection; @@ -935,6 +951,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Might throw. + @GuardedBy("mLock") private SQLiteConnection tryAcquireNonPrimaryConnectionLocked( String sql, int connectionFlags) { // Try to acquire the next connection in the queue. @@ -974,6 +991,7 @@ public final class SQLiteConnectionPool implements Closeable { } // Might throw. + @GuardedBy("mLock") private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) { try { final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0; @@ -1094,6 +1112,11 @@ public final class SQLiteConnectionPool implements Closeable { printer.println(" Open: " + mIsOpen); printer.println(" Max connections: " + mMaxConnectionPoolSize); printer.println(" Total execution time: " + mTotalExecutionTimeCounter); + printer.println(" Configuration: openFlags=" + mConfiguration.openFlags + + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal() + + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode) + + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode)); + if (SQLiteCompatibilityWalFlags.areFlagsSet()) { printer.println(" Compatibility WAL settings: compatibility_wal_supported=" + SQLiteCompatibilityWalFlags diff --git a/android/database/sqlite/SQLiteDatabase.java b/android/database/sqlite/SQLiteDatabase.java index c1c0812e..b463d8d0 100644 --- a/android/database/sqlite/SQLiteDatabase.java +++ b/android/database/sqlite/SQLiteDatabase.java @@ -252,6 +252,13 @@ public final class SQLiteDatabase extends SQLiteClosable { */ public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; + /** + * Open flag: Flag for {@link #openDatabase} to disable Compatibility WAL when opening database. + * + * @hide + */ + public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000; + /** * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}. * @@ -288,10 +295,10 @@ public final class SQLiteDatabase extends SQLiteClosable { mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs; mConfigurationLocked.journalMode = journalMode; mConfigurationLocked.syncMode = syncMode; - mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported(); - if (!mConfigurationLocked.isInMemoryDb() && SQLiteCompatibilityWalFlags.areFlagsSet()) { - mConfigurationLocked.useCompatibilityWal = SQLiteCompatibilityWalFlags - .isCompatibilityWalSupported(); + if (!SQLiteGlobal.isCompatibilityWalSupported() || ( + SQLiteCompatibilityWalFlags.areFlagsSet() && !SQLiteCompatibilityWalFlags + .isCompatibilityWalSupported())) { + mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; } } @@ -2006,7 +2013,6 @@ public final class SQLiteDatabase extends SQLiteClosable { * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory, * SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING, * myDatabaseErrorHandler); - * db.enableWriteAheadLogging(); * *

* Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging} @@ -2083,21 +2089,21 @@ public final class SQLiteDatabase extends SQLiteClosable { synchronized (mLock) { throwIfNotOpenLocked(); - final boolean oldUseCompatibilityWal = mConfigurationLocked.useCompatibilityWal; final int oldFlags = mConfigurationLocked.openFlags; - if (!oldUseCompatibilityWal && (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) { + final boolean walDisabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0; + final boolean compatibilityWalDisabled = (oldFlags & DISABLE_COMPATIBILITY_WAL) != 0; + if (walDisabled && compatibilityWalDisabled) { return; } mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING; - // If an app explicitly disables WAL, do not even use compatibility mode - mConfigurationLocked.useCompatibilityWal = false; + // If an app explicitly disables WAL, compatibility mode should be disabled too + mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; try { mConnectionPoolLocked.reconfigure(mConfigurationLocked); } catch (RuntimeException ex) { mConfigurationLocked.openFlags = oldFlags; - mConfigurationLocked.useCompatibilityWal = oldUseCompatibilityWal; throw ex; } } diff --git a/android/database/sqlite/SQLiteDatabaseConfiguration.java b/android/database/sqlite/SQLiteDatabaseConfiguration.java index a14df1eb..8b9dfcf5 100644 --- a/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -110,15 +110,6 @@ public final class SQLiteDatabaseConfiguration { */ public long idleConnectionTimeoutMs = Long.MAX_VALUE; - /** - * Enables compatibility WAL mode. Applications cannot explicitly choose compatibility WAL mode, - * therefore it is not exposed as a flag. - * - *

In this mode, only database journal mode will be changed, connection pool - * size will still be limited to a single connection. - */ - public boolean useCompatibilityWal; - /** * Journal mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set. *

Default is returned by {@link SQLiteGlobal#getDefaultJournalMode()} @@ -191,7 +182,6 @@ public final class SQLiteDatabaseConfiguration { lookasideSlotSize = other.lookasideSlotSize; lookasideSlotCount = other.lookasideSlotCount; idleConnectionTimeoutMs = other.idleConnectionTimeoutMs; - useCompatibilityWal = other.useCompatibilityWal; journalMode = other.journalMode; syncMode = other.syncMode; } @@ -204,6 +194,11 @@ public final class SQLiteDatabaseConfiguration { return path.equalsIgnoreCase(MEMORY_DB_PATH); } + boolean useCompatibilityWal() { + return journalMode == null && syncMode == null + && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0; + } + private static String stripPathForLogs(String path) { if (path.indexOf('@') == -1) { return path; diff --git a/android/database/sqlite/SQLiteOpenHelper.java b/android/database/sqlite/SQLiteOpenHelper.java index a2991e6e..7ff66358 100644 --- a/android/database/sqlite/SQLiteOpenHelper.java +++ b/android/database/sqlite/SQLiteOpenHelper.java @@ -58,7 +58,7 @@ public abstract class SQLiteOpenHelper { private SQLiteDatabase mDatabase; private boolean mIsInitializing; - private final SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder; + private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder; /** * Create a helper object to create, open, and/or manage a database. @@ -163,8 +163,7 @@ public abstract class SQLiteOpenHelper { mName = name; mNewVersion = version; mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion); - mOpenParamsBuilder = openParamsBuilder; - mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY); + setOpenParamsBuilder(openParamsBuilder); } /** @@ -198,6 +197,8 @@ public abstract class SQLiteOpenHelper { } mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled); } + // Compatibility WAL is disabled if an app disables or enables WAL + mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL); } } @@ -229,6 +230,30 @@ public abstract class SQLiteOpenHelper { } } + /** + * Sets configuration parameters that are used for opening {@link SQLiteDatabase}. + *

Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when + * opening the database + * + * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}. + * @throws IllegalStateException if the database is already open + */ + public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) { + Preconditions.checkNotNull(openParams); + synchronized (this) { + if (mDatabase != null && mDatabase.isOpen()) { + throw new IllegalStateException( + "OpenParams cannot be set after opening the database"); + } + setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams)); + } + } + + private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) { + mOpenParamsBuilder = openParamsBuilder; + mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY); + } + /** * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle * before it is closed and removed from the pool. diff --git a/android/database/sqlite/SQLiteQueryBuilder.java b/android/database/sqlite/SQLiteQueryBuilder.java index 56cba795..c6c676f8 100644 --- a/android/database/sqlite/SQLiteQueryBuilder.java +++ b/android/database/sqlite/SQLiteQueryBuilder.java @@ -31,7 +31,7 @@ import java.util.Set; import java.util.regex.Pattern; /** - * This is a convience class that helps build SQL queries to be sent to + * This is a convenience class that helps build SQL queries to be sent to * {@link SQLiteDatabase} objects. */ public class SQLiteQueryBuilder -- cgit v1.2.3