1 /*
2  * Copyright 2018 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 package androidx.work
17 
18 import android.os.Build
19 import android.util.Log
20 import androidx.annotation.IntRange
21 import androidx.annotation.RestrictTo
22 import androidx.core.util.Consumer
23 import androidx.work.impl.DefaultRunnableScheduler
24 import androidx.work.impl.Scheduler
25 import androidx.work.impl.utils.INITIAL_ID
26 import java.util.concurrent.Executor
27 import java.util.concurrent.Executors
28 import java.util.concurrent.ThreadFactory
29 import java.util.concurrent.atomic.AtomicInteger
30 import kotlin.coroutines.ContinuationInterceptor
31 import kotlin.coroutines.CoroutineContext
32 import kotlin.math.max
33 import kotlin.math.min
34 import kotlinx.coroutines.CoroutineDispatcher
35 import kotlinx.coroutines.Dispatchers
36 import kotlinx.coroutines.asCoroutineDispatcher
37 import kotlinx.coroutines.asExecutor
38 
39 /**
40  * The Configuration object used to customize [WorkManager] upon initialization. Configuration
41  * contains various parameters used to setup WorkManager. For example, it is possible to customize
42  * the [Executor] used by [Worker]s here.
43  *
44  * To set a custom Configuration for WorkManager, see [WorkManager.initialize].
45  */
46 @OptIn(ExperimentalConfigurationApi::class)
47 class Configuration internal constructor(builder: Builder) {
48     /** The [Executor] used by [WorkManager] to execute [Worker]s. */
49     val executor: Executor
50 
51     /** The [CoroutineContext] used by [WorkManager] to execute [CoroutineWorker]s. */
52     val workerCoroutineContext: CoroutineContext
53 
54     /** The [Executor] used by [WorkManager] for all its internal business logic */
55     val taskExecutor: Executor
56 
57     /** The [Clock] used by [WorkManager] to calculate schedules and perform book-keeping. */
58     val clock: Clock
59 
60     /** The [WorkerFactory] used by [WorkManager] to create [ListenableWorker]s */
61     val workerFactory: WorkerFactory
62 
63     /** The [InputMergerFactory] used by [WorkManager] to create instances of [InputMerger]s. */
64     val inputMergerFactory: InputMergerFactory
65 
66     /** The [RunnableScheduler] to keep track of timed work in the in-process scheduler. */
67     val runnableScheduler: RunnableScheduler
68 
69     /**
70      * The exception handler that is used to intercept exceptions caused when trying to initialize
71      * [WorkManager].
72      */
73     val initializationExceptionHandler: Consumer<Throwable>?
74 
75     /**
76      * The exception handler that can be used to intercept exceptions caused when trying to schedule
77      * [WorkRequest]s.
78      */
79     val schedulingExceptionHandler: Consumer<Throwable>?
80 
81     /**
82      * The exception handler that can be used to intercept exceptions caused when trying to
83      * initialize [ListenableWorker]s.
84      */
85     val workerInitializationExceptionHandler: Consumer<WorkerExceptionInfo>?
86 
87     /**
88      * The exception handler that can be used to intercept exceptions caused when trying to execute
89      * [ListenableWorker]s.
90      */
91     val workerExecutionExceptionHandler: Consumer<WorkerExceptionInfo>?
92 
93     /** The [String] name of the process where work should be scheduled. */
94     val defaultProcessName: String?
95 
96     /** The minimum logging level, corresponding to the constants found in [android.util.Log] */
97     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val minimumLoggingLevel: Int
98 
99     /**
100      * The first valid id (inclusive) used by [WorkManager] when creating new instances of
101      * [android.app.job.JobInfo]s.
102      *
103      * If the current `jobId` goes beyond the bounds of the defined range of
104      * ([Configuration.minJobSchedulerId], [Configuration.maxJobSchedulerId]), it is reset to
105      * ([Configuration.minJobSchedulerId]).
106      */
107     val minJobSchedulerId: Int
108 
109     /**
110      * The last valid id (inclusive) used by [WorkManager] when creating new instances of
111      * [android.app.job.JobInfo]s.
112      *
113      * If the current `jobId` goes beyond the bounds of the defined range of
114      * ([Configuration.minJobSchedulerId], [Configuration.maxJobSchedulerId]), it is reset to
115      * ([Configuration.minJobSchedulerId]).
116      */
117     val maxJobSchedulerId: Int
118 
119     /**
120      * Maximum number of Workers with [Constraints.contentUriTriggers] that could be enqueued
121      * simultaneously.
122      *
123      * Unlike the other workers Workers with [Constraints.contentUriTriggers] must immediately
124      * occupy slots in JobScheduler to avoid missing updates, thus they are separated in its own
125      * category.
126      */
127     val contentUriTriggerWorkersLimit: Int
128 
129     /**
130      * The maximum number of system requests which can be enqueued by [WorkManager] when using
131      * [android.app.job.JobScheduler] or [android.app.AlarmManager]
132      */
133     @get:IntRange(from = MIN_SCHEDULER_LIMIT.toLong(), to = Scheduler.MAX_SCHEDULER_LIMIT.toLong())
134     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
135     val maxSchedulerLimit: Int
136 
137     /** @return `true` If the default task [Executor] is being used */
138     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val isUsingDefaultTaskExecutor: Boolean
139 
140     /**
141      * Specifies whether WorkManager automatically set
142      * [android.app.job.JobInfo.Builder.setImportantWhileForeground] for workers that are eligible
143      * to run immediately.
144      */
145     @get:ExperimentalConfigurationApi
146     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
147     @property:ExperimentalConfigurationApi
148     val isMarkingJobsAsImportantWhileForeground: Boolean
149 
150     /**
151      * @return The [Tracer] instance that can be used by [WorkManager] to record trace spans when
152      *   executing [WorkRequest]s.
153      */
154     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val tracer: Tracer
155 
156     init {
157         val builderWorkerDispatcher = builder.workerContext
158 
159         executor =
160             builder.executor
161                 ?: builderWorkerDispatcher?.asExecutor()
162                 ?: createDefaultExecutor(isTaskExecutor = false)
163 
164         workerCoroutineContext =
165             when {
166                 builderWorkerDispatcher != null -> builderWorkerDispatcher
167                 // we don't want simply always use executor.asCoroutineDispatcher()
168                 // as compatibility measure
169                 builder.executor != null -> executor.asCoroutineDispatcher()
170                 else -> Dispatchers.Default
171             }
172 
173         isUsingDefaultTaskExecutor = builder.taskExecutor == null
174         // This executor is used for *both* WorkManager's tasks and Room's query executor.
175         // So this should not be a single threaded executor. Writes will still be serialized
176         // as this will be wrapped with an SerialExecutor.
177         taskExecutor = builder.taskExecutor ?: createDefaultExecutor(isTaskExecutor = true)
178         clock = builder.clock ?: SystemClock()
179         workerFactory = builder.workerFactory ?: DefaultWorkerFactory
180         inputMergerFactory = builder.inputMergerFactory ?: NoOpInputMergerFactory
181         runnableScheduler = builder.runnableScheduler ?: DefaultRunnableScheduler()
182         minimumLoggingLevel = builder.loggingLevel
183         minJobSchedulerId = builder.minJobSchedulerId
184         maxJobSchedulerId = builder.maxJobSchedulerId
185         maxSchedulerLimit =
186             if (Build.VERSION.SDK_INT == 23) {
187                 // We double schedule jobs in SDK 23. So use half the number of max slots specified.
188                 builder.maxSchedulerLimit / 2
189             } else {
190                 builder.maxSchedulerLimit
191             }
192         initializationExceptionHandler = builder.initializationExceptionHandler
193         schedulingExceptionHandler = builder.schedulingExceptionHandler
194         workerInitializationExceptionHandler = builder.workerInitializationExceptionHandler
195         workerExecutionExceptionHandler = builder.workerExecutionExceptionHandler
196         defaultProcessName = builder.defaultProcessName
197         contentUriTriggerWorkersLimit = builder.contentUriTriggerWorkersLimit
198         isMarkingJobsAsImportantWhileForeground = builder.markJobsAsImportantWhileForeground
199         tracer = builder.tracer ?: createDefaultTracer()
200     }
201 
202     /** A Builder for [Configuration]s. */
203     class Builder {
204         internal var executor: Executor? = null
205         internal var workerContext: CoroutineContext? = null
206         internal var workerFactory: WorkerFactory? = null
207         internal var inputMergerFactory: InputMergerFactory? = null
208         internal var taskExecutor: Executor? = null
209         internal var clock: Clock? = null
210         internal var runnableScheduler: RunnableScheduler? = null
211         internal var initializationExceptionHandler: Consumer<Throwable>? = null
212         internal var schedulingExceptionHandler: Consumer<Throwable>? = null
213         internal var workerInitializationExceptionHandler: Consumer<WorkerExceptionInfo>? = null
214         internal var workerExecutionExceptionHandler: Consumer<WorkerExceptionInfo>? = null
215         internal var defaultProcessName: String? = null
216         internal var loggingLevel: Int = Log.INFO
217         internal var minJobSchedulerId: Int = INITIAL_ID
218         internal var maxJobSchedulerId: Int = Int.MAX_VALUE
219         internal var maxSchedulerLimit: Int = MIN_SCHEDULER_LIMIT
220         internal var contentUriTriggerWorkersLimit: Int = DEFAULT_CONTENT_URI_TRIGGERS_WORKERS_LIMIT
221         internal var markJobsAsImportantWhileForeground: Boolean = true
222         internal var tracer: Tracer? = null
223 
224         /** Creates a new [Configuration.Builder]. */
225         constructor()
226 
227         /**
228          * Creates a new [Configuration.Builder] with an existing [Configuration] as its template.
229          *
230          * @param configuration An existing [Configuration] to use as a template
231          */
232         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
233         constructor(configuration: Configuration) {
234             // Note that these must be accessed through fields and not the getters, which can
235             // otherwise manipulate the returned value (see getMaxSchedulerLimit(), for example).
236             executor = configuration.executor
237             workerFactory = configuration.workerFactory
238             inputMergerFactory = configuration.inputMergerFactory
239             taskExecutor = configuration.taskExecutor
240             clock = configuration.clock
241             loggingLevel = configuration.minimumLoggingLevel
242             minJobSchedulerId = configuration.minJobSchedulerId
243             maxJobSchedulerId = configuration.maxJobSchedulerId
244             maxSchedulerLimit = configuration.maxSchedulerLimit
245             runnableScheduler = configuration.runnableScheduler
246             initializationExceptionHandler = configuration.initializationExceptionHandler
247             schedulingExceptionHandler = configuration.schedulingExceptionHandler
248             workerInitializationExceptionHandler =
249                 configuration.workerInitializationExceptionHandler
250             workerExecutionExceptionHandler = configuration.workerExecutionExceptionHandler
251             defaultProcessName = configuration.defaultProcessName
252             contentUriTriggerWorkersLimit = configuration.contentUriTriggerWorkersLimit
253             markJobsAsImportantWhileForeground =
254                 configuration.isMarkingJobsAsImportantWhileForeground
255             tracer = configuration.tracer
256         }
257 
258         /**
259          * Specifies a custom [WorkerFactory] for WorkManager.
260          *
261          * @param workerFactory A [WorkerFactory] for creating [ListenableWorker]s
262          * @return This [Builder] instance
263          */
setWorkerFactorynull264         fun setWorkerFactory(workerFactory: WorkerFactory): Builder {
265             this.workerFactory = workerFactory
266             return this
267         }
268 
269         /**
270          * Specifies a custom [InputMergerFactory] for WorkManager.
271          *
272          * @param inputMergerFactory A [InputMergerFactory] for creating [InputMerger]s
273          * @return This [Builder] instance
274          */
setInputMergerFactorynull275         fun setInputMergerFactory(inputMergerFactory: InputMergerFactory): Builder {
276             this.inputMergerFactory = inputMergerFactory
277             return this
278         }
279 
280         /**
281          * Specifies a custom [Executor] to run [Worker.doWork].
282          *
283          * If [setWorkerCoroutineContext] wasn't called then the [executor] will be used as
284          * [CoroutineDispatcher] to run [CoroutineWorker] as well.
285          *
286          * @param executor An [Executor] for running [Worker]s
287          * @return This [Builder] instance
288          */
setExecutornull289         fun setExecutor(executor: Executor): Builder {
290             this.executor = executor
291             return this
292         }
293 
294         /**
295          * Specifies a custom [CoroutineContext] to run [CoroutineWorker.doWork]. WorkManager will
296          * use its own `Job` with the provided [CoroutineContext].
297          *
298          * If [setExecutor] wasn't called then [context] will be used as [Executor] to run [Worker]
299          * as well.
300          *
301          * @param context A [CoroutineContext] for running [CoroutineWorker]s
302          * @return This [Builder] instance
303          */
setWorkerCoroutineContextnull304         fun setWorkerCoroutineContext(context: CoroutineContext): Builder {
305             this.workerContext = context
306             return this
307         }
308 
309         /**
310          * Specifies a [Executor] which will be used by WorkManager for all its internal
311          * book-keeping.
312          *
313          * For best performance this [Executor] should be bounded.
314          *
315          * For more information look at [androidx.room.RoomDatabase.Builder.setQueryExecutor].
316          *
317          * @param taskExecutor The [Executor] which will be used by WorkManager for all its internal
318          *   book-keeping
319          * @return This [Builder] instance
320          */
setTaskExecutornull321         fun setTaskExecutor(taskExecutor: Executor): Builder {
322             this.taskExecutor = taskExecutor
323             return this
324         }
325 
326         /**
327          * Sets a [Clock] for WorkManager to calculate schedules and perform book-keeping.
328          *
329          * This should only be overridden for testing. It must return the same value as
330          * [System.currentTimeMillis] in production code.
331          *
332          * @param clock The [Clock] to use
333          * @return This [Builder] instance
334          */
setClocknull335         fun setClock(clock: Clock): Builder {
336             this.clock = clock
337             return this
338         }
339 
340         /**
341          * Specifies the range of [android.app.job.JobInfo] IDs that can be used by [WorkManager].
342          * WorkManager needs a range of at least `1000` IDs.
343          *
344          * JobScheduler uses integers as identifiers for jobs, and WorkManager delegates to
345          * JobScheduler on certain API levels. In order to not clash job codes used in the rest of
346          * your app, you can use this method to tell WorkManager the valid range of job IDs that it
347          * can use.
348          *
349          * The default values are `0` and `Integer#MAX_VALUE`.
350          *
351          * @param minJobSchedulerId The first valid [android.app.job.JobInfo] ID (inclusive).
352          * @param maxJobSchedulerId The last valid [android.app.job.JobInfo] ID (inclusive).
353          * @return This [Builder] instance
354          * @throws IllegalArgumentException when the size of the range is less than 1000
355          */
setJobSchedulerJobIdRangenull356         fun setJobSchedulerJobIdRange(minJobSchedulerId: Int, maxJobSchedulerId: Int): Builder {
357             require(maxJobSchedulerId - minJobSchedulerId >= 1000) {
358                 "WorkManager needs a range of at least 1000 job ids."
359             }
360             this.minJobSchedulerId = minJobSchedulerId
361             this.maxJobSchedulerId = maxJobSchedulerId
362             return this
363         }
364 
365         /**
366          * Specifies the maximum number of system requests made by [WorkManager] when using
367          * [android.app.job.JobScheduler] or [android.app.AlarmManager].
368          *
369          * By default, WorkManager might schedule a large number of alarms or JobScheduler jobs. If
370          * your app uses JobScheduler or AlarmManager directly, this might exhaust the OS-enforced
371          * limit on the number of jobs or alarms an app is allowed to schedule. To help manage this
372          * situation, you can use this method to reduce the number of underlying jobs and alarms
373          * that WorkManager might schedule.
374          *
375          * When the application exceeds this limit, WorkManager maintains an internal queue of
376          * [WorkRequest]s, and schedules them when slots become free.
377          *
378          * WorkManager requires a minimum of [Configuration.MIN_SCHEDULER_LIMIT] slots; this is also
379          * the default value. The total number of slots also cannot exceed `50`.
380          *
381          * @param maxSchedulerLimit The total number of jobs which can be enqueued by [WorkManager]
382          *   when using [android.app.job.JobScheduler].
383          * @return This [Builder] instance
384          * @throws IllegalArgumentException if `maxSchedulerLimit` is less than
385          *   [Configuration.MIN_SCHEDULER_LIMIT]
386          */
setMaxSchedulerLimitnull387         fun setMaxSchedulerLimit(maxSchedulerLimit: Int): Builder {
388             require(maxSchedulerLimit >= MIN_SCHEDULER_LIMIT) {
389                 "WorkManager needs to be able to schedule at least 20 jobs in JobScheduler."
390             }
391             this.maxSchedulerLimit = min(maxSchedulerLimit, Scheduler.MAX_SCHEDULER_LIMIT)
392             return this
393         }
394 
395         /**
396          * Specifies the maximum number of Workers with [Constraints.contentUriTriggers] that could
397          * be enqueued simultaneously.
398          *
399          * Unlike the other workers Workers with [Constraints.contentUriTriggers] must immediately
400          * occupy slots in JobScheduler to avoid missing updates, thus they are separated in its own
401          * category.
402          */
setContentUriTriggerWorkersLimitnull403         fun setContentUriTriggerWorkersLimit(contentUriTriggerWorkersLimit: Int): Builder {
404             this.contentUriTriggerWorkersLimit = max(contentUriTriggerWorkersLimit, 0)
405             return this
406         }
407 
408         /**
409          * Specifies the minimum logging level, corresponding to the constants found in
410          * [android.util.Log]. For example, specifying [android.util.Log.VERBOSE] will log
411          * everything, whereas specifying [android.util.Log.ERROR] will only log errors and
412          * assertions.The default value is [android.util.Log.INFO].
413          *
414          * @param loggingLevel The minimum logging level, corresponding to the constants found in
415          *   [android.util.Log]
416          * @return This [Builder] instance
417          */
setMinimumLoggingLevelnull418         fun setMinimumLoggingLevel(loggingLevel: Int): Builder {
419             this.loggingLevel = loggingLevel
420             return this
421         }
422 
423         /**
424          * Specifies the [RunnableScheduler] to be used by [WorkManager].
425          *
426          * This is used by the in-process scheduler to keep track of timed work.
427          *
428          * @param runnableScheduler The [RunnableScheduler] to be used
429          * @return This [Builder] instance
430          */
setRunnableSchedulernull431         fun setRunnableScheduler(runnableScheduler: RunnableScheduler): Builder {
432             this.runnableScheduler = runnableScheduler
433             return this
434         }
435 
436         /**
437          * Specifies a `Consumer<Throwable>` that can be used to intercept exceptions caused when
438          * trying to initialize {@link WorkManager}, that usually happens when WorkManager cannot
439          * access its internal datastore.
440          *
441          * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
442          *
443          * @param exceptionHandler an instance to handle exceptions
444          * @return This [Builder] instance
445          */
setInitializationExceptionHandlernull446         fun setInitializationExceptionHandler(exceptionHandler: Consumer<Throwable>): Builder {
447             this.initializationExceptionHandler = exceptionHandler
448             return this
449         }
450 
451         /**
452          * Specifies a `Consumer<Throwable>` that can be used to intercept exceptions caused when
453          * trying to schedule [WorkRequest]s.
454          *
455          * It allows the application to handle a [Throwable] throwable typically caused when trying
456          * to schedule [WorkRequest]s.
457          *
458          * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
459          *
460          * @param schedulingExceptionHandler an instance to handle exceptions
461          * @return This [Builder] instance
462          */
setSchedulingExceptionHandlernull463         fun setSchedulingExceptionHandler(
464             schedulingExceptionHandler: Consumer<Throwable>
465         ): Builder {
466             this.schedulingExceptionHandler = schedulingExceptionHandler
467             return this
468         }
469 
470         /**
471          * Specifies a `WorkerExceptionHandler` that can be used to intercept exceptions caused when
472          * trying to initialize [ListenableWorker]s.
473          *
474          * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
475          *
476          * @param workerExceptionHandler an instance to handle exceptions
477          * @return This [Builder] instance
478          */
setWorkerInitializationExceptionHandlernull479         fun setWorkerInitializationExceptionHandler(
480             workerExceptionHandler: Consumer<WorkerExceptionInfo>
481         ): Builder {
482             this.workerInitializationExceptionHandler = workerExceptionHandler
483             return this
484         }
485 
486         /**
487          * Specifies a `WorkerExceptionHandler` that can be used to intercept exceptions caused when
488          * trying to execute [ListenableWorker]s.
489          *
490          * This exception handler will be invoked on a thread bound to [Configuration.taskExecutor].
491          *
492          * @param workerExceptionHandler an instance to handle exceptions
493          * @return This [Builder] instance
494          */
setWorkerExecutionExceptionHandlernull495         fun setWorkerExecutionExceptionHandler(
496             workerExceptionHandler: Consumer<WorkerExceptionInfo>
497         ): Builder {
498             this.workerExecutionExceptionHandler = workerExceptionHandler
499             return this
500         }
501 
502         /**
503          * Designates the primary process that [WorkManager] should schedule work in.
504          *
505          * @param processName The [String] process name.
506          * @return This [Builder] instance
507          */
setDefaultProcessNamenull508         fun setDefaultProcessName(processName: String): Builder {
509             defaultProcessName = processName
510             return this
511         }
512 
513         /**
514          * Regulates whether WorkManager should automatically set
515          * [android.app.job.JobInfo.Builder.setImportantWhileForeground] for workers that are
516          * eligible to run immediately.
517          *
518          * It will have effects only on API levels >= 23.
519          *
520          * @param markAsImportant whether to mark jobs as important
521          * @return This [Builder] instance
522          */
523         @ExperimentalConfigurationApi
setMarkingJobsAsImportantWhileForegroundnull524         fun setMarkingJobsAsImportantWhileForeground(markAsImportant: Boolean): Builder {
525             this.markJobsAsImportantWhileForeground = markAsImportant
526             return this
527         }
528 
529         /**
530          * Specifies the [Tracer] that can be used by [WorkManager] to record trace spans.
531          *
532          * @param tracer The [Tracer] instance to be used.
533          * @return This [Builder] instance
534          */
535         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
setTracernull536         fun setTracer(tracer: Tracer): Builder {
537             this.tracer = tracer
538             return this
539         }
540 
541         /**
542          * Builds a [Configuration] object.
543          *
544          * @return A [Configuration] object with this [Builder]'s parameters.
545          */
buildnull546         fun build(): Configuration {
547             return Configuration(this)
548         }
549     }
550 
551     /**
552      * A class that can provide the [Configuration] for WorkManager and allow for on-demand
553      * initialization of WorkManager. To do this:
554      * - Disable `androidx.work.WorkManagerInitializer` in your manifest
555      * - Implement the [Configuration.Provider] interface on your [android.app.Application] class
556      * - Use [WorkManager.getInstance] when accessing WorkManager (NOT [WorkManager.getInstance])
557      *
558      * Note that on-demand initialization may delay some useful features of WorkManager such as
559      * automatic rescheduling of work following a crash and recovery from the application being
560      * force-stopped by the user or device.
561      *
562      * @see WorkManager.initialize
563      */
564     interface Provider {
565         /** The [Configuration] used to initialize WorkManager */
566         val workManagerConfiguration: Configuration
567     }
568 
569     companion object {
570         /**
571          * The minimum number of system requests which can be enqueued by [WorkManager] when using
572          * [android.app.job.JobScheduler] or [android.app.AlarmManager].
573          */
574         const val MIN_SCHEDULER_LIMIT = 20
575     }
576 }
577 
578 internal const val DEFAULT_CONTENT_URI_TRIGGERS_WORKERS_LIMIT = 8
579 
createDefaultExecutornull580 private fun createDefaultExecutor(isTaskExecutor: Boolean): Executor {
581     val factory =
582         object : ThreadFactory {
583             private val threadCount = AtomicInteger(0)
584 
585             override fun newThread(runnable: Runnable): Thread {
586                 // Thread names are constrained to a max of 15 characters by the Linux Kernel.
587                 val prefix = if (isTaskExecutor) "WM.task-" else "androidx.work-"
588                 return Thread(runnable, "$prefix${threadCount.incrementAndGet()}")
589             }
590         }
591     return Executors.newFixedThreadPool(
592         // This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
593         max(2, min(Runtime.getRuntime().availableProcessors() - 1, 4)),
594         factory
595     )
596 }
597 
createDefaultTracernull598 private fun createDefaultTracer(): Tracer {
599     // Delegate to AndroidX Tracing while leaving the implementation open-ended for a pluggable
600     // implementation.
601     val tracer =
602         object : Tracer {
603             override fun isEnabled(): Boolean {
604                 return androidx.tracing.Trace.isEnabled()
605             }
606 
607             override fun beginSection(label: String) {
608                 androidx.tracing.Trace.beginSection(label)
609             }
610 
611             override fun endSection() {
612                 androidx.tracing.Trace.endSection()
613             }
614 
615             override fun beginAsyncSection(methodName: String, cookie: Int) {
616                 androidx.tracing.Trace.beginAsyncSection(methodName, cookie)
617             }
618 
619             override fun endAsyncSection(methodName: String, cookie: Int) {
620                 androidx.tracing.Trace.endAsyncSection(methodName, cookie)
621             }
622         }
623     return tracer
624 }
625 
CoroutineContextnull626 private fun CoroutineContext?.asExecutor(): Executor? =
627     (this?.get(ContinuationInterceptor) as? CoroutineDispatcher)?.asExecutor()
628