1 /*
<lambda>null2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 @file:JvmMultifileClass
17 @file:JvmName("RoomDatabaseKt")
18 
19 package androidx.room
20 
21 import android.annotation.SuppressLint
22 import android.app.ActivityManager
23 import android.content.Context
24 import android.content.Intent
25 import android.database.Cursor
26 import android.os.CancellationSignal
27 import android.os.Looper
28 import android.util.Log
29 import androidx.annotation.CallSuper
30 import androidx.annotation.IntRange
31 import androidx.annotation.RestrictTo
32 import androidx.annotation.WorkerThread
33 import androidx.arch.core.executor.ArchTaskExecutor
34 import androidx.room.Room.LOG_TAG
35 import androidx.room.concurrent.CloseBarrier
36 import androidx.room.coroutines.runBlockingUninterruptible
37 import androidx.room.driver.SupportSQLiteConnection
38 import androidx.room.migration.AutoMigrationSpec
39 import androidx.room.migration.Migration
40 import androidx.room.support.AutoCloser
41 import androidx.room.support.AutoClosingRoomOpenHelper
42 import androidx.room.support.AutoClosingRoomOpenHelperFactory
43 import androidx.room.support.PrePackagedCopyOpenHelper
44 import androidx.room.support.PrePackagedCopyOpenHelperFactory
45 import androidx.room.support.QueryInterceptorOpenHelperFactory
46 import androidx.room.util.contains as containsCommon
47 import androidx.room.util.findAndInstantiateDatabaseImpl
48 import androidx.room.util.findMigrationPath as findMigrationPathExt
49 import androidx.room.util.performBlocking
50 import androidx.sqlite.SQLiteConnection
51 import androidx.sqlite.SQLiteDriver
52 import androidx.sqlite.db.SimpleSQLiteQuery
53 import androidx.sqlite.db.SupportSQLiteDatabase
54 import androidx.sqlite.db.SupportSQLiteOpenHelper
55 import androidx.sqlite.db.SupportSQLiteQuery
56 import androidx.sqlite.db.SupportSQLiteStatement
57 import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
58 import java.io.File
59 import java.io.InputStream
60 import java.util.TreeMap
61 import java.util.concurrent.Callable
62 import java.util.concurrent.Executor
63 import java.util.concurrent.RejectedExecutionException
64 import java.util.concurrent.TimeUnit
65 import java.util.concurrent.atomic.AtomicInteger
66 import kotlin.coroutines.ContinuationInterceptor
67 import kotlin.coroutines.CoroutineContext
68 import kotlin.coroutines.coroutineContext
69 import kotlin.coroutines.resume
70 import kotlin.reflect.KClass
71 import kotlinx.coroutines.CoroutineDispatcher
72 import kotlinx.coroutines.CoroutineScope
73 import kotlinx.coroutines.ExperimentalCoroutinesApi
74 import kotlinx.coroutines.Job
75 import kotlinx.coroutines.SupervisorJob
76 import kotlinx.coroutines.asContextElement
77 import kotlinx.coroutines.asCoroutineDispatcher
78 import kotlinx.coroutines.asExecutor
79 import kotlinx.coroutines.cancel
80 import kotlinx.coroutines.flow.Flow
81 import kotlinx.coroutines.runBlocking
82 import kotlinx.coroutines.suspendCancellableCoroutine
83 import kotlinx.coroutines.withContext
84 
85 /**
86  * Base class for all Room databases. All classes that are annotated with [Database] must extend
87  * this class.
88  *
89  * RoomDatabase provides direct access to the underlying database implementation but you should
90  * prefer using [Dao] classes.
91  *
92  * @constructor You cannot create an instance of a database, instead, you should acquire it via
93  *   [#Room.databaseBuilder] or [#Room.inMemoryDatabaseBuilder].
94  * @see Database
95  */
96 actual abstract class RoomDatabase {
97     @Volatile
98     @JvmField
99     @Deprecated(
100         message = "This property is always null and will be removed in a future version.",
101         level = DeprecationLevel.ERROR
102     )
103     protected var mDatabase: SupportSQLiteDatabase? = null
104 
105     private lateinit var coroutineScope: CoroutineScope
106     private lateinit var transactionContext: CoroutineContext
107 
108     /** The Executor in use by this database for async queries. */
109     open val queryExecutor: Executor
110         get() = internalQueryExecutor
111 
112     private lateinit var internalQueryExecutor: Executor
113 
114     /** The Executor in use by this database for async transactions. */
115     open val transactionExecutor: Executor
116         get() = internalTransactionExecutor
117 
118     private lateinit var internalTransactionExecutor: Executor
119 
120     /**
121      * The SQLite open helper used by this database.
122      *
123      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
124      */
125     open val openHelper: SupportSQLiteOpenHelper
126         get() =
127             connectionManager.supportOpenHelper
128                 ?: error(
129                     "Cannot return a SupportSQLiteOpenHelper since no " +
130                         "SupportSQLiteOpenHelper.Factory was configured with Room."
131                 )
132 
133     private lateinit var connectionManager: RoomConnectionManager
134 
135     /**
136      * The invalidation tracker for this database.
137      *
138      * You can use the invalidation tracker to get notified when certain tables in the database are
139      * modified.
140      *
141      * @return The invalidation tracker for the database.
142      */
143     actual open val invalidationTracker: InvalidationTracker
144         get() = internalTracker
145 
146     private lateinit var internalTracker: InvalidationTracker
147 
148     /**
149      * A barrier that prevents the database from closing while the [InvalidationTracker] is using
150      * the database asynchronously.
151      *
152      * @return The barrier for [close].
153      */
154     internal actual val closeBarrier = CloseBarrier(::onClosed)
155 
156     private var allowMainThreadQueries = false
157 
158     @JvmField
159     @Deprecated(
160         message = "This property is always null and will be removed in a future version.",
161         level = DeprecationLevel.ERROR
162     )
163     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
164     protected var mCallbacks: List<Callback>? = null
165 
166     private var autoCloser: AutoCloser? = null
167 
168     /**
169      * Suspending transaction id of the current thread.
170      *
171      * This id is only set on threads that are used to dispatch coroutines within a suspending
172      * database transaction.
173      */
174     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val suspendingTransactionId = ThreadLocal<Int>()
175 
176     private val typeConverters: MutableMap<KClass<*>, Any> = mutableMapOf()
177 
178     internal var useTempTrackingTable: Boolean = true
179 
180     /**
181      * Gets the instance of the given Type Converter.
182      *
183      * @param klass The Type Converter class.
184      * @param T The type of the expected Type Converter subclass.
185      * @return An instance of T if it is provided in the builder.
186      */
187     @Deprecated("No longer called by generated implementation")
188     @Suppress("UNCHECKED_CAST")
189     open fun <T : Any> getTypeConverter(klass: Class<T>): T? {
190         return typeConverters[klass.kotlin] as T?
191     }
192 
193     /**
194      * Gets the instance of the given type converter class.
195      *
196      * This method should only be called by the generated DAO implementations.
197      *
198      * @param klass The Type Converter class.
199      * @param T The type of the expected Type Converter subclass.
200      * @return An instance of T.
201      */
202     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
203     @Suppress("UNCHECKED_CAST")
204     actual fun <T : Any> getTypeConverter(klass: KClass<T>): T {
205         return typeConverters[klass] as T
206     }
207 
208     /**
209      * Adds a provided type converter to be used in the database DAOs.
210      *
211      * @param kclass the class of the type converter
212      * @param converter an instance of the converter
213      */
214     internal actual fun addTypeConverter(kclass: KClass<*>, converter: Any) {
215         typeConverters[kclass] = converter
216     }
217 
218     /**
219      * Called by Room when it is initialized.
220      *
221      * @param configuration The database configuration.
222      * @throws IllegalArgumentException if initialization fails.
223      */
224     @CallSuper
225     @OptIn(ExperimentalCoroutinesApi::class) // For limitedParallelism(1)
226     actual open fun init(configuration: DatabaseConfiguration) {
227         useTempTrackingTable = configuration.useTempTrackingTable
228 
229         connectionManager = createConnectionManager(configuration)
230         internalTracker = createInvalidationTracker()
231         validateAutoMigrations(configuration)
232         validateTypeConverters(configuration)
233 
234         if (configuration.queryCoroutineContext != null) {
235             // For backwards compatibility with internals not converted to Coroutines, use the
236             // provided dispatcher as executor.
237             val dispatcher =
238                 configuration.queryCoroutineContext[ContinuationInterceptor] as CoroutineDispatcher
239             internalQueryExecutor = dispatcher.asExecutor()
240             internalTransactionExecutor = TransactionExecutor(internalQueryExecutor)
241             // For Room's coroutine scope, we use the provided context but add a SupervisorJob that
242             // is tied to the given Job (if any).
243             val parentJob = configuration.queryCoroutineContext[Job]
244             coroutineScope =
245                 CoroutineScope(configuration.queryCoroutineContext + SupervisorJob(parentJob))
246             transactionContext =
247                 if (inCompatibilityMode()) {
248                     // To prevent starvation due to primary connection blocking in
249                     // SupportSQLiteDatabase a limited dispatcher is used for transactions.
250                     coroutineScope.coroutineContext + dispatcher.limitedParallelism(1)
251                 } else {
252                     // When a SQLiteDriver is provided a suspending connection pool is used and
253                     // there is no reason to limit parallelism.
254                     coroutineScope.coroutineContext
255                 }
256         } else {
257             internalQueryExecutor = configuration.queryExecutor
258             internalTransactionExecutor = TransactionExecutor(configuration.transactionExecutor)
259             // For Room's coroutine scope, we use the provided executor as dispatcher along with a
260             // SupervisorJob.
261             coroutineScope =
262                 CoroutineScope(internalQueryExecutor.asCoroutineDispatcher() + SupervisorJob())
263             transactionContext =
264                 coroutineScope.coroutineContext +
265                     internalTransactionExecutor.asCoroutineDispatcher()
266         }
267 
268         allowMainThreadQueries = configuration.allowMainThreadQueries
269 
270         // Configure PrePackagedCopyOpenHelper if it is available
271         unwrapOpenHelper<PrePackagedCopyOpenHelper>(connectionManager.supportOpenHelper)
272             ?.setDatabaseConfiguration(configuration)
273 
274         // Configure AutoClosingRoomOpenHelper if it is available
275         unwrapOpenHelper<AutoClosingRoomOpenHelper>(connectionManager.supportOpenHelper)?.let {
276             autoCloser = it.autoCloser
277             it.autoCloser.initCoroutineScope(coroutineScope)
278             invalidationTracker.setAutoCloser(it.autoCloser)
279         }
280 
281         // Configure multi-instance invalidation, if enabled
282         if (configuration.multiInstanceInvalidationServiceIntent != null) {
283             requireNotNull(configuration.name)
284             invalidationTracker.initMultiInstanceInvalidation(
285                 configuration.context,
286                 configuration.name,
287                 configuration.multiInstanceInvalidationServiceIntent
288             )
289         }
290     }
291 
292     /**
293      * Creates a connection manager to manage database connection. Note that this method is called
294      * when the [RoomDatabase] is initialized.
295      *
296      * @return A new connection manager.
297      */
298     internal actual fun createConnectionManager(
299         configuration: DatabaseConfiguration
300     ): RoomConnectionManager {
301         val openDelegate =
302             try {
303                 createOpenDelegate() as RoomOpenDelegate
304             } catch (ex: NotImplementedError) {
305                 null
306             }
307         // If createOpenDelegate() is not implemented then the database implementation was
308         // generated with an older compiler, we are force to create a connection manager
309         // using the SupportSQLiteOpenHelper returned from createOpenHelper() with the
310         // deprecated RoomOpenHelper installed.
311         return if (openDelegate == null) {
312             @Suppress("DEPRECATION")
313             RoomConnectionManager(
314                 config = configuration,
315                 supportOpenHelperFactory = { config -> createOpenHelper(config) }
316             )
317         } else {
318             RoomConnectionManager(config = configuration, openDelegate = openDelegate)
319         }
320     }
321 
322     /**
323      * Returns a list of [Migration] of a database that have been automatically generated.
324      *
325      * @param autoMigrationSpecs
326      * @return A list of migration instances each of which is a generated autoMigration
327      */
328     @Deprecated("No longer implemented by generated")
329     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
330     @JvmSuppressWildcards // Suppress wildcards due to generated Java code
331     open fun getAutoMigrations(
332         autoMigrationSpecs: Map<Class<out AutoMigrationSpec>, AutoMigrationSpec>
333     ): List<Migration> {
334         return emptyList()
335     }
336 
337     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
338     actual open fun createAutoMigrations(
339         autoMigrationSpecs: Map<KClass<out AutoMigrationSpec>, AutoMigrationSpec>
340     ): List<Migration> {
341         // For backwards compatibility when newer runtime is used with older generated code,
342         // call the Java version of getAutoMigrations()
343         val javaClassesMap = autoMigrationSpecs.mapKeys { it.key.java }
344         @Suppress("DEPRECATION") return getAutoMigrations(javaClassesMap)
345     }
346 
347     /**
348      * Unwraps (delegating) open helpers until it finds [T], otherwise returns null.
349      *
350      * @param openHelper the open helper to search through
351      * @param T the type of open helper type to search for
352      * @return the instance of [T], otherwise null
353      */
354     private inline fun <reified T : SupportSQLiteOpenHelper> unwrapOpenHelper(
355         openHelper: SupportSQLiteOpenHelper?
356     ): T? {
357         if (openHelper == null) {
358             return null
359         }
360         var current: SupportSQLiteOpenHelper = openHelper
361         while (true) {
362             if (current is T) {
363                 return current
364             }
365             if (current is DelegatingOpenHelper) {
366                 current = current.delegate
367             } else {
368                 break
369             }
370         }
371         return null
372     }
373 
374     /**
375      * Creates the open helper to access the database. Generated class already implements this
376      * method. Note that this method is called when the RoomDatabase is initialized.
377      *
378      * @param config The configuration of the Room database.
379      * @return A new SupportSQLiteOpenHelper to be used while connecting to the database.
380      * @throws NotImplementedError by default
381      */
382     @Deprecated("No longer implemented by generated")
383     protected open fun createOpenHelper(config: DatabaseConfiguration): SupportSQLiteOpenHelper {
384         throw NotImplementedError()
385     }
386 
387     /**
388      * Creates a delegate to configure and initialize the database when it is being opened. An
389      * implementation of this function is generated by the Room processor. Note that this method is
390      * called when the [RoomDatabase] is initialized.
391      *
392      * @return A new delegate to be used while opening the database
393      * @throws NotImplementedError by default
394      */
395     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
396     protected actual open fun createOpenDelegate(): RoomOpenDelegateMarker {
397         throw NotImplementedError()
398     }
399 
400     /**
401      * Creates the invalidation tracker
402      *
403      * An implementation of this function is generated by the Room processor. Note that this method
404      * is called when the [RoomDatabase] is initialized.
405      *
406      * @return A new invalidation tracker.
407      */
408     protected actual abstract fun createInvalidationTracker(): InvalidationTracker
409 
410     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
411     actual fun getCoroutineScope(): CoroutineScope {
412         return coroutineScope
413     }
414 
415     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
416     fun getQueryContext(): CoroutineContext {
417         return coroutineScope.coroutineContext
418     }
419 
420     internal fun getTransactionContext(): CoroutineContext {
421         return transactionContext
422     }
423 
424     /**
425      * Returns a Map of String -> List&lt;Class&gt; where each entry has the `key` as the DAO name
426      * and `value` as the list of type converter classes that are necessary for the database to
427      * function.
428      *
429      * This is implemented by the generated code.
430      *
431      * @return Creates a map that will include all required type converters for this database.
432      */
433     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
434     protected open fun getRequiredTypeConverters(): Map<Class<*>, List<Class<*>>> {
435         return emptyMap()
436     }
437 
438     /**
439      * Returns a Map of String -> List&lt;KClass&gt; where each entry has the `key` as the DAO name
440      * and `value` as the list of type converter classes that are necessary for the database to
441      * function.
442      *
443      * An implementation of this function is generated by the Room processor. Note that this method
444      * is called when the [RoomDatabase] is initialized.
445      *
446      * @return A map that will include all required type converters for this database.
447      */
448     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
449     protected actual open fun getRequiredTypeConverterClasses(): Map<KClass<*>, List<KClass<*>>> {
450         // For backwards compatibility when newer runtime is used with older generated code,
451         // call the Java version this function.
452         return getRequiredTypeConverters().entries.associate { (key, value) ->
453             key.kotlin to value.map { it.kotlin }
454         }
455     }
456 
457     /** Property delegate of [getRequiredTypeConverterClasses] for common ext functionality. */
458     internal actual val requiredTypeConverterClassesMap: Map<KClass<*>, List<KClass<*>>>
459         get() = getRequiredTypeConverterClasses()
460 
461     /**
462      * Returns a Set of required AutoMigrationSpec classes.
463      *
464      * This is implemented by the generated code.
465      *
466      * @return Creates a set that will include all required auto migration specs for this database.
467      */
468     @Deprecated("No longer implemented by generated")
469     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
470     open fun getRequiredAutoMigrationSpecs(): Set<Class<out AutoMigrationSpec>> {
471         return emptySet()
472     }
473 
474     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
475     actual open fun getRequiredAutoMigrationSpecClasses(): Set<KClass<out AutoMigrationSpec>> {
476         // For backwards compatibility when newer runtime is used with older generated code,
477         // call the Java version of this function.
478         @Suppress("DEPRECATION") return getRequiredAutoMigrationSpecs().map { it.kotlin }.toSet()
479     }
480 
481     /**
482      * Deletes all rows from all the tables that are registered to this database as
483      * [Database.entities].
484      *
485      * This does NOT reset the auto-increment value generated by [PrimaryKey.autoGenerate].
486      *
487      * After deleting the rows, Room will set a WAL checkpoint and run VACUUM. This means that the
488      * data is completely erased. The space will be reclaimed by the system if the amount surpasses
489      * the threshold of database file size.
490      *
491      * See SQLite documentation for details. [FileFormat](https://www.sqlite.org/fileformat.html)
492      */
493     @WorkerThread abstract fun clearAllTables()
494 
495     /**
496      * Performs a 'clear all tables' operation.
497      *
498      * This should only be invoked from generated code.
499      *
500      * @see [RoomDatabase.clearAllTables]
501      */
502     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
503     protected fun performClear(hasForeignKeys: Boolean, vararg tableNames: String) {
504         assertNotMainThread()
505         assertNotSuspendingTransaction()
506         runBlockingUninterruptible {
507             connectionManager.useConnection(isReadOnly = false) { connection ->
508                 if (!connection.inTransaction()) {
509                     invalidationTracker.sync()
510                 }
511                 connection.withTransaction(Transactor.SQLiteTransactionType.IMMEDIATE) {
512                     if (hasForeignKeys) {
513                         execSQL("PRAGMA defer_foreign_keys = TRUE")
514                     }
515                     tableNames.forEach { tableName -> execSQL("DELETE FROM `$tableName`") }
516                 }
517                 if (!connection.inTransaction()) {
518                     connection.execSQL("PRAGMA wal_checkpoint(FULL)")
519                     connection.execSQL("VACUUM")
520                     invalidationTracker.refreshAsync()
521                 }
522             }
523         }
524     }
525 
526     /**
527      * True if database connection is open and initialized.
528      *
529      * When Room is configured with [RoomDatabase.Builder.setAutoCloseTimeout] the database is
530      * considered open even if internally the connection has been closed, unless manually closed.
531      *
532      * @return true if the database connection is open, false otherwise.
533      */
534     open val isOpen: Boolean
535         get() = autoCloser?.isActive ?: connectionManager.isSupportDatabaseOpen()
536 
537     /** True if the actual database connection is open, regardless of auto-close. */
538     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
539     val isOpenInternal: Boolean
540         get() = connectionManager.isSupportDatabaseOpen()
541 
542     /**
543      * Closes the database.
544      *
545      * Once a [RoomDatabase] is closed it should no longer be used.
546      */
547     actual open fun close() {
548         closeBarrier.close()
549     }
550 
551     private fun onClosed() {
552         coroutineScope.cancel()
553         invalidationTracker.stop()
554         connectionManager.close()
555     }
556 
557     /** True if the calling thread is the main thread. */
558     internal val isMainThread: Boolean
559         get() = Looper.getMainLooper().thread === Thread.currentThread()
560 
561     /** Asserts that we are not on the main thread. */
562     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
563     open fun assertNotMainThread() {
564         if (allowMainThreadQueries) {
565             return
566         }
567         check(!isMainThread) {
568             "Cannot access database on the main thread since" +
569                 " it may potentially lock the UI for a long period of time."
570         }
571     }
572 
573     /** Asserts that we are not on a suspending transaction. */
574     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
575     open fun assertNotSuspendingTransaction() {
576         check(!inCompatibilityMode() || inTransaction() || suspendingTransactionId.get() == null) {
577             "Cannot access database on a different coroutine" +
578                 " context inherited from a suspending transaction."
579         }
580     }
581 
582     /**
583      * Use a connection to perform database operations.
584      *
585      * This function is for internal access to the pool, it is an unconfined coroutine function to
586      * be used by Room generated code paths. For the public version see [useReaderConnection] and
587      * [useWriterConnection].
588      */
589     internal actual suspend fun <R> useConnection(
590         isReadOnly: Boolean,
591         block: suspend (Transactor) -> R
592     ): R {
593         return connectionManager.useConnection(isReadOnly, block)
594     }
595 
596     /**
597      * Return true if this database is operating in compatibility mode, otherwise false.
598      *
599      * Room is considered in compatibility mode in Android when no [SQLiteDriver] was provided and
600      * [androidx.sqlite.db] APIs are used instead (SupportSQLite*).
601      *
602      * @see RoomConnectionManager
603      */
604     internal fun inCompatibilityMode(): Boolean = connectionManager.supportOpenHelper != null
605 
606     // Below, there are wrapper methods for SupportSQLiteDatabase. This helps us track which
607     // methods we are using and also helps unit tests to mock this class without mocking
608     // all SQLite database methods.
609     /**
610      * Convenience method to query the database with arguments.
611      *
612      * @param query The sql query
613      * @param args The bind arguments for the placeholders in the query
614      * @return A Cursor obtained by running the given query in the Room database.
615      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
616      */
617     open fun query(query: String, args: Array<out Any?>?): Cursor {
618         assertNotMainThread()
619         assertNotSuspendingTransaction()
620         return openHelper.writableDatabase.query(SimpleSQLiteQuery(query, args))
621     }
622 
623     /**
624      * Wrapper for [SupportSQLiteDatabase.query].
625      *
626      * @param query The Query which includes the SQL and a bind callback for bind arguments.
627      * @param signal The cancellation signal to be attached to the query.
628      * @return Result of the query.
629      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
630      */
631     @JvmOverloads
632     open fun query(query: SupportSQLiteQuery, signal: CancellationSignal? = null): Cursor {
633         assertNotMainThread()
634         assertNotSuspendingTransaction()
635         return if (signal != null) {
636             openHelper.writableDatabase.query(query, signal)
637         } else {
638             openHelper.writableDatabase.query(query)
639         }
640     }
641 
642     /**
643      * Wrapper for [SupportSQLiteDatabase.compileStatement].
644      *
645      * @param sql The query to compile.
646      * @return The compiled query.
647      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
648      */
649     open fun compileStatement(sql: String): SupportSQLiteStatement {
650         assertNotMainThread()
651         assertNotSuspendingTransaction()
652         return openHelper.writableDatabase.compileStatement(sql)
653     }
654 
655     /**
656      * Wrapper for [SupportSQLiteDatabase.beginTransaction].
657      *
658      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
659      */
660     @Deprecated("beginTransaction() is deprecated", ReplaceWith("runInTransaction(Runnable)"))
661     open fun beginTransaction() {
662         assertNotMainThread()
663         val autoCloser = autoCloser
664         if (autoCloser == null) {
665             internalBeginTransaction()
666         } else {
667             autoCloser.executeRefCountingFunction { internalBeginTransaction() }
668         }
669     }
670 
671     private fun internalBeginTransaction() {
672         assertNotMainThread()
673         val database = openHelper.writableDatabase
674         if (!database.inTransaction()) {
675             invalidationTracker.syncBlocking()
676         }
677         if (database.isWriteAheadLoggingEnabled) {
678             database.beginTransactionNonExclusive()
679         } else {
680             database.beginTransaction()
681         }
682     }
683 
684     /**
685      * Wrapper for [SupportSQLiteDatabase.endTransaction].
686      *
687      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
688      */
689     @Deprecated("endTransaction() is deprecated", ReplaceWith("runInTransaction(Runnable)"))
690     open fun endTransaction() {
691         val autoCloser = autoCloser
692         if (autoCloser == null) {
693             internalEndTransaction()
694         } else {
695             autoCloser.executeRefCountingFunction { internalEndTransaction() }
696         }
697     }
698 
699     private fun internalEndTransaction() {
700         openHelper.writableDatabase.endTransaction()
701         if (!inTransaction()) {
702             // enqueue refresh only if we are NOT in a transaction. Otherwise, wait for the last
703             // endTransaction call to do it.
704             invalidationTracker.refreshVersionsAsync()
705         }
706     }
707 
708     /**
709      * Wrapper for [SupportSQLiteDatabase.setTransactionSuccessful].
710      *
711      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
712      */
713     @Deprecated(
714         "setTransactionSuccessful() is deprecated",
715         ReplaceWith("runInTransaction(Runnable)")
716     )
717     open fun setTransactionSuccessful() {
718         openHelper.writableDatabase.setTransactionSuccessful()
719     }
720 
721     /**
722      * Executes the specified [Runnable] in a database transaction. The transaction will be marked
723      * as successful unless an exception is thrown in the [Runnable].
724      *
725      * Room will only perform at most one transaction at a time.
726      *
727      * If a [SQLiteDriver] is configured with this database, then it is best to use
728      * [useWriterConnection] along with [immediateTransaction] to perform transactional operations.
729      *
730      * @param body The piece of code to execute.
731      */
732     open fun runInTransaction(body: Runnable) {
733         runInTransaction { body.run() }
734     }
735 
736     /**
737      * Executes the specified [Callable] in a database transaction. The transaction will be marked
738      * as successful unless an exception is thrown in the [Callable].
739      *
740      * Room will only perform at most one transaction at a time.
741      *
742      * If a [SQLiteDriver] is configured with this database, then it is best to use
743      * [useWriterConnection] along with [immediateTransaction] to perform transactional operations.
744      *
745      * @param body The piece of code to execute.
746      * @param V The type of the return value.
747      * @return The value returned from the [Callable].
748      */
749     open fun <V> runInTransaction(body: Callable<V>): V {
750         return runInTransaction { body.call() }
751     }
752 
753     @Suppress("DEPRECATION") // Usage of try-finally transaction idiom APIs
754     private fun <T> runInTransaction(body: () -> T): T {
755         if (inCompatibilityMode()) {
756             beginTransaction()
757             try {
758                 val result = body.invoke()
759                 setTransactionSuccessful()
760                 return result
761             } finally {
762                 endTransaction()
763             }
764         } else {
765             return performBlocking(db = this, isReadOnly = false, inTransaction = true) {
766                 body.invoke()
767             }
768         }
769     }
770 
771     /**
772      * Initialize invalidation tracker. Note that this method is called when the [RoomDatabase] is
773      * initialized and opens a database connection.
774      *
775      * @param db The database instance.
776      */
777     @Deprecated("No longer called by generated")
778     protected open fun internalInitInvalidationTracker(db: SupportSQLiteDatabase) {
779         internalInitInvalidationTracker(SupportSQLiteConnection(db))
780     }
781 
782     /**
783      * Initialize invalidation tracker. Note that this method is called when the [RoomDatabase] is
784      * initialized and opens a database connection.
785      *
786      * @param connection The database connection.
787      */
788     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
789     protected actual fun internalInitInvalidationTracker(connection: SQLiteConnection) {
790         invalidationTracker.internalInit(connection)
791     }
792 
793     /**
794      * Wrapper for [SupportSQLiteDatabase.inTransaction]. Returns true if current thread is in a
795      * transaction.
796      *
797      * @return True if there is an active transaction in current thread, false otherwise.
798      * @throws IllegalStateException If a [SQLiteDriver] is configured with this database.
799      * @see SupportSQLiteDatabase.inTransaction
800      */
801     open fun inTransaction(): Boolean {
802         return isOpenInternal && openHelper.writableDatabase.inTransaction()
803     }
804 
805     /**
806      * Journal modes for SQLite database.
807      *
808      * @see Builder.setJournalMode
809      */
810     actual enum class JournalMode {
811         /**
812          * Let Room choose the journal mode. This is the default value when no explicit value is
813          * specified.
814          *
815          * The actual value will be [TRUNCATE] when the device runs API Level lower than 16 or it is
816          * a low-RAM device. Otherwise, [WRITE_AHEAD_LOGGING] will be used.
817          */
818         AUTOMATIC,
819 
820         /** Truncate journal mode. */
821         TRUNCATE,
822 
823         /** Write-Ahead Logging mode. */
824         WRITE_AHEAD_LOGGING;
825 
826         /** Resolves [AUTOMATIC] to either [TRUNCATE] or [WRITE_AHEAD_LOGGING]. */
827         internal fun resolve(context: Context): JournalMode {
828             if (this != AUTOMATIC) {
829                 return this
830             }
831             val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
832             if (manager != null && !manager.isLowRamDevice) {
833                 return WRITE_AHEAD_LOGGING
834             }
835             return TRUNCATE
836         }
837     }
838 
839     /**
840      * Builder for [RoomDatabase].
841      *
842      * @param T The type of the abstract database class.
843      */
844     @Suppress("GetterOnBuilder") // To keep ABI compatibility from Java
845     actual open class Builder<T : RoomDatabase> {
846         private val klass: KClass<T>
847         private val context: Context
848         private val name: String?
849         private val factory: (() -> T)?
850 
851         /**
852          * Constructor for [RoomDatabase.Builder].
853          *
854          * @param klass The abstract database class.
855          * @param name The name of the database or NULL for an in-memory database.
856          * @param factory The lambda calling `initializeImpl()` on the abstract database class which
857          *   returns the generated database implementation.
858          * @param context The context for the database, this is usually the Application context.
859          */
860         @PublishedApi
861         internal constructor(
862             klass: KClass<T>,
863             name: String?,
864             factory: (() -> T)?,
865             context: Context
866         ) {
867             this.klass = klass
868             this.context = context
869             this.name = name
870             this.factory = factory
871         }
872 
873         /**
874          * Constructor for [RoomDatabase.Builder].
875          *
876          * @param context The context for the database, this is usually the Application context.
877          * @param klass The abstract database class.
878          * @param name The name of the database or NULL for an in-memory database.
879          */
880         internal constructor(context: Context, klass: Class<T>, name: String?) {
881             this.klass = klass.kotlin
882             this.context = context
883             this.name = name
884             this.factory = null
885         }
886 
887         private val callbacks: MutableList<Callback> = mutableListOf()
888         private var prepackagedDatabaseCallback: PrepackagedDatabaseCallback? = null
889         private var queryCallback: QueryCallback? = null
890         private var queryCallbackExecutor: Executor? = null
891         private var queryCallbackCoroutineContext: CoroutineContext? = null
892         private val typeConverters: MutableList<Any> = mutableListOf()
893         private var queryExecutor: Executor? = null
894         private var transactionExecutor: Executor? = null
895 
896         private var supportOpenHelperFactory: SupportSQLiteOpenHelper.Factory? = null
897         private var allowMainThreadQueries = false
898         private var journalMode: JournalMode = JournalMode.AUTOMATIC
899         private var multiInstanceInvalidationIntent: Intent? = null
900 
901         private var autoCloseTimeout = -1L
902         private var autoCloseTimeUnit: TimeUnit? = null
903 
904         /** Migrations, mapped by from-to pairs. */
905         private val migrationContainer: MigrationContainer = MigrationContainer()
906 
907         /**
908          * Versions that don't require migrations, configured via
909          * [fallbackToDestructiveMigrationFrom].
910          */
911         private var migrationsNotRequiredFrom: MutableSet<Int> = mutableSetOf()
912 
913         /**
914          * Keeps track of [Migration.startVersion]s and [Migration.endVersion]s added in
915          * [addMigrations] for later validation that makes those versions don't match any versions
916          * passed to [fallbackToDestructiveMigrationFrom].
917          */
918         private val migrationStartAndEndVersions = mutableSetOf<Int>()
919 
920         private val autoMigrationSpecs: MutableList<AutoMigrationSpec> = mutableListOf()
921 
922         private var requireMigration: Boolean = true
923         private var allowDestructiveMigrationOnDowngrade = false
924         private var allowDestructiveMigrationForAllTables = false
925 
926         private var copyFromAssetPath: String? = null
927         private var copyFromFile: File? = null
928         private var copyFromInputStream: Callable<InputStream>? = null
929 
930         private var driver: SQLiteDriver? = null
931         private var queryCoroutineContext: CoroutineContext? = null
932 
933         private var inMemoryTrackingTableMode = true
934 
935         /**
936          * Configures Room to create and open the database using a pre-packaged database located in
937          * the application 'assets/' folder.
938          *
939          * Room does not open the pre-packaged database, instead it copies it into the internal app
940          * database folder and then opens it. The pre-packaged database file must be located in the
941          * "assets/" folder of your application. For example, the path for a file located in
942          * "assets/databases/products.db" would be "databases/products.db".
943          *
944          * The pre-packaged database schema will be validated. It might be best to create your
945          * pre-packaged database schema utilizing the exported schema files generated when
946          * [Database.exportSchema] is enabled.
947          *
948          * This method is not supported for an in memory database [Builder].
949          *
950          * @param databaseFilePath The file path within the 'assets/' directory of where the
951          *   database file is located.
952          * @return This builder instance.
953          */
954         open fun createFromAsset(databaseFilePath: String) = apply {
955             this.copyFromAssetPath = databaseFilePath
956         }
957 
958         /**
959          * Configures Room to create and open the database using a pre-packaged database located in
960          * the application 'assets/' folder.
961          *
962          * Room does not open the pre-packaged database, instead it copies it into the internal app
963          * database folder and then opens it. The pre-packaged database file must be located in the
964          * "assets/" folder of your application. For example, the path for a file located in
965          * "assets/databases/products.db" would be "databases/products.db".
966          *
967          * The pre-packaged database schema will be validated. It might be best to create your
968          * pre-packaged database schema utilizing the exported schema files generated when
969          * [Database.exportSchema] is enabled.
970          *
971          * This method is not supported for an in memory database [Builder].
972          *
973          * @param databaseFilePath The file path within the 'assets/' directory of where the
974          *   database file is located.
975          * @param callback The pre-packaged callback.
976          * @return This builder instance.
977          */
978         @SuppressLint("BuilderSetStyle") // To keep naming consistency.
979         open fun createFromAsset(databaseFilePath: String, callback: PrepackagedDatabaseCallback) =
980             apply {
981                 this.prepackagedDatabaseCallback = callback
982                 this.copyFromAssetPath = databaseFilePath
983             }
984 
985         /**
986          * Configures Room to create and open the database using a pre-packaged database file.
987          *
988          * Room does not open the pre-packaged database, instead it copies it into the internal app
989          * database folder and then opens it. The given file must be accessible and the right
990          * permissions must be granted for Room to copy the file.
991          *
992          * The pre-packaged database schema will be validated. It might be best to create your
993          * pre-packaged database schema utilizing the exported schema files generated when
994          * [Database.exportSchema] is enabled.
995          *
996          * The [Callback.onOpen] method can be used as an indicator that the pre-packaged database
997          * was successfully opened by Room and can be cleaned up.
998          *
999          * This method is not supported for an in memory database [Builder].
1000          *
1001          * @param databaseFile The database file.
1002          * @return This builder instance.
1003          */
1004         open fun createFromFile(databaseFile: File) = apply { this.copyFromFile = databaseFile }
1005 
1006         /**
1007          * Configures Room to create and open the database using a pre-packaged database file.
1008          *
1009          * Room does not open the pre-packaged database, instead it copies it into the internal app
1010          * database folder and then opens it. The given file must be accessible and the right
1011          * permissions must be granted for Room to copy the file.
1012          *
1013          * The pre-packaged database schema will be validated. It might be best to create your
1014          * pre-packaged database schema utilizing the exported schema files generated when
1015          * [Database.exportSchema] is enabled.
1016          *
1017          * The [Callback.onOpen] method can be used as an indicator that the pre-packaged database
1018          * was successfully opened by Room and can be cleaned up.
1019          *
1020          * This method is not supported for an in memory database [Builder].
1021          *
1022          * @param databaseFile The database file.
1023          * @param callback The pre-packaged callback.
1024          * @return This builder instance.
1025          */
1026         @SuppressLint("BuilderSetStyle", "StreamFiles") // To keep naming consistency.
1027         open fun createFromFile(databaseFile: File, callback: PrepackagedDatabaseCallback) = apply {
1028             this.prepackagedDatabaseCallback = callback
1029             this.copyFromFile = databaseFile
1030         }
1031 
1032         /**
1033          * Configures Room to create and open the database using a pre-packaged database via an
1034          * [InputStream].
1035          *
1036          * This is useful for processing compressed database files. Room does not open the
1037          * pre-packaged database, instead it copies it into the internal app database folder, and
1038          * then open it. The [InputStream] will be closed once Room is done consuming it.
1039          *
1040          * The pre-packaged database schema will be validated. It might be best to create your
1041          * pre-packaged database schema utilizing the exported schema files generated when
1042          * [Database.exportSchema] is enabled.
1043          *
1044          * The [Callback.onOpen] method can be used as an indicator that the pre-packaged database
1045          * was successfully opened by Room and can be cleaned up.
1046          *
1047          * This method is not supported for an in memory database [Builder].
1048          *
1049          * @param inputStreamCallable A callable that returns an InputStream from which to copy the
1050          *   database. The callable will be invoked in a thread from the Executor set via
1051          *   [setQueryExecutor]. The callable is only invoked if Room needs to create and open the
1052          *   database from the pre-package database, usually the first time it is created or during
1053          *   a destructive migration.
1054          * @return This builder instance.
1055          */
1056         @SuppressLint("BuilderSetStyle") // To keep naming consistency.
1057         open fun createFromInputStream(inputStreamCallable: Callable<InputStream>) = apply {
1058             this.copyFromInputStream = inputStreamCallable
1059         }
1060 
1061         /**
1062          * Configures Room to create and open the database using a pre-packaged database via an
1063          * [InputStream].
1064          *
1065          * This is useful for processing compressed database files. Room does not open the
1066          * pre-packaged database, instead it copies it into the internal app database folder, and
1067          * then open it. The [InputStream] will be closed once Room is done consuming it.
1068          *
1069          * The pre-packaged database schema will be validated. It might be best to create your
1070          * pre-packaged database schema utilizing the exported schema files generated when
1071          * [Database.exportSchema] is enabled.
1072          *
1073          * The [Callback.onOpen] method can be used as an indicator that the pre-packaged database
1074          * was successfully opened by Room and can be cleaned up.
1075          *
1076          * This method is not supported for an in memory database [Builder].
1077          *
1078          * @param inputStreamCallable A callable that returns an InputStream from which to copy the
1079          *   database. The callable will be invoked in a thread from the Executor set via
1080          *   [setQueryExecutor]. The callable is only invoked if Room needs to create and open the
1081          *   database from the pre-package database, usually the first time it is created or during
1082          *   a destructive migration.
1083          * @param callback The pre-packaged callback.
1084          * @return This builder instance.
1085          */
1086         @SuppressLint("BuilderSetStyle", "LambdaLast") // To keep naming consistency.
1087         open fun createFromInputStream(
1088             inputStreamCallable: Callable<InputStream>,
1089             callback: PrepackagedDatabaseCallback
1090         ) = apply {
1091             this.prepackagedDatabaseCallback = callback
1092             this.copyFromInputStream = inputStreamCallable
1093         }
1094 
1095         /**
1096          * Sets the database factory. If not set, it defaults to [FrameworkSQLiteOpenHelperFactory].
1097          *
1098          * @param factory The factory to use to access the database.
1099          * @return This builder instance.
1100          */
1101         open fun openHelperFactory(factory: SupportSQLiteOpenHelper.Factory?) = apply {
1102             this.supportOpenHelperFactory = factory
1103         }
1104 
1105         /**
1106          * Adds a migration to the builder.
1107          *
1108          * Each [Migration] has a start and end versions and Room runs these migrations to bring the
1109          * database to the latest version.
1110          *
1111          * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
1112          * going from version 3 to 5 without going to version 4). If Room opens a database at
1113          * version 3 and latest version is >= 5, Room will use the migration object that can migrate
1114          * from 3 to 5 instead of 3 to 4 and 4 to 5.
1115          *
1116          * @param migrations The migration objects that modify the database schema with the
1117          *   necessary changes for a version change.
1118          * @return This builder instance.
1119          */
1120         actual open fun addMigrations(vararg migrations: Migration) = apply {
1121             for (migration in migrations) {
1122                 migrationStartAndEndVersions.add(migration.startVersion)
1123                 migrationStartAndEndVersions.add(migration.endVersion)
1124             }
1125             migrationContainer.addMigrations(*migrations)
1126         }
1127 
1128         /**
1129          * Adds an auto migration spec instance to the builder.
1130          *
1131          * @param autoMigrationSpec The auto migration object that is annotated with
1132          *   [ProvidedAutoMigrationSpec] and is declared in an [AutoMigration] annotation.
1133          * @return This builder instance.
1134          */
1135         @Suppress("MissingGetterMatchingBuilder")
1136         actual open fun addAutoMigrationSpec(autoMigrationSpec: AutoMigrationSpec) = apply {
1137             this.autoMigrationSpecs.add(autoMigrationSpec)
1138         }
1139 
1140         /**
1141          * Disables the main thread query check for Room.
1142          *
1143          * Room ensures that Database is never accessed on the main thread because it may lock the
1144          * main thread and trigger an ANR. If you need to access the database from the main thread,
1145          * you should always use async alternatives or manually move the call to a background
1146          * thread.
1147          *
1148          * You may want to turn this check off for testing.
1149          *
1150          * @return This builder instance.
1151          */
1152         open fun allowMainThreadQueries() = apply { this.allowMainThreadQueries = true }
1153 
1154         /**
1155          * Sets the journal mode for this database.
1156          *
1157          * The value is ignored if the builder is for an 'in-memory database'. The journal mode
1158          * should be consistent across multiple instances of [RoomDatabase] for a single SQLite
1159          * database file.
1160          *
1161          * The default value is [JournalMode.AUTOMATIC].
1162          *
1163          * @param journalMode The journal mode.
1164          * @return This builder instance.
1165          */
1166         actual open fun setJournalMode(journalMode: JournalMode) = apply {
1167             this.journalMode = journalMode
1168         }
1169 
1170         /**
1171          * Sets the [Executor] that will be used to execute all non-blocking asynchronous queries
1172          * and tasks, including `LiveData` invalidation, `Flowable` scheduling and
1173          * `ListenableFuture` tasks.
1174          *
1175          * When both the query executor and transaction executor are unset, then a default
1176          * `Executor` will be used. The default `Executor` allocates and shares threads amongst
1177          * Architecture Components libraries. If the query executor is unset but a transaction
1178          * executor was set via [setTransactionExecutor], then the same `Executor` will be used for
1179          * queries.
1180          *
1181          * For best performance the given `Executor` should be bounded (max number of threads is
1182          * limited).
1183          *
1184          * The input `Executor` cannot run tasks on the UI thread.
1185          *
1186          * If either [setQueryCoroutineContext] has been called, then this function will throw an
1187          * [IllegalArgumentException].
1188          *
1189          * @return This builder instance.
1190          * @throws IllegalArgumentException if this builder was already configured with a
1191          *   [CoroutineContext].
1192          */
1193         open fun setQueryExecutor(executor: Executor) = apply {
1194             require(queryCoroutineContext == null) {
1195                 "This builder has already been configured with a CoroutineContext. A RoomDatabase" +
1196                     "can only be configured with either an Executor or a CoroutineContext."
1197             }
1198             this.queryExecutor = executor
1199         }
1200 
1201         /**
1202          * Sets the [Executor] that will be used to execute all non-blocking asynchronous
1203          * transaction queries and tasks, including `LiveData` invalidation, `Flowable` scheduling
1204          * and `ListenableFuture` tasks.
1205          *
1206          * When both the transaction executor and query executor are unset, then a default
1207          * `Executor` will be used. The default `Executor` allocates and shares threads amongst
1208          * Architecture Components libraries. If the transaction executor is unset but a query
1209          * executor was set using [setQueryExecutor], then the same `Executor` will be used for
1210          * transactions.
1211          *
1212          * If the given `Executor` is shared then it should be unbounded to avoid the possibility of
1213          * a deadlock. Room will not use more than one thread at a time from this executor since
1214          * only one transaction at a time can be executed, other transactions will be queued on a
1215          * first come, first serve order.
1216          *
1217          * The input `Executor` cannot run tasks on the UI thread.
1218          *
1219          * If either [setQueryCoroutineContext] has been called, then this function will throw an
1220          * [IllegalArgumentException].
1221          *
1222          * @return This builder instance.
1223          * @throws IllegalArgumentException if this builder was already configured with a
1224          *   [CoroutineContext].
1225          */
1226         open fun setTransactionExecutor(executor: Executor) = apply {
1227             require(queryCoroutineContext == null) {
1228                 "This builder has already been configured with a CoroutineContext. A RoomDatabase" +
1229                     "can only be configured with either an Executor or a CoroutineContext."
1230             }
1231             this.transactionExecutor = executor
1232         }
1233 
1234         /**
1235          * Sets whether table invalidation in this instance of [RoomDatabase] should be broadcast
1236          * and synchronized with other instances of the same [RoomDatabase], including those in a
1237          * separate process. In order to enable multi-instance invalidation, this has to be turned
1238          * on both ends.
1239          *
1240          * This is not enabled by default.
1241          *
1242          * This does not work for in-memory databases. This does not work between database instances
1243          * targeting different database files.
1244          *
1245          * @return This builder instance.
1246          */
1247         @OptIn(ExperimentalRoomApi::class)
1248         @Suppress("UnsafeOptInUsageError")
1249         open fun enableMultiInstanceInvalidation() = apply {
1250             this.multiInstanceInvalidationIntent =
1251                 if (name != null) {
1252                     Intent(context, MultiInstanceInvalidationService::class.java)
1253                 } else {
1254                     null
1255                 }
1256         }
1257 
1258         /**
1259          * Sets whether table invalidation in this instance of [RoomDatabase] should be broadcast
1260          * and synchronized with other instances of the same [RoomDatabase], including those in a
1261          * separate process. In order to enable multi-instance invalidation, this has to be turned
1262          * on both ends and need to point to the same [MultiInstanceInvalidationService].
1263          *
1264          * This is not enabled by default.
1265          *
1266          * This does not work for in-memory databases. This does not work between database instances
1267          * targeting different database files.
1268          *
1269          * @param invalidationServiceIntent Intent to bind to the
1270          *   [MultiInstanceInvalidationService].
1271          * @return This builder instance.
1272          */
1273         @ExperimentalRoomApi
1274         @Suppress("MissingGetterMatchingBuilder")
1275         open fun setMultiInstanceInvalidationServiceIntent(invalidationServiceIntent: Intent) =
1276             apply {
1277                 this.multiInstanceInvalidationIntent =
1278                     if (name != null) invalidationServiceIntent else null
1279             }
1280 
1281         /**
1282          * Allows Room to destructively recreate database tables if [Migration]s that would migrate
1283          * old database schemas to the latest schema version are not found.
1284          *
1285          * When the database version on the device does not match the latest schema version, Room
1286          * runs necessary [Migration]s on the database.
1287          *
1288          * If it cannot find the set of [Migration]s that will bring the database to the current
1289          * version, it will throw an [IllegalStateException].
1290          *
1291          * You can call this method to change this behavior to re-create the database tables instead
1292          * of crashing.
1293          *
1294          * If the database was create from an asset or a file then Room will try to use the same
1295          * file to re-create the database, otherwise this will delete all of the data in the
1296          * database tables managed by Room.
1297          *
1298          * To let Room fallback to destructive migration only during a schema downgrade then use
1299          * [fallbackToDestructiveMigrationOnDowngrade].
1300          *
1301          * @return This builder instance.
1302          */
1303         @Deprecated(
1304             message =
1305                 "Replace by overloaded version with parameter to indicate if all tables " +
1306                     "should be dropped or not.",
1307             replaceWith = ReplaceWith("fallbackToDestructiveMigration(false)")
1308         )
1309         @Suppress("BuilderSetStyle") // Overload of exsisting API
1310         open fun fallbackToDestructiveMigration() = apply {
1311             this.requireMigration = false
1312             this.allowDestructiveMigrationOnDowngrade = true
1313         }
1314 
1315         /**
1316          * Allows Room to destructively recreate database tables if [Migration]s that would migrate
1317          * old database schemas to the latest schema version are not found.
1318          *
1319          * When the database version on the device does not match the latest schema version, Room
1320          * runs necessary [Migration]s on the database. If it cannot find the set of [Migration]s
1321          * that will bring the database to the current version, it will throw an
1322          * [IllegalStateException]. You can call this method to change this behavior to re-create
1323          * the database tables instead of crashing.
1324          *
1325          * If the database was create from an asset or a file then Room will try to use the same
1326          * file to re-create the database, otherwise this will delete all of the data in the
1327          * database tables managed by Room.
1328          *
1329          * To let Room fallback to destructive migration only during a schema downgrade then use
1330          * [fallbackToDestructiveMigrationOnDowngrade].
1331          *
1332          * @param dropAllTables Set to `true` if all tables should be dropped during destructive
1333          *   migration including those not managed by Room. Recommended value is `true` as otherwise
1334          *   Room could leave obsolete data when table names or existence changes between versions.
1335          * @return This builder instance.
1336          */
1337         @Suppress("BuilderSetStyle") // Overload of existing API
1338         actual fun fallbackToDestructiveMigration(dropAllTables: Boolean) = apply {
1339             this.requireMigration = false
1340             this.allowDestructiveMigrationOnDowngrade = true
1341             this.allowDestructiveMigrationForAllTables = dropAllTables
1342         }
1343 
1344         /**
1345          * Allows Room to destructively recreate database tables if [Migration]s are not available
1346          * when downgrading to old schema versions.
1347          *
1348          * For details, see [Builder.fallbackToDestructiveMigration].
1349          *
1350          * @return This builder instance.
1351          */
1352         @Deprecated(
1353             message =
1354                 "Replace by overloaded version with parameter to indicate if all tables " +
1355                     "should be dropped or not.",
1356             replaceWith = ReplaceWith("fallbackToDestructiveMigrationOnDowngrade(false)")
1357         )
1358         open fun fallbackToDestructiveMigrationOnDowngrade() = apply {
1359             this.requireMigration = true
1360             this.allowDestructiveMigrationOnDowngrade = true
1361         }
1362 
1363         /**
1364          * Allows Room to destructively recreate database tables if [Migration]s are not available
1365          * when downgrading to old schema versions.
1366          *
1367          * For details, see [Builder.fallbackToDestructiveMigration].
1368          *
1369          * @param dropAllTables Set to `true` if all tables should be dropped during destructive
1370          *   migration including those not managed by Room. Recommended value is `true` as otherwise
1371          *   Room could leave obsolete data when table names or existence changes between versions.
1372          * @return This builder instance.
1373          */
1374         @Suppress("BuilderSetStyle") // Overload of existing API
1375         actual fun fallbackToDestructiveMigrationOnDowngrade(dropAllTables: Boolean) = apply {
1376             this.requireMigration = true
1377             this.allowDestructiveMigrationOnDowngrade = true
1378             this.allowDestructiveMigrationForAllTables = dropAllTables
1379         }
1380 
1381         /**
1382          * Informs Room that it is allowed to destructively recreate database tables from specific
1383          * starting schema versions.
1384          *
1385          * This functionality is the same as that provided by [fallbackToDestructiveMigration],
1386          * except that this method allows the specification of a set of schema versions for which
1387          * destructive recreation is allowed.
1388          *
1389          * Using this method is preferable to [fallbackToDestructiveMigration] if you want to allow
1390          * destructive migrations from some schema versions while still taking advantage of
1391          * exceptions being thrown due to unintentionally missing migrations.
1392          *
1393          * Note: No versions passed to this method may also exist as either starting or ending
1394          * versions in the [Migration]s provided to [addMigrations]. If a version passed to this
1395          * method is found as a starting or ending version in a Migration, an exception will be
1396          * thrown.
1397          *
1398          * @param startVersions The set of schema versions from which Room should use a destructive
1399          *   migration.
1400          * @return This builder instance.
1401          */
1402         @Deprecated(
1403             message =
1404                 "Replace by overloaded version with parameter to indicate if all tables " +
1405                     "should be dropped or not.",
1406             replaceWith = ReplaceWith("fallbackToDestructiveMigrationFrom(false, startVersions)")
1407         )
1408         open fun fallbackToDestructiveMigrationFrom(vararg startVersions: Int) = apply {
1409             for (startVersion in startVersions) {
1410                 this.migrationsNotRequiredFrom.add(startVersion)
1411             }
1412         }
1413 
1414         /**
1415          * Informs Room that it is allowed to destructively recreate database tables from specific
1416          * starting schema versions.
1417          *
1418          * This functionality is the same [fallbackToDestructiveMigration], except that this method
1419          * allows the specification of a set of schema versions for which destructive recreation is
1420          * allowed.
1421          *
1422          * Using this method is preferable to [fallbackToDestructiveMigration] if you want to allow
1423          * destructive migrations from some schema versions while still taking advantage of
1424          * exceptions being thrown due to unintentionally missing migrations.
1425          *
1426          * Note: No versions passed to this method may also exist as either starting or ending
1427          * versions in the [Migration]s provided via [addMigrations]. If a version passed to this
1428          * method is found as a starting or ending version in a Migration, an exception will be
1429          * thrown.
1430          *
1431          * @param dropAllTables Set to `true` if all tables should be dropped during destructive
1432          *   migration including those not managed by Room.
1433          * @param startVersions The set of schema versions from which Room should use a destructive
1434          *   migration.
1435          * @return This builder instance.
1436          */
1437         @Suppress(
1438             "BuilderSetStyle", // Overload of existing API
1439             "MissingJvmstatic", // No need for @JvmOverloads due to an overload already existing
1440         )
1441         actual open fun fallbackToDestructiveMigrationFrom(
1442             @Suppress("KotlinDefaultParameterOrder") // There is a vararg that must be last
1443             dropAllTables: Boolean,
1444             vararg startVersions: Int
1445         ) = apply {
1446             for (startVersion in startVersions) {
1447                 this.migrationsNotRequiredFrom.add(startVersion)
1448             }
1449             this.allowDestructiveMigrationForAllTables = dropAllTables
1450         }
1451 
1452         /**
1453          * Adds a [Callback] to this database.
1454          *
1455          * @param callback The callback.
1456          * @return This builder instance.
1457          */
1458         actual open fun addCallback(callback: Callback) = apply { this.callbacks.add(callback) }
1459 
1460         /**
1461          * Sets a [QueryCallback] to be invoked when queries are executed.
1462          *
1463          * The callback is invoked whenever a query is executed, note that adding this callback has
1464          * a small cost and should be avoided in production builds unless needed.
1465          *
1466          * A use case for providing a callback is to allow logging executed queries. When the
1467          * callback implementation logs then it is recommended to use an immediate executor.
1468          *
1469          * If a previous callback was set with [setQueryCallback] then this call will override it,
1470          * including removing the Coroutine context previously set, if any.
1471          *
1472          * @param queryCallback The query callback.
1473          * @param executor The executor on which the query callback will be invoked.
1474          * @return This builder instance.
1475          */
1476         @Suppress("MissingGetterMatchingBuilder")
1477         open fun setQueryCallback(queryCallback: QueryCallback, executor: Executor) = apply {
1478             this.queryCallback = queryCallback
1479             this.queryCallbackExecutor = executor
1480             this.queryCallbackCoroutineContext = null
1481         }
1482 
1483         /**
1484          * Sets a [QueryCallback] to be invoked when queries are executed.
1485          *
1486          * The callback is invoked whenever a query is executed, note that adding this callback has
1487          * a small cost and should be avoided in production builds unless needed.
1488          *
1489          * A use case for providing a callback is to allow logging executed queries. When the
1490          * callback implementation simply logs then it is recommended to use
1491          * [kotlinx.coroutines.Dispatchers.Unconfined].
1492          *
1493          * If a previous callback was set with [setQueryCallback] then this call will override it,
1494          * including removing the executor previously set, if any.
1495          *
1496          * @param context The coroutine context on which the query callback will be invoked.
1497          * @param queryCallback The query callback.
1498          * @return This builder instance.
1499          */
1500         @Suppress("MissingGetterMatchingBuilder")
1501         fun setQueryCallback(context: CoroutineContext, queryCallback: QueryCallback) = apply {
1502             this.queryCallback = queryCallback
1503             this.queryCallbackExecutor = null
1504             this.queryCallbackCoroutineContext = context
1505         }
1506 
1507         /**
1508          * Adds a type converter instance to the builder.
1509          *
1510          * @param typeConverter The converter instance that is annotated with
1511          *   [ProvidedTypeConverter].
1512          * @return This builder instance.
1513          */
1514         actual open fun addTypeConverter(typeConverter: Any) = apply {
1515             this.typeConverters.add(typeConverter)
1516         }
1517 
1518         /**
1519          * Enables auto-closing for the database to free up unused resources. The underlying
1520          * database will be closed after it's last use after the specified [autoCloseTimeout] has
1521          * elapsed since its last usage. The database will be automatically re-opened the next time
1522          * it is accessed.
1523          *
1524          * Auto-closing is not compatible with in-memory databases since the data will be lost when
1525          * the database is auto-closed.
1526          *
1527          * Also, temp tables and temp triggers will be cleared each time the database is
1528          * auto-closed. If you need to use them, please include them in your callback
1529          * [RoomDatabase.Callback.onOpen].
1530          *
1531          * All configuration should happen in your [RoomDatabase.Callback.onOpen] callback so it is
1532          * re-applied every time the database is re-opened. Note that the
1533          * [RoomDatabase.Callback.onOpen] will be called every time the database is re-opened.
1534          *
1535          * The auto-closing database operation runs on the query executor.
1536          *
1537          * The database will not be re-opened if the RoomDatabase or the SupportSqliteOpenHelper is
1538          * closed manually (by calling [RoomDatabase.close] or [SupportSQLiteOpenHelper.close]. If
1539          * the database is closed manually, you must create a new database using
1540          * [RoomDatabase.Builder.build].
1541          *
1542          * @param autoCloseTimeout the amount of time after the last usage before closing the
1543          *   database. Must greater or equal to zero.
1544          * @param autoCloseTimeUnit the timeunit for autoCloseTimeout.
1545          * @return This builder instance.
1546          */
1547         @ExperimentalRoomApi // When experimental is removed, add these parameters to
1548         // DatabaseConfiguration
1549         @Suppress("MissingGetterMatchingBuilder")
1550         open fun setAutoCloseTimeout(
1551             @IntRange(from = 0) autoCloseTimeout: Long,
1552             autoCloseTimeUnit: TimeUnit
1553         ) = apply {
1554             require(autoCloseTimeout >= 0) { "autoCloseTimeout must be >= 0" }
1555             this.autoCloseTimeout = autoCloseTimeout
1556             this.autoCloseTimeUnit = autoCloseTimeUnit
1557         }
1558 
1559         /**
1560          * Sets the [SQLiteDriver] implementation to be used by Room to open database connections.
1561          * For example, an instance of [androidx.sqlite.driver.AndroidSQLiteDriver] or
1562          * [androidx.sqlite.driver.bundled.BundledSQLiteDriver].
1563          *
1564          * Once a driver is configured using this function, various callbacks that receive a
1565          * [SupportSQLiteDatabase] will not be invoked, such as [RoomDatabase.Callback.onCreate].
1566          * Moreover, APIs that use SupportSQLite will also throw an exception, such as
1567          * [RoomDatabase.openHelper].
1568          *
1569          * See the documentation on
1570          * [Migrating to SQLite Driver](https://d.android.com/training/data-storage/room/room-kmp-migration#migrate_from_support_sqlite_to_sqlite_driver)
1571          * for more information.
1572          *
1573          * @param driver The driver
1574          * @return This builder instance.
1575          */
1576         @Suppress("MissingGetterMatchingBuilder")
1577         actual fun setDriver(driver: SQLiteDriver) = apply { this.driver = driver }
1578 
1579         /**
1580          * Sets the [CoroutineContext] that will be used to execute all asynchronous queries and
1581          * tasks, such as `Flow` emissions and [InvalidationTracker] notifications.
1582          *
1583          * If no [CoroutineDispatcher] is present in the [context] then this function will throw an
1584          * [IllegalArgumentException]
1585          *
1586          * If no context is provided, then Room wil default to using the [Executor] set via
1587          * [setQueryExecutor] as the context via the conversion function [asCoroutineDispatcher].
1588          *
1589          * If either [setQueryExecutor] or [setTransactionExecutor] has been called, then this
1590          * function will throw an [IllegalArgumentException].
1591          *
1592          * @param context The context
1593          * @return This [Builder] instance
1594          * @throws IllegalArgumentException if no [CoroutineDispatcher] is found in the given
1595          *   [context] or if this builder was already configured with an [Executor].
1596          */
1597         @Suppress("MissingGetterMatchingBuilder")
1598         actual fun setQueryCoroutineContext(context: CoroutineContext) = apply {
1599             require(queryExecutor == null && transactionExecutor == null) {
1600                 "This builder has already been configured with an Executor. A RoomDatabase can" +
1601                     "only be configured with either an Executor or a CoroutineContext."
1602             }
1603             require(context[ContinuationInterceptor] != null) {
1604                 "It is required that the coroutine context contain a dispatcher."
1605             }
1606             this.queryCoroutineContext = context
1607         }
1608 
1609         /**
1610          * Sets whether Room will use an in-memory table or a persisted table to track invalidation.
1611          *
1612          * An in-memory table is used by default. Using an in-memory tables is more performant,
1613          * reduces the journal file size but has an increased memory footprint, where as using a
1614          * real table has the opposite effect.
1615          *
1616          * @param inMemory True if in-memory tables should be used, false otherwise.
1617          * @return This [Builder] instance
1618          */
1619         @ExperimentalRoomApi
1620         @Suppress("MissingGetterMatchingBuilder")
1621         fun setInMemoryTrackingMode(inMemory: Boolean) = apply {
1622             this.inMemoryTrackingTableMode = inMemory
1623         }
1624 
1625         /**
1626          * Creates the databases and initializes it.
1627          *
1628          * By default, all RoomDatabases use in memory storage for TEMP tables and enables recursive
1629          * triggers.
1630          *
1631          * @return A new database instance.
1632          * @throws IllegalArgumentException if the builder was misconfigured.
1633          */
1634         actual open fun build(): T {
1635             if (queryExecutor == null && transactionExecutor == null) {
1636                 transactionExecutor = ArchTaskExecutor.getIOThreadExecutor()
1637                 queryExecutor = transactionExecutor
1638             } else if (queryExecutor != null && transactionExecutor == null) {
1639                 transactionExecutor = queryExecutor
1640             } else if (queryExecutor == null) {
1641                 queryExecutor = transactionExecutor
1642             }
1643 
1644             validateMigrationsNotRequired(migrationStartAndEndVersions, migrationsNotRequiredFrom)
1645 
1646             val initialFactory: SupportSQLiteOpenHelper.Factory? =
1647                 if (driver == null && supportOpenHelperFactory == null) {
1648                     // No driver and no factory, compatibility mode, create the default factory
1649                     FrameworkSQLiteOpenHelperFactory()
1650                 } else if (driver == null) {
1651                     // No driver but a factory was provided, use it in compatibility mode
1652                     supportOpenHelperFactory
1653                 } else if (supportOpenHelperFactory == null) {
1654                     // A driver was provided, no need to create the default factory
1655                     null
1656                 } else {
1657                     // Both driver and factory provided, invalid configuration.
1658                     throw IllegalArgumentException(
1659                         "A RoomDatabase cannot be configured with both a SQLiteDriver and a " +
1660                             "SupportOpenHelper.Factory."
1661                     )
1662                 }
1663             val autoCloseEnabled = autoCloseTimeout > 0
1664             val prePackagedCopyEnabled =
1665                 copyFromAssetPath != null || copyFromFile != null || copyFromInputStream != null
1666             val queryCallbackEnabled = queryCallback != null
1667             val supportOpenHelperFactory =
1668                 initialFactory
1669                     ?.let {
1670                         if (autoCloseEnabled) {
1671                             requireNotNull(name) {
1672                                 "Cannot create auto-closing database for an in-memory database."
1673                             }
1674                             val autoCloser =
1675                                 AutoCloser(
1676                                     timeoutAmount = autoCloseTimeout,
1677                                     timeUnit = requireNotNull(autoCloseTimeUnit)
1678                                 )
1679                             AutoClosingRoomOpenHelperFactory(delegate = it, autoCloser = autoCloser)
1680                         } else {
1681                             it
1682                         }
1683                     }
1684                     ?.let {
1685                         if (prePackagedCopyEnabled) {
1686                             requireNotNull(name) {
1687                                 "Cannot create from asset or file for an in-memory database."
1688                             }
1689 
1690                             val copyFromAssetPathConfig = if (copyFromAssetPath == null) 0 else 1
1691                             val copyFromFileConfig = if (copyFromFile == null) 0 else 1
1692                             val copyFromInputStreamConfig =
1693                                 if (copyFromInputStream == null) 0 else 1
1694                             val copyConfigurations =
1695                                 copyFromAssetPathConfig +
1696                                     copyFromFileConfig +
1697                                     copyFromInputStreamConfig
1698 
1699                             require(copyConfigurations == 1) {
1700                                 "More than one of createFromAsset(), " +
1701                                     "createFromInputStream(), and createFromFile() were called on this " +
1702                                     "Builder, but the database can only be created using one of the " +
1703                                     "three configurations."
1704                             }
1705                             PrePackagedCopyOpenHelperFactory(
1706                                 copyFromAssetPath = copyFromAssetPath,
1707                                 copyFromFile = copyFromFile,
1708                                 copyFromInputStream = copyFromInputStream,
1709                                 delegate = it
1710                             )
1711                         } else {
1712                             it
1713                         }
1714                     }
1715                     ?.let {
1716                         if (queryCallbackEnabled) {
1717                             val queryCallbackContext =
1718                                 queryCallbackExecutor?.asCoroutineDispatcher()
1719                                     ?: requireNotNull(queryCallbackCoroutineContext)
1720                             QueryInterceptorOpenHelperFactory(
1721                                 delegate = it,
1722                                 queryCallbackScope = CoroutineScope(queryCallbackContext),
1723                                 queryCallback = requireNotNull(queryCallback)
1724                             )
1725                         } else {
1726                             it
1727                         }
1728                     }
1729             // No open helper means a driver is to be used.
1730             if (supportOpenHelperFactory == null) {
1731                 require(!autoCloseEnabled) {
1732                     "Auto Closing Database is not supported when an SQLiteDriver is configured."
1733                 }
1734                 require(!prePackagedCopyEnabled) {
1735                     "Pre-Package Database is not supported when an SQLiteDriver is configured."
1736                 }
1737                 require(!queryCallbackEnabled) {
1738                     "Query Callback is not supported when an SQLiteDriver is configured."
1739                 }
1740             }
1741             val configuration =
1742                 DatabaseConfiguration(
1743                         context = context,
1744                         name = name,
1745                         sqliteOpenHelperFactory = supportOpenHelperFactory,
1746                         migrationContainer = migrationContainer,
1747                         callbacks = callbacks,
1748                         allowMainThreadQueries = allowMainThreadQueries,
1749                         journalMode = journalMode.resolve(context),
1750                         queryExecutor = requireNotNull(queryExecutor),
1751                         transactionExecutor = requireNotNull(transactionExecutor),
1752                         multiInstanceInvalidationServiceIntent = multiInstanceInvalidationIntent,
1753                         requireMigration = requireMigration,
1754                         allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade,
1755                         migrationNotRequiredFrom = migrationsNotRequiredFrom,
1756                         copyFromAssetPath = copyFromAssetPath,
1757                         copyFromFile = copyFromFile,
1758                         copyFromInputStream = copyFromInputStream,
1759                         prepackagedDatabaseCallback = prepackagedDatabaseCallback,
1760                         typeConverters = typeConverters,
1761                         autoMigrationSpecs = autoMigrationSpecs,
1762                         allowDestructiveMigrationForAllTables =
1763                             allowDestructiveMigrationForAllTables,
1764                         sqliteDriver = driver,
1765                         queryCoroutineContext = queryCoroutineContext,
1766                     )
1767                     .apply { this.useTempTrackingTable = inMemoryTrackingTableMode }
1768             val db = factory?.invoke() ?: findAndInstantiateDatabaseImpl(klass.java)
1769             db.init(configuration)
1770             return db
1771         }
1772     }
1773 
1774     /**
1775      * A container to hold migrations. It also allows querying its contents to find migrations
1776      * between two versions.
1777      */
1778     actual open class MigrationContainer {
1779         private val migrations = mutableMapOf<Int, TreeMap<Int, Migration>>()
1780 
1781         /**
1782          * Adds the given migrations to the list of available migrations. If 2 migrations have the
1783          * same start-end versions, the latter migration overrides the previous one.
1784          *
1785          * @param migrations List of available migrations.
1786          */
1787         open fun addMigrations(vararg migrations: Migration) {
1788             migrations.forEach(::addMigration)
1789         }
1790 
1791         /**
1792          * Adds the given migrations to the list of available migrations. If 2 migrations have the
1793          * same start-end versions, the latter migration overrides the previous one.
1794          *
1795          * @param migrations List of available migrations.
1796          */
1797         actual open fun addMigrations(migrations: List<Migration>) {
1798             migrations.forEach(::addMigration)
1799         }
1800 
1801         /**
1802          * Add a [Migration] to the container. If the container already has a migration with the
1803          * same start-end versions then it will be overwritten.
1804          *
1805          * @param migration the migration to add.
1806          */
1807         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1808         actual fun addMigration(migration: Migration) {
1809             val start = migration.startVersion
1810             val end = migration.endVersion
1811             val targetMap = migrations.getOrPut(start) { TreeMap<Int, Migration>() }
1812 
1813             if (targetMap.contains(end)) {
1814                 Log.w(LOG_TAG, "Overriding migration ${targetMap[end]} with $migration")
1815             }
1816             targetMap[end] = migration
1817         }
1818 
1819         /**
1820          * Returns the map of available migrations where the key is the start version of the
1821          * migration, and the value is a map of (end version -> Migration).
1822          *
1823          * @return Map of migrations keyed by the start version
1824          */
1825         actual open fun getMigrations(): Map<Int, Map<Int, Migration>> {
1826             return migrations
1827         }
1828 
1829         /**
1830          * Finds the list of migrations that should be run to move from `start` version to `end`
1831          * version.
1832          *
1833          * @param start The current database version
1834          * @param end The target database version
1835          * @return An ordered list of [Migration] objects that should be run to migrate between the
1836          *   given versions. If a migration path cannot be found, returns `null`.
1837          */
1838         open fun findMigrationPath(start: Int, end: Int): List<Migration>? {
1839             return this.findMigrationPathExt(start, end)
1840         }
1841 
1842         /**
1843          * Indicates if the given migration is contained within the [MigrationContainer] based on
1844          * its start-end versions.
1845          *
1846          * @param startVersion Start version of the migration.
1847          * @param endVersion End version of the migration
1848          * @return True if it contains a migration with the same start-end version, false otherwise.
1849          */
1850         actual fun contains(startVersion: Int, endVersion: Int): Boolean {
1851             return this.containsCommon(startVersion, endVersion)
1852         }
1853 
1854         internal actual fun getSortedNodes(
1855             migrationStart: Int
1856         ): Pair<Map<Int, Migration>, Iterable<Int>>? {
1857             val targetNodes = migrations[migrationStart] ?: return null
1858             return targetNodes to targetNodes.keys
1859         }
1860 
1861         internal actual fun getSortedDescendingNodes(
1862             migrationStart: Int
1863         ): Pair<Map<Int, Migration>, Iterable<Int>>? {
1864             val targetNodes = migrations[migrationStart] ?: return null
1865             return targetNodes to targetNodes.descendingKeySet()
1866         }
1867     }
1868 
1869     /** Callback for [RoomDatabase]. */
1870     actual abstract class Callback {
1871         /**
1872          * Called when the database is created for the first time. This is called after all the
1873          * tables are created.
1874          *
1875          * This function is only called when Room is configured without a driver. If a driver is set
1876          * using [androidx.room.RoomDatabase.Builder.setDriver], then only the version that receives
1877          * a [SQLiteConnection] is called.
1878          *
1879          * @param db The database.
1880          */
1881         open fun onCreate(db: SupportSQLiteDatabase) {}
1882 
1883         /**
1884          * Called when the database is created for the first time.
1885          *
1886          * This function called after all the tables are created.
1887          *
1888          * @param connection The database connection.
1889          */
1890         actual open fun onCreate(connection: SQLiteConnection) {
1891             if (connection is SupportSQLiteConnection) {
1892                 onCreate(connection.db)
1893             }
1894         }
1895 
1896         /**
1897          * Called after the database was destructively migrated
1898          *
1899          * This function is only called when Room is configured without a driver. If a driver is set
1900          * using [androidx.room.RoomDatabase.Builder.setDriver], then only the version that receives
1901          * a [SQLiteConnection] is called.
1902          *
1903          * @param db The database.
1904          */
1905         open fun onDestructiveMigration(db: SupportSQLiteDatabase) {}
1906 
1907         /**
1908          * Called after the database was destructively migrated.
1909          *
1910          * @param connection The database connection.
1911          */
1912         actual open fun onDestructiveMigration(connection: SQLiteConnection) {
1913             if (connection is SupportSQLiteConnection) {
1914                 onDestructiveMigration(connection.db)
1915             }
1916         }
1917 
1918         /**
1919          * Called when the database has been opened.
1920          *
1921          * This function is only called when Room is configured without a driver. If a driver is set
1922          * using [androidx.room.RoomDatabase.Builder.setDriver], then only the version that receives
1923          * a [SQLiteConnection] is called.
1924          *
1925          * @param db The database.
1926          */
1927         open fun onOpen(db: SupportSQLiteDatabase) {}
1928 
1929         /**
1930          * Called when the database has been opened.
1931          *
1932          * @param connection The database connection.
1933          */
1934         actual open fun onOpen(connection: SQLiteConnection) {
1935             if (connection is SupportSQLiteConnection) {
1936                 onOpen(connection.db)
1937             }
1938         }
1939     }
1940 
1941     /**
1942      * Callback for [Builder.createFromAsset], [Builder.createFromFile] and
1943      * [Builder.createFromInputStream]
1944      *
1945      * This callback will be invoked after the pre-package DB is copied but before Room had a chance
1946      * to open it and therefore before the [RoomDatabase.Callback] methods are invoked. This
1947      * callback can be useful for updating the pre-package DB schema to satisfy Room's schema
1948      * validation.
1949      */
1950     abstract class PrepackagedDatabaseCallback {
1951         /**
1952          * Called when the pre-packaged database has been copied.
1953          *
1954          * @param db The database.
1955          */
1956         open fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) {}
1957     }
1958 
1959     /**
1960      * Callback interface for when SQLite queries are executed.
1961      *
1962      * Can be set using [RoomDatabase.Builder.setQueryCallback].
1963      */
1964     fun interface QueryCallback {
1965         /**
1966          * Called when a SQL query is executed.
1967          *
1968          * @param sqlQuery The SQLite query statement.
1969          * @param bindArgs Arguments of the query if available, empty list otherwise.
1970          */
1971         fun onQuery(sqlQuery: String, bindArgs: List<Any?>)
1972     }
1973 
1974     companion object {
1975         /**
1976          * Unfortunately, we cannot read this value so we are only setting it to the SQLite default.
1977          */
1978         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
1979         const val MAX_BIND_PARAMETER_CNT = 999
1980     }
1981 }
1982 
1983 /**
1984  * Calls the specified suspending [block] in a database transaction. The transaction will be marked
1985  * as successful unless an exception is thrown in the suspending [block] or the coroutine is
1986  * cancelled.
1987  *
1988  * Room will only perform at most one transaction at a time, additional transactions are queued and
1989  * executed on a first come, first serve order.
1990  *
1991  * Performing blocking database operations is not permitted in a coroutine scope other than the one
1992  * received by the suspending block. It is recommended that all [Dao] function invoked within the
1993  * [block] be suspending functions.
1994  *
1995  * The internal dispatcher used to execute the given [block] will block an utilize a thread from
1996  * Room's transaction executor until the [block] is complete.
1997  */
<lambda>null1998 suspend fun <R> RoomDatabase.withTransaction(block: suspend () -> R): R = withTransactionContext {
1999     @Suppress("DEPRECATION") beginTransaction()
2000     try {
2001         val result = block.invoke()
2002         @Suppress("DEPRECATION") setTransactionSuccessful()
2003         result
2004     } finally {
2005         @Suppress("DEPRECATION") endTransaction()
2006     }
2007 }
2008 
2009 /** Calls the specified suspending [block] with Room's transaction context. */
withTransactionContextnull2010 internal suspend fun <R> RoomDatabase.withTransactionContext(block: suspend () -> R): R {
2011     val transactionBlock: suspend CoroutineScope.() -> R = transaction@{
2012         val transactionElement = coroutineContext[TransactionElement]!!
2013         transactionElement.acquire()
2014         try {
2015             return@transaction block.invoke()
2016         } finally {
2017             transactionElement.release()
2018         }
2019     }
2020     // Use inherited transaction context if available, this allows nested suspending transactions.
2021     val transactionDispatcher = coroutineContext[TransactionElement]?.transactionDispatcher
2022     return if (transactionDispatcher != null) {
2023         withContext(transactionDispatcher, transactionBlock)
2024     } else {
2025         startTransactionCoroutine(coroutineContext, transactionBlock)
2026     }
2027 }
2028 
2029 /**
2030  * Suspend caller coroutine and start the transaction coroutine in a thread from the
2031  * [RoomDatabase.transactionExecutor], resuming the caller coroutine with the result once done. The
2032  * [context] will be a parent of the started coroutine to propagating cancellation and release the
2033  * thread when cancelled.
2034  */
startTransactionCoroutinenull2035 private suspend fun <R> RoomDatabase.startTransactionCoroutine(
2036     context: CoroutineContext,
2037     transactionBlock: suspend CoroutineScope.() -> R
2038 ): R = suspendCancellableCoroutine { continuation ->
2039     try {
2040         transactionExecutor.execute {
2041             try {
2042                 // Thread acquired, start the transaction coroutine using the parent context.
2043                 // The started coroutine will have an event loop dispatcher that we'll use for the
2044                 // transaction context.
2045                 runBlocking(context.minusKey(ContinuationInterceptor)) {
2046                     val dispatcher = coroutineContext[ContinuationInterceptor]!!
2047                     val transactionContext = createTransactionContext(dispatcher)
2048                     continuation.resume(withContext(transactionContext, transactionBlock))
2049                 }
2050             } catch (ex: Throwable) {
2051                 // If anything goes wrong, propagate exception to the calling coroutine.
2052                 continuation.cancel(ex)
2053             }
2054         }
2055     } catch (ex: RejectedExecutionException) {
2056         // Couldn't acquire a thread, cancel coroutine.
2057         continuation.cancel(
2058             IllegalStateException(
2059                 "Unable to acquire a thread to perform the database transaction.",
2060                 ex
2061             )
2062         )
2063     }
2064 }
2065 
2066 /**
2067  * Creates a [CoroutineContext] for performing database operations within a coroutine transaction.
2068  *
2069  * The context is a combination of a dispatcher, a [TransactionElement] and a thread local element.
2070  * * The dispatcher will dispatch coroutines to a single thread that is taken over from the Room
2071  *   transaction executor. If the coroutine context is switched, suspending DAO functions will be
2072  *   able to dispatch to the transaction thread. In reality the dispatcher is the event loop of a
2073  *   [runBlocking] started on the dedicated thread.
2074  * * The [TransactionElement] serves as an indicator for inherited context, meaning, if there is a
2075  *   switch of context, suspending DAO methods will be able to use the indicator to dispatch the
2076  *   database operation to the transaction thread.
2077  * * The thread local element serves as a second indicator and marks threads that are used to
2078  *   execute coroutines within the coroutine transaction, more specifically it allows us to identify
2079  *   if a blocking DAO method is invoked within the transaction coroutine. Never assign meaning to
2080  *   this value, for now all we care is if its present or not.
2081  */
createTransactionContextnull2082 private fun RoomDatabase.createTransactionContext(
2083     dispatcher: ContinuationInterceptor
2084 ): CoroutineContext {
2085     val transactionElement = TransactionElement(dispatcher)
2086     val threadLocalElement =
2087         suspendingTransactionId.asContextElement(System.identityHashCode(transactionElement))
2088     return dispatcher + transactionElement + threadLocalElement
2089 }
2090 
2091 /** A [CoroutineContext.Element] that indicates there is an on-going database transaction. */
2092 internal class TransactionElement(internal val transactionDispatcher: ContinuationInterceptor) :
2093     CoroutineContext.Element {
2094 
2095     companion object Key : CoroutineContext.Key<TransactionElement>
2096 
2097     override val key: CoroutineContext.Key<TransactionElement>
2098         get() = TransactionElement
2099 
2100     /**
2101      * Number of transactions (including nested ones) started with this element. Call [acquire] to
2102      * increase the count and [release] to decrease it.
2103      */
2104     private val referenceCount = AtomicInteger(0)
2105 
acquirenull2106     fun acquire() {
2107         referenceCount.incrementAndGet()
2108     }
2109 
releasenull2110     fun release() {
2111         val count = referenceCount.decrementAndGet()
2112         if (count < 0) {
2113             throw IllegalStateException("Transaction was never started or was already released.")
2114         }
2115     }
2116 }
2117 
2118 /**
2119  * Creates a [Flow] that listens for changes in the database via the [InvalidationTracker] and emits
2120  * sets of the tables that were invalidated.
2121  *
2122  * The Flow will emit at least one value, a set of all the tables registered for observation to
2123  * kick-start the stream unless [emitInitialState] is set to `false`.
2124  *
2125  * If one of the tables to observe does not exist in the database, this functions throws an
2126  * [IllegalArgumentException].
2127  *
2128  * The returned Flow can be used to create a stream that reacts to changes in the database:
2129  * ```
2130  * fun getArtistTours(from: Date, to: Date): Flow<Map<Artist, TourState>> {
2131  *   return db.invalidationTrackerFlow("Artist").map { _ ->
2132  *     val artists = artistsDao.getAllArtists()
2133  *     val tours = tourService.fetchStates(artists.map { it.id })
2134  *     associateTours(artists, tours, from, to)
2135  *   }
2136  * }
2137  * ```
2138  *
2139  * @param tables The name of the tables or views to observe.
2140  * @param emitInitialState Set to `false` if no initial emission is desired. Default value is
2141  *   `true`.
2142  */
2143 @Deprecated(
2144     message = "Replaced by equivalent API in InvalidationTracker.",
2145     replaceWith = ReplaceWith("this.invalidationTracker.createFlow(*tables)")
2146 )
invalidationTrackerFlownull2147 fun RoomDatabase.invalidationTrackerFlow(
2148     vararg tables: String,
2149     emitInitialState: Boolean = true
2150 ): Flow<Set<String>> = invalidationTracker.createFlow(*tables, emitInitialState = emitInitialState)
2151