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

Commit 5eee0b59 authored by Edgar Arriaga's avatar Edgar Arriaga
Browse files

Add support for normal sync mode and propagate journalMode and syncMode on database open

This fixes dumpsys dbinfo not reporting the journalMode and syncMode as
well and made flags and configuration converge to the same value which
is less bug prone.

Test: dumpsys dbinfo
Bug: 193925357
Change-Id: I9a30b64b06a94fa9bd5589eba391a561ddcfaaf0
parent 5e28d4b5
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -14261,11 +14261,21 @@ package android.database.sqlite {
    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 String JOURNAL_MODE_DELETE = "DELETE";
    field public static final String JOURNAL_MODE_MEMORY = "MEMORY";
    field public static final String JOURNAL_MODE_OFF = "OFF";
    field public static final String JOURNAL_MODE_PERSIST = "PERSIST";
    field public static final String JOURNAL_MODE_TRUNCATE = "TRUNCATE";
    field public static final String JOURNAL_MODE_WAL = "WAL";
    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
    field public static final int OPEN_READWRITE = 0; // 0x0
    field public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000; // 0xc350
    field public static final String SYNC_MODE_EXTRA = "EXTRA";
    field public static final String SYNC_MODE_FULL = "FULL";
    field public static final String SYNC_MODE_NORMAL = "NORMAL";
    field public static final String SYNC_MODE_OFF = "OFF";
  }
  public static interface SQLiteDatabase.CursorFactory {
+49 −39
Original line number Diff line number Diff line
@@ -26,14 +26,13 @@ import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
import android.util.Printer;

import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
@@ -177,7 +176,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        mConfiguration = new SQLiteDatabaseConfiguration(configuration);
        mConnectionId = connectionId;
        mIsPrimaryConnection = primaryConnection;
        mIsReadOnlyConnection = (configuration.openFlags & SQLiteDatabase.OPEN_READONLY) != 0;
        mIsReadOnlyConnection = mConfiguration.isReadOnlyDatabase();
        mPreparedStatementCache = new PreparedStatementCache(
                mConfiguration.maxSqlCacheSize);
        mCloseGuard.open("close");
@@ -266,7 +265,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }
        setPageSize();
        setForeignKeyModeFromConfiguration();
        setWalModeFromConfiguration();
        setJournalFromConfiguration();
        setSyncModeFromConfiguration();
        setJournalSizeLimit();
        setAutoCheckpointInterval();
        setLocaleFromConfiguration();
