diff options
| author | 2017-07-31 23:56:02 +0000 | |
|---|---|---|
| committer | 2017-07-31 23:56:02 +0000 | |
| commit | 82f289a4b4cf712aa02eb37f67d5f836e8016eaf (patch) | |
| tree | d47fc51470d332f0dcb87d5de6968a4aaaabfb37 | |
| parent | 102e9a7e3bb1abe413a6816d998374547fcbee57 (diff) | |
| parent | cf97b6b7f7ceae808c1b354a18388c2bd96abf70 (diff) | |
Merge "Added setIdleConnectionTimeout method" into oc-mr1-dev
| -rw-r--r-- | api/current.txt | 3 | ||||
| -rw-r--r-- | api/system-current.txt | 3 | ||||
| -rw-r--r-- | api/test-current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteConnectionPool.java | 51 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteDatabase.java | 77 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java | 14 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteGlobal.java | 11 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteOpenHelper.java | 20 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 4 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 1 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/database/DatabaseGeneralTest.java | 36 |
11 files changed, 178 insertions, 45 deletions
diff --git a/api/current.txt b/api/current.txt index 4db5cee1dc28..56b608c1a962 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11956,6 +11956,7 @@ package android.database.sqlite { public static final class SQLiteDatabase.OpenParams { method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); method public android.database.DatabaseErrorHandler getErrorHandler(); + method public long getIdleConnectionTimeout(); method public int getLookasideSlotCount(); method public int getLookasideSlotSize(); method public int getOpenFlags(); @@ -11969,6 +11970,7 @@ package android.database.sqlite { method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder removeOpenFlags(int); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler); + method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int); } @@ -12026,6 +12028,7 @@ package android.database.sqlite { 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 setIdleConnectionTimeout(long); method public void setLookasideConfig(int, int); method public void setWriteAheadLoggingEnabled(boolean); } diff --git a/api/system-current.txt b/api/system-current.txt index 41f63535ddd2..eeeecb307eb9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12752,6 +12752,7 @@ package android.database.sqlite { public static final class SQLiteDatabase.OpenParams { method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); method public android.database.DatabaseErrorHandler getErrorHandler(); + method public long getIdleConnectionTimeout(); method public int getLookasideSlotCount(); method public int getLookasideSlotSize(); method public int getOpenFlags(); @@ -12765,6 +12766,7 @@ package android.database.sqlite { method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder removeOpenFlags(int); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler); + method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int); } @@ -12822,6 +12824,7 @@ package android.database.sqlite { 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 setIdleConnectionTimeout(long); method public void setLookasideConfig(int, int); method public void setWriteAheadLoggingEnabled(boolean); } diff --git a/api/test-current.txt b/api/test-current.txt index 87539ec87945..84eea3692959 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12000,6 +12000,7 @@ package android.database.sqlite { public static final class SQLiteDatabase.OpenParams { method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); method public android.database.DatabaseErrorHandler getErrorHandler(); + method public long getIdleConnectionTimeout(); method public int getLookasideSlotCount(); method public int getLookasideSlotSize(); method public int getOpenFlags(); @@ -12013,6 +12014,7 @@ package android.database.sqlite { method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder removeOpenFlags(int); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler); + method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int); method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int); } @@ -12095,6 +12097,7 @@ package android.database.sqlite { 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 setIdleConnectionTimeout(long); method public void setLookasideConfig(int, int); method public void setWriteAheadLoggingEnabled(boolean); } diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index 6ce8787e6c38..b66bf18fca1d 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -16,7 +16,6 @@ package android.database.sqlite; -import android.app.ActivityManager; import android.database.sqlite.SQLiteDebug.DbStats; import android.os.CancellationSignal; import android.os.Handler; @@ -24,7 +23,6 @@ import android.os.Looper; import android.os.Message; import android.os.OperationCanceledException; import android.os.SystemClock; -import android.os.SystemProperties; import android.util.Log; import android.util.PrefixPrinter; import android.util.Printer; @@ -84,15 +82,6 @@ public final class SQLiteConnectionPool implements Closeable { // and logging a message about the connection pool being busy. private static final long CONNECTION_POOL_BUSY_MILLIS = 30 * 1000; // 30 seconds - // TODO b/63398887 Move to SQLiteGlobal - private static final long IDLE_CONNECTION_CLOSE_DELAY_MILLIS = SystemProperties - .getInt("persist.debug.sqlite.idle_connection_close_delay", 30000); - - // TODO b/63398887 STOPSHIP. - // Temporarily enabled for testing across a broader set of dogfood devices. - private static final boolean CLOSE_IDLE_CONNECTIONS = SystemProperties - .getBoolean("persist.debug.sqlite.close_idle_connections", true); - private final CloseGuard mCloseGuard = CloseGuard.get(); private final Object mLock = new Object(); @@ -167,16 +156,12 @@ public final class SQLiteConnectionPool implements Closeable { private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) { mConfiguration = new SQLiteDatabaseConfiguration(configuration); - // Disable lookaside allocator on low-RAM devices - if (ActivityManager.isLowRamDeviceStatic()) { - mConfiguration.lookasideSlotCount = 0; - mConfiguration.lookasideSlotSize = 0; - } setMaxConnectionPoolSizeLocked(); - - // Do not close idle connections for in-memory databases - if (CLOSE_IDLE_CONNECTIONS && !configuration.isInMemoryDb()) { - setupIdleConnectionHandler(Looper.getMainLooper(), IDLE_CONNECTION_CLOSE_DELAY_MILLIS); + // If timeout is set, setup idle connection handler + // In case of MAX_VALUE - idle connections are never closed + if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) { + setupIdleConnectionHandler(Looper.getMainLooper(), + mConfiguration.idleConnectionTimeoutMs); } } @@ -214,6 +199,12 @@ public final class SQLiteConnectionPool implements Closeable { // This might throw if the database is corrupt. mAvailablePrimaryConnection = openConnectionLocked(mConfiguration, true /*primaryConnection*/); // might throw + // Mark it released so it can be closed after idle timeout + synchronized (mLock) { + if (mIdleConnectionHandler != null) { + mIdleConnectionHandler.connectionReleased(mAvailablePrimaryConnection); + } + } // Mark the pool as being open for business. mIsOpen = true; @@ -1023,12 +1014,12 @@ public final class SQLiteConnectionPool implements Closeable { } /** - * Set up the handler based on the provided looper and delay. + * Set up the handler based on the provided looper and timeout. */ @VisibleForTesting - public void setupIdleConnectionHandler(Looper looper, long delayMs) { + public void setupIdleConnectionHandler(Looper looper, long timeoutMs) { synchronized (mLock) { - mIdleConnectionHandler = new IdleConnectionHandler(looper, delayMs); + mIdleConnectionHandler = new IdleConnectionHandler(looper, timeoutMs); } } @@ -1089,6 +1080,10 @@ public final class SQLiteConnectionPool implements Closeable { printer.println(" Lookaside config: sz=" + mConfiguration.lookasideSlotSize + " cnt=" + mConfiguration.lookasideSlotCount); } + if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) { + printer.println( + " Idle connection timeout: " + mConfiguration.idleConnectionTimeoutMs); + } printer.println(" Available primary connection:"); if (mAvailablePrimaryConnection != null) { mAvailablePrimaryConnection.dump(indentedPrinter, verbose); @@ -1155,11 +1150,11 @@ public final class SQLiteConnectionPool implements Closeable { } private class IdleConnectionHandler extends Handler { - private final long mDelay; + private final long mTimeout; - IdleConnectionHandler(Looper looper, long delay) { + IdleConnectionHandler(Looper looper, long timeout) { super(looper); - mDelay = delay; + mTimeout = timeout; } @Override @@ -1172,14 +1167,14 @@ public final class SQLiteConnectionPool implements Closeable { if (closeAvailableConnectionLocked(msg.what)) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Closed idle connection " + mConfiguration.label + " " + msg.what - + " after " + mDelay); + + " after " + mTimeout); } } } } void connectionReleased(SQLiteConnection con) { - sendEmptyMessageDelayed(con.getConnectionId(), mDelay); + sendEmptyMessageDelayed(con.getConnectionId(), mTimeout); } void connectionAcquired(SQLiteConnection con) { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index af6df1554a86..5b6efd4dcffe 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.content.ContentValues; import android.database.Cursor; import android.database.DatabaseErrorHandler; @@ -30,6 +31,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.os.CancellationSignal; import android.os.Looper; import android.os.OperationCanceledException; +import android.os.SystemProperties; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -77,21 +79,21 @@ public final class SQLiteDatabase extends SQLiteClosable { private static final int EVENT_DB_CORRUPT = 75004; + // TODO b/63398887 STOPSHIP. + // Temporarily enabled for testing across a broader set of dogfood devices. + private static final boolean DEBUG_CLOSE_IDLE_CONNECTIONS = SystemProperties + .getBoolean("persist.debug.sqlite.close_idle_connections", true); + // Stores reference to all databases opened in the current process. // (The referent Object is not used at this time.) // INVARIANT: Guarded by sActiveDatabases. - private static WeakHashMap<SQLiteDatabase, Object> sActiveDatabases = - new WeakHashMap<SQLiteDatabase, Object>(); + private static WeakHashMap<SQLiteDatabase, Object> sActiveDatabases = new WeakHashMap<>(); // Thread-local for database sessions that belong to this database. // Each thread has its own database session. // INVARIANT: Immutable. - private final ThreadLocal<SQLiteSession> mThreadSession = new ThreadLocal<SQLiteSession>() { - @Override - protected SQLiteSession initialValue() { - return createSession(); - } - }; + private final ThreadLocal<SQLiteSession> mThreadSession = ThreadLocal + .withInitial(this::createSession); // The optional factory to use when creating new Cursors. May be null. // INVARIANT: Immutable. @@ -261,12 +263,29 @@ public final class SQLiteDatabase extends SQLiteClosable { private SQLiteDatabase(final String path, final int openFlags, CursorFactory cursorFactory, DatabaseErrorHandler errorHandler, - int lookasideSlotSize, int lookasideSlotCount) { + int lookasideSlotSize, int lookasideSlotCount, long idleConnectionTimeoutMs) { mCursorFactory = cursorFactory; mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler(); mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags); mConfigurationLocked.lookasideSlotSize = lookasideSlotSize; mConfigurationLocked.lookasideSlotCount = lookasideSlotCount; + // Disable lookaside allocator on low-RAM devices + if (ActivityManager.isLowRamDeviceStatic()) { + mConfigurationLocked.lookasideSlotCount = 0; + mConfigurationLocked.lookasideSlotSize = 0; + } + long effectiveTimeoutMs = Long.MAX_VALUE; + // Never close idle connections for in-memory databases + if (!mConfigurationLocked.isInMemoryDb()) { + // First, check app-specific value. Otherwise use defaults + // -1 in idleConnectionTimeoutMs indicates unset value + if (idleConnectionTimeoutMs >= 0) { + effectiveTimeoutMs = idleConnectionTimeoutMs; + } else if (DEBUG_CLOSE_IDLE_CONNECTIONS) { + effectiveTimeoutMs = SQLiteGlobal.getIdleConnectionTimeout(); + } + } + mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs; } @Override @@ -694,7 +713,8 @@ public final class SQLiteDatabase extends SQLiteClosable { Preconditions.checkArgument(openParams != null, "OpenParams cannot be null"); SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags, openParams.mCursorFactory, openParams.mErrorHandler, - openParams.mLookasideSlotSize, openParams.mLookasideSlotCount); + openParams.mLookasideSlotSize, openParams.mLookasideSlotCount, + openParams.mIdleConnectionTimeout); db.open(); return db; } @@ -720,7 +740,7 @@ public final class SQLiteDatabase extends SQLiteClosable { */ public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler) { - SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1); + SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1); db.open(); return db; } @@ -2267,14 +2287,17 @@ public final class SQLiteDatabase extends SQLiteClosable { private final DatabaseErrorHandler mErrorHandler; private final int mLookasideSlotSize; private final int mLookasideSlotCount; + private long mIdleConnectionTimeout; private OpenParams(int openFlags, CursorFactory cursorFactory, - DatabaseErrorHandler errorHandler, int lookasideSlotSize, int lookasideSlotCount) { + DatabaseErrorHandler errorHandler, int lookasideSlotSize, int lookasideSlotCount, + long idleConnectionTimeout) { mOpenFlags = openFlags; mCursorFactory = cursorFactory; mErrorHandler = errorHandler; mLookasideSlotSize = lookasideSlotSize; mLookasideSlotCount = lookasideSlotCount; + mIdleConnectionTimeout = idleConnectionTimeout; } /** @@ -2330,6 +2353,17 @@ public final class SQLiteDatabase extends SQLiteClosable { } /** + * Returns maximum number of milliseconds that SQLite connection is allowed to be idle + * before it is closed and removed from the pool. + * <p>If the value isn't set, the timeout defaults to the system wide timeout + * + * @return timeout in milliseconds or -1 if the value wasn't set. + */ + public long getIdleConnectionTimeout() { + return mIdleConnectionTimeout; + } + + /** * Creates a new instance of builder {@link Builder#Builder(OpenParams) initialized} with * {@code this} parameters. * @hide @@ -2345,6 +2379,7 @@ public final class SQLiteDatabase extends SQLiteClosable { public static final class Builder { private int mLookasideSlotSize = -1; private int mLookasideSlotCount = -1; + private long mIdleConnectionTimeout = -1; private int mOpenFlags; private CursorFactory mCursorFactory; private DatabaseErrorHandler mErrorHandler; @@ -2474,13 +2509,29 @@ public final class SQLiteDatabase extends SQLiteClosable { } /** + * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle + * before it is closed and removed from the pool. + * + * @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} + * to allow unlimited idle connections. + */ + @NonNull + public Builder setIdleConnectionTimeout( + @IntRange(from = 0) long idleConnectionTimeoutMs) { + Preconditions.checkArgument(idleConnectionTimeoutMs >= 0, + "idle connection timeout cannot be negative"); + mIdleConnectionTimeout = idleConnectionTimeoutMs; + return this; + } + + /** * Creates an instance of {@link OpenParams} with the options that were previously set * on this builder */ @NonNull public OpenParams build() { return new OpenParams(mOpenFlags, mCursorFactory, mErrorHandler, mLookasideSlotSize, - mLookasideSlotCount); + mLookasideSlotCount, mIdleConnectionTimeout); } } } diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 7f09b73adaa7..34c9b3395d1a 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -94,14 +94,21 @@ public final class SQLiteDatabaseConfiguration { * * <p>If negative, the default lookaside configuration will be used */ - public int lookasideSlotSize; + public int lookasideSlotSize = -1; /** * The total number of lookaside memory slots per database connection * * <p>If negative, the default lookaside configuration will be used */ - public int lookasideSlotCount; + public int lookasideSlotCount = -1; + + /** + * The number of milliseconds that SQLite connection is allowed to be idle before it + * is closed and removed from the pool. + * <p>By default, idle connections are not closed + */ + public long idleConnectionTimeoutMs = Long.MAX_VALUE; /** * Creates a database configuration with the required parameters for opening a @@ -122,8 +129,6 @@ public final class SQLiteDatabaseConfiguration { // Set default values for optional parameters. maxSqlCacheSize = 25; locale = Locale.getDefault(); - lookasideSlotSize = -1; - lookasideSlotCount = -1; } /** @@ -164,6 +169,7 @@ public final class SQLiteDatabaseConfiguration { customFunctions.addAll(other.customFunctions); lookasideSlotSize = other.lookasideSlotSize; lookasideSlotCount = other.lookasideSlotCount; + idleConnectionTimeoutMs = other.idleConnectionTimeoutMs; } /** diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java index 922d11b6ae65..571656a22240 100644 --- a/core/java/android/database/sqlite/SQLiteGlobal.java +++ b/core/java/android/database/sqlite/SQLiteGlobal.java @@ -124,4 +124,15 @@ public final class SQLiteGlobal { com.android.internal.R.integer.db_connection_pool_size)); return Math.max(2, value); } + + /** + * The default number of milliseconds that SQLite connection is allowed to be idle before it + * is closed and removed from the pool. + */ + public static int getIdleConnectionTimeout() { + return SystemProperties.getInt("debug.sqlite.idle_connection_timeout", + Resources.getSystem().getInteger( + com.android.internal.R.integer.db_default_idle_connection_timeout)); + } + } diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index c19db82a81f7..dfaf714963eb 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -195,6 +195,26 @@ public abstract class SQLiteOpenHelper { } /** + * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle + * before it is closed and removed from the pool. + * + * <p>This method should be called from the constructor of the subclass, + * before opening the database + * + * @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} value + * to allow unlimited idle connections. + */ + public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) { + synchronized (this) { + if (mDatabase != null && mDatabase.isOpen()) { + throw new IllegalStateException( + "Connection timeout setting cannot be changed after opening the database"); + } + mOpenParamsBuilder.setIdleConnectionTimeout(idleConnectionTimeoutMs); + } + } + + /** * 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 diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 46dbf046d12b..40380100121a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1673,6 +1673,10 @@ The size of the WAL file is also constrained by 'db_journal_size_limit'. --> <integer name="db_wal_autocheckpoint">100</integer> + <!-- The number of milliseconds that SQLite connection is allowed to be idle before it + is closed and removed from the pool --> + <integer name="db_default_idle_connection_timeout">30000</integer> + <!-- Max space (in MB) allocated to DownloadManager to store the downloaded files if they are to be stored in DownloadManager's data dir, which typically is /data/data/com.android.providers.downloads/files --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 37f1daa2193b..f392ea0e3833 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -428,6 +428,7 @@ <java-symbol type="integer" name="db_connection_pool_size" /> <java-symbol type="integer" name="db_journal_size_limit" /> <java-symbol type="integer" name="db_wal_autocheckpoint" /> + <java-symbol type="integer" name="db_default_idle_connection_timeout" /> <java-symbol type="integer" name="config_soundEffectVolumeDb" /> <java-symbol type="integer" name="config_lockSoundVolumeDb" /> <java-symbol type="integer" name="config_multiuserMaximumUsers" /> diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java index 7800f4ab0598..f97d51d3a38a 100644 --- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java +++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java @@ -25,6 +25,8 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDebug; import android.database.sqlite.SQLiteException; import android.os.Parcel; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.UiDevice; import android.test.AndroidTestCase; import android.test.PerformanceTestCase; import android.test.suitebuilder.annotation.LargeTest; @@ -1185,4 +1187,38 @@ public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceT fail("unexpected"); } } + + @MediumTest + public void testCloseIdleConnection() throws Exception { + mDatabase.close(); + SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder() + .setIdleConnectionTimeout(1000).build(); + mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), params); + // Wait a bit and check that connection is still open + Thread.sleep(100); + String output = executeShellCommand("dumpsys dbinfo " + getContext().getPackageName()); + assertTrue("Connection #0 should be open. Output: " + output, + output.contains("Connection #0:")); + + // Now cause idle timeout and check that connection is closed + Thread.sleep(1000); + output = executeShellCommand("dumpsys dbinfo " + getContext().getPackageName()); + assertFalse("Connection #0 should be closed. Output: " + output, + output.contains("Connection #0:")); + } + + @SmallTest + public void testSetIdleConnectionTimeoutValidation() throws Exception { + try { + new SQLiteDatabase.OpenParams.Builder().setIdleConnectionTimeout(-1).build(); + fail("Negative timeout should be rejected"); + } catch (IllegalArgumentException expected) { + } + } + + private String executeShellCommand(String cmd) throws Exception { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } + } |