Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf97b6b7 authored by Fyodor Kupolov's avatar Fyodor Kupolov
Browse files

Added setIdleConnectionTimeout method

It allows apps to set time SQLite connection is allowed to be idle
before it is closed and removed from the pool.

Test: manual + DatabaseGeneralTest
Bug: 63398887
Change-Id: Ie09eeb4dc2b9e52ba67d9355b1f9bd869b148613
parent d294d8dc
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
  }
+3 −0
Original line number Diff line number Diff line
@@ -12751,6 +12751,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();
@@ -12764,6 +12765,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);
  }
@@ -12821,6 +12823,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);
  }
+3 −0
Original line number Diff line number Diff line
@@ -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);
  }
+23 −28
Original line number Diff line number Diff line
@@ -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;
@@ -1021,12 +1012,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);
        }
    }

@@ -1087,6 +1078,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);
@@ -1153,11 +1148,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
@@ -1170,14 +1165,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) {
+64 −13
Original line number Diff line number Diff line
@@ -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;
        }

        /**
@@ -2329,6 +2352,17 @@ public final class SQLiteDatabase extends SQLiteClosable {
            return mErrorHandler;
        }

        /**
         * 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.
@@ -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;
@@ -2473,6 +2508,22 @@ public final class SQLiteDatabase extends SQLiteClosable {
                return this;
            }

            /**
             * 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
@@ -2480,7 +2531,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
            @NonNull
            public OpenParams build() {
                return new OpenParams(mOpenFlags, mCursorFactory, mErrorHandler, mLookasideSlotSize,
                        mLookasideSlotCount);
                        mLookasideSlotCount, mIdleConnectionTimeout);
            }
        }
    }
Loading