@@ -334,31 +334,20 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }
    }

    private void setWalModeFromConfiguration() {
        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
            final boolean walEnabled =
                    (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
            // Use compatibility WAL unless an app explicitly set journal/synchronous mode
            // or DISABLE_COMPATIBILITY_WAL flag is set
            final boolean isCompatibilityWalEnabled =
                    mConfiguration.isLegacyCompatibilityWalEnabled();
            if (walEnabled || isCompatibilityWalEnabled) {
                setJournalMode("WAL");
                if (mConfiguration.syncMode != null) {
                    setSyncMode(mConfiguration.syncMode);
                } else if (isCompatibilityWalEnabled) {
                    setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode());
                } else {
                    setSyncMode(SQLiteGlobal.getWALSyncMode());
                }
    private void setJournalFromConfiguration() {
        if (!mIsReadOnlyConnection) {
            setJournalMode(mConfiguration.resolveJournalMode());
            maybeTruncateWalFile();
        } else {
                setJournalMode(mConfiguration.journalMode == null
                        ? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
                setSyncMode(mConfiguration.syncMode == null
                        ? SQLiteGlobal.getDefaultSyncMode() : mConfiguration.syncMode);
            // No need to truncate for read only databases.
            mConfiguration.shouldTruncateWalFile = false;
        }
    }

    private void setSyncModeFromConfiguration() {
        if (!mIsReadOnlyConnection) {
            setSyncMode(mConfiguration.resolveSyncMode());
        }
    }

    /**
@@ -366,6 +355,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
     * PRAGMA wal_checkpoint.
     */
    private void maybeTruncateWalFile() {
        if (!mConfiguration.shouldTruncateWalFile) {
            return;
        }

        final long threshold = SQLiteGlobal.getWALTruncateSize();
        if (DEBUG) {
            Log.d(TAG, "Truncate threshold=" + threshold);
@@ -390,12 +383,17 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
                + threshold + "; truncating");
        try {
            executeForString("PRAGMA wal_checkpoint(TRUNCATE)", null, null);
            mConfiguration.shouldTruncateWalFile = false;
        } catch (SQLiteException e) {
            Log.w(TAG, "Failed to truncate the -wal file", e);
        }
    }

    private void setSyncMode(String newValue) {
    private void setSyncMode(@SQLiteDatabase.SyncMode String newValue) {
        if (TextUtils.isEmpty(newValue)) {
            // No change to the sync mode is intended
            return;
        }
        String value = executeForString("PRAGMA synchronous", null, null);
        if (!canonicalizeSyncMode(value).equalsIgnoreCase(
                canonicalizeSyncMode(newValue))) {
@@ -403,16 +401,21 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }
    }

    private static String canonicalizeSyncMode(String value) {
    private static @SQLiteDatabase.SyncMode String canonicalizeSyncMode(String value) {
        switch (value) {
            case "0": return "OFF";
            case "1": return "NORMAL";
            case "2": return "FULL";
            case "0": return SQLiteDatabase.SYNC_MODE_OFF;
            case "1": return SQLiteDatabase.SYNC_MODE_NORMAL;
            case "2": return SQLiteDatabase.SYNC_MODE_FULL;
            case "3": return SQLiteDatabase.SYNC_MODE_EXTRA;
        }
        return value;
    }

    private void setJournalMode(String newValue) {
    private void setJournalMode(@SQLiteDatabase.JournalMode String newValue) {
        if (TextUtils.isEmpty(newValue)) {
            // No change to the journal mode is intended
            return;
        }
        String value = executeForString("PRAGMA journal_mode", null, null);
        if (!value.equalsIgnoreCase(newValue)) {
            try {
@@ -565,9 +568,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        // Remember what changed.
        boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
                != mConfiguration.foreignKeyConstraintsEnabled;
        boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
                & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING
                | SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL)) != 0;
        boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
        boolean customScalarFunctionsChanged = !configuration.customScalarFunctions
                .equals(mConfiguration.customScalarFunctions);
@@ -586,9 +586,19 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        if (foreignKeyModeChanged) {
            setForeignKeyModeFromConfiguration();
        }
        if (walModeChanged) {
            setWalModeFromConfiguration();

        boolean journalModeChanged = !configuration.resolveJournalMode().equalsIgnoreCase(
                mConfiguration.resolveJournalMode());
        if (journalModeChanged) {
            setJournalFromConfiguration();
        }

        boolean syncModeChanged =
                !configuration.resolveSyncMode().equalsIgnoreCase(mConfiguration.resolveSyncMode());
        if (syncModeChanged) {
            setSyncModeFromConfiguration();
        }

        if (localeChanged) {
            setLocaleFromConfiguration();
        }
+9 −9
Original line number Diff line number Diff line
@@ -290,8 +290,11 @@ public final class SQLiteConnectionPool implements Closeable {
        synchronized (mLock) {
            throwIfClosedLocked();

            boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
                    & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
            boolean isWalCurrentMode = mConfiguration.resolveJournalMode().equalsIgnoreCase(
                    SQLiteDatabase.JOURNAL_MODE_WAL);
            boolean isWalNewMode = configuration.resolveJournalMode().equalsIgnoreCase(
                    SQLiteDatabase.JOURNAL_MODE_WAL);
            boolean walModeChanged = isWalCurrentMode ^ isWalNewMode;
            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.
@@ -1042,8 +1045,7 @@ public final class SQLiteConnectionPool implements Closeable {
    }

    private void setMaxConnectionPoolSizeLocked() {
        if (!mConfiguration.isInMemoryDb()
                && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
        if (mConfiguration.resolveJournalMode().equalsIgnoreCase(SQLiteDatabase.JOURNAL_MODE_WAL)) {
            mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
        } else {
            // We don't actually need to always restrict the connection pool size to 1
@@ -1131,11 +1133,9 @@ public final class SQLiteConnectionPool implements Closeable {
            }
            printer.println("  Configuration: openFlags=" + mConfiguration.openFlags
                    + ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled
                    + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode)
                    + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode));
            boolean isReadOnlyDatabase =
                    (mConfiguration.openFlags & SQLiteDatabase.OPEN_READONLY) != 0;
            printer.println("  IsReadOnlyDatabase=" + isReadOnlyDatabase);
                    + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.resolveJournalMode())
                    + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.resolveSyncMode()));
            printer.println("  IsReadOnlyDatabase=" + mConfiguration.isReadOnlyDatabase());

            if (isCompatibilityWalEnabled) {
                printer.println("  Compatibility WAL enabled: wal_syncmode="
+191 −22
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
@@ -287,15 +289,182 @@ public final class SQLiteDatabase extends SQLiteClosable {
     */
    public static final int MAX_SQL_CACHE_SIZE = 100;

    private SQLiteDatabase(final String path, final int openFlags,
            CursorFactory cursorFactory, DatabaseErrorHandler errorHandler,
    /**
     * @hide
     */
    @StringDef(prefix = {"JOURNAL_MODE_"},
            value =
                    {
                            JOURNAL_MODE_WAL,
                            JOURNAL_MODE_PERSIST,
                            JOURNAL_MODE_TRUNCATE,
                            JOURNAL_MODE_MEMORY,
                            JOURNAL_MODE_DELETE,
                            JOURNAL_MODE_OFF,
                    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface JournalMode {}

    /**
     * The {@code WAL} journaling mode uses a write-ahead log instead of a rollback journal to
     * implement transactions. The WAL journaling mode is persistent; after being set it stays
     * in effect across multiple database connections and after closing and reopening the database.
     *
     * Performance Considerations:
     * This mode is recommended when the goal is to improve write performance or parallel read/write
     * performance. However, it is important to note that WAL introduces checkpoints which commit
     * all transactions that have not been synced to the database thus to maximize read performance
     * and lower checkpointing cost a small journal size is recommended. However, other modes such
     * as {@code DELETE} will not perform checkpoints, so it is a trade off that needs to be
     * considered as part of the decision of which journal mode to use.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_journal_mode>here</a> for more
     * details.</p>
     */
    public static final String JOURNAL_MODE_WAL = "WAL";

    /**
     * The {@code PERSIST} journaling mode prevents the rollback journal from being deleted at the
     * end of each transaction. Instead, the header of the journal is overwritten with zeros.
     * This will prevent other database connections from rolling the journal back.
     *
     * This mode is useful as an optimization on platforms where deleting or truncating a file is
     * much more expensive than overwriting the first block of a file with zeros.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_journal_mode>here</a> for more
     * details.</p>
     */
    public static final String JOURNAL_MODE_PERSIST = "PERSIST";

    /**
     * The {@code TRUNCATE} journaling mode commits transactions by truncating the rollback journal
     * to zero-length instead of deleting it. On many systems, truncating a file is much faster than
     * deleting the file since the containing directory does not need to be changed.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_journal_mode>here</a> for more
     * details.</p>
     */
    public static final String JOURNAL_MODE_TRUNCATE = "TRUNCATE";

    /**
     * The {@code MEMORY} journaling mode stores the rollback journal in volatile RAM.
     * This saves disk I/O but at the expense of database safety and integrity. If the application
     * using SQLite crashes in the middle of a transaction when the MEMORY journaling mode is set,
     * then the database file will very likely go corrupt.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_journal_mode>here</a> for more
     * details.</p>
     */
    public static final String JOURNAL_MODE_MEMORY = "MEMORY";

    /**
     * The {@code DELETE} journaling mode is the normal behavior. In the DELETE mode, the rollback
     * journal is deleted at the conclusion of each transaction.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_journal_mode>here</a> for more
     * details.</p>
     */
    public static final String JOURNAL_MODE_DELETE = "DELETE";

    /**
     * The {@code OFF} journaling mode disables the rollback journal completely. No rollback journal
     * is ever created and hence there is never a rollback journal to delete. The OFF journaling
     * mode disables the atomic commit and rollback capabilities of SQLite. The ROLLBACK command
     * behaves in an undefined way thus applications must avoid using the ROLLBACK command.
     * If the application crashes in the middle of a transaction, then the database file will very
     * likely go corrupt.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_journal_mode>here</a> for more
     * details.</p>
     */
    public static final String JOURNAL_MODE_OFF = "OFF";

    /**
     * @hide
     */
    @StringDef(prefix = {"SYNC_MODE_"},
            value =
                    {
                            SYNC_MODE_EXTRA,
                            SYNC_MODE_FULL,
                            SYNC_MODE_NORMAL,
                            SYNC_MODE_OFF,
                    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SyncMode {}

    /**
     * The {@code EXTRA} sync mode is like {@code FULL} sync mode with the addition that the
     * directory containing a rollback journal is synced after that journal is unlinked to commit a
     * transaction in {@code DELETE} journal mode.
     *
     * {@code EXTRA} provides additional durability if the commit is followed closely by a
     * power loss.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_synchronous>here</a> for more
     * details.</p>
     */
    @SuppressLint("IntentName") public static final String SYNC_MODE_EXTRA = "EXTRA";

    /**
     * In {@code FULL} sync mode the SQLite database engine will use the xSync method of the VFS
     * to ensure that all content is safely written to the disk surface prior to continuing.
     * This ensures that an operating system crash or power failure will not corrupt the database.
     * {@code FULL} is very safe, but it is also slower.
     *
     * {@code FULL} is the most commonly used synchronous setting when not in WAL mode.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_synchronous>here</a> for more
     * details.</p>
     */
    public static final String SYNC_MODE_FULL = "FULL";

    /**
     * The {@code NORMAL} sync mode, the SQLite database engine will still sync at the most critical
     * moments, but less often than in {@code FULL} mode. There is a very small chance that a
     * power failure at the wrong time could corrupt the database in {@code DELETE} journal mode on
     * an older filesystem.
     *
     * {@code WAL} journal mode is safe from corruption with {@code NORMAL} sync mode, and probably
     * {@code DELETE} sync mode is safe too on modern filesystems. WAL mode is always consistent
     * with {@code NORMAL} sync mode, but WAL mode does lose durability. A transaction committed in
     * WAL mode with {@code NORMAL} might roll back following a power loss or system crash.
     * Transactions are durable across application crashes regardless of the synchronous setting
     * or journal mode.
     *
     * The {@code NORMAL} sync mode is a good choice for most applications running in WAL mode.
     *
     * <p>Caveat: Even though this sync mode is safe Be careful when using {@code NORMAL} sync mode
     * when dealing with data dependencies between multiple databases, unless those databases use
     * the same durability or are somehow synced, there could be corruption.</p>
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_synchronous>here</a> for more
     * details.</p>
     */
    public static final String SYNC_MODE_NORMAL = "NORMAL";

    /**
     * In {@code OFF} sync mode SQLite continues without syncing as soon as it has handed data off
     * to the operating system. If the application running SQLite crashes, the data will be safe,
     * but the database might become corrupted if the operating system crashes or the computer loses
     * power before that data has been written to the disk surface. On the other hand, commits can
     * be orders of magnitude faster with synchronous {@code OFF}.
     *
     * <p> See <a href=https://www.sqlite.org/pragma.html#pragma_synchronous>here</a> for more
     * details.</p>
     */
    public static final String SYNC_MODE_OFF = "OFF";

    private SQLiteDatabase(@Nullable final String path, @Nullable final int openFlags,
            @Nullable CursorFactory cursorFactory, @Nullable DatabaseErrorHandler errorHandler,
            int lookasideSlotSize, int lookasideSlotCount, long idleConnectionTimeoutMs,
            String journalMode, String syncMode) {
            @Nullable String journalMode, @Nullable String syncMode) {
        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;
@@ -313,11 +482,11 @@ public final class SQLiteDatabase extends SQLiteClosable {
            }
        }
        mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
        mConfigurationLocked.journalMode = journalMode;
        mConfigurationLocked.syncMode = syncMode;
        if (SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()) {
            mConfigurationLocked.openFlags |= ENABLE_LEGACY_COMPATIBILITY_WAL;
        }
        mConfigurationLocked.journalMode = journalMode;
        mConfigurationLocked.syncMode = syncMode;
    }

    @Override
@@ -2188,7 +2357,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
        synchronized (mLock) {
            throwIfNotOpenLocked();

            if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0) {
            if (mConfigurationLocked.resolveJournalMode().equalsIgnoreCase(
                        SQLiteDatabase.JOURNAL_MODE_WAL)) {
                return true;
            }

@@ -2238,11 +2408,9 @@ public final class SQLiteDatabase extends SQLiteClosable {
            throwIfNotOpenLocked();

            final int oldFlags = mConfigurationLocked.openFlags;
            final boolean walEnabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0;
            final boolean compatibilityWalEnabled =
                    (oldFlags & ENABLE_LEGACY_COMPATIBILITY_WAL) != 0;
            // WAL was never enabled for this database, so there's nothing left to do.
            if (!walEnabled && !compatibilityWalEnabled) {
            if (!mConfigurationLocked.resolveJournalMode().equalsIgnoreCase(
                        SQLiteDatabase.JOURNAL_MODE_WAL)) {
                return;
            }

@@ -2272,7 +2440,8 @@ public final class SQLiteDatabase extends SQLiteClosable {
        synchronized (mLock) {
            throwIfNotOpenLocked();

            return (mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0;
            return mConfigurationLocked.resolveJournalMode().equalsIgnoreCase(
                    SQLiteDatabase.JOURNAL_MODE_WAL);
        }
    }

@@ -2306,7 +2475,6 @@ public final class SQLiteDatabase extends SQLiteClosable {
        return databases;
    }

    @UnsupportedAppUsage
    private static ArrayList<SQLiteConnectionPool> getActiveDatabasePools() {
        ArrayList<SQLiteConnectionPool> connectionPools = new ArrayList<SQLiteConnectionPool>();
        synchronized (sActiveDatabases) {
@@ -2638,9 +2806,7 @@ public final class SQLiteDatabase extends SQLiteClosable {

        /**
         * Returns <a href="https://sqlite.org/pragma.html#pragma_journal_mode">journal mode</a>.
         * This journal mode will only be used if {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING}
         * flag is not set, otherwise a platform will use "WAL" journal mode.
         * @see Builder#setJournalMode(String)
         * set via {@link Builder#setJournalMode(String)}.
         */
        @Nullable
        public String getJournalMode() {
@@ -2839,25 +3005,28 @@ public final class SQLiteDatabase extends SQLiteClosable {
                return this;
            }


            /**
             * Sets <a href="https://sqlite.org/pragma.html#pragma_journal_mode">journal mode</a>
             * to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag is not set.
             * to use.
             *
             * <p>Note: If journal mode is not set, the platform will use a manufactured-specified
             * default which can vary across devices.
             */
            @NonNull
            public Builder setJournalMode(@NonNull  String journalMode) {
            public Builder setJournalMode(@JournalMode @NonNull String journalMode) {
                Objects.requireNonNull(journalMode);
                mJournalMode = journalMode;
                return this;
            }

            /**w
            /**
             * Sets <a href="https://sqlite.org/pragma.html#pragma_synchronous">synchronous mode</a>
             * .
             * @return
             *
             * <p>Note: If sync mode is not set, the platform will use a manufactured-specified
             * default which can vary across devices.
             */
            @NonNull
            public Builder setSynchronousMode(@NonNull String syncMode) {
            public Builder setSynchronousMode(@SyncMode @NonNull String syncMode) {
                Objects.requireNonNull(syncMode);
                mSyncMode = syncMode;
                return this;
+86 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading