1 /*
2 * Copyright 2017 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:JvmName("CancelWorkRunnable")
17
18 package androidx.work.impl.utils
19
20 import android.app.job.JobParameters
21 import androidx.work.Operation
22 import androidx.work.WorkInfo
23 import androidx.work.impl.Schedulers
24 import androidx.work.impl.WorkDatabase
25 import androidx.work.impl.WorkManagerImpl
26 import androidx.work.launchOperation
27 import java.util.UUID
28 import kotlin.collections.removeLast as removeLastKt
29
cancelnull30 private fun cancel(workManagerImpl: WorkManagerImpl, workSpecId: String) {
31 iterativelyCancelWorkAndDependents(workManagerImpl.workDatabase, workSpecId)
32 val processor = workManagerImpl.processor
33 processor.stopAndCancelWork(workSpecId, JobParameters.STOP_REASON_CANCELLED_BY_APP)
34 for (scheduler in workManagerImpl.schedulers) {
35 scheduler.cancel(workSpecId)
36 }
37 }
38
reschedulePendingWorkersnull39 private fun reschedulePendingWorkers(workManagerImpl: WorkManagerImpl) {
40 Schedulers.schedule(
41 workManagerImpl.configuration,
42 workManagerImpl.workDatabase,
43 workManagerImpl.schedulers
44 )
45 }
46
iterativelyCancelWorkAndDependentsnull47 private fun iterativelyCancelWorkAndDependents(workDatabase: WorkDatabase, workSpecId: String) {
48 val workSpecDao = workDatabase.workSpecDao()
49 val dependencyDao = workDatabase.dependencyDao()
50 val idsToProcess = mutableListOf(workSpecId)
51 while (idsToProcess.isNotEmpty()) {
52 val id = idsToProcess.removeLastKt()
53 // Don't fail already cancelled work.
54 val state = workSpecDao.getState(id)
55 if (state !== WorkInfo.State.SUCCEEDED && state !== WorkInfo.State.FAILED) {
56 workSpecDao.setCancelledState(id)
57 }
58 idsToProcess.addAll(dependencyDao.getDependentWorkIds(id))
59 }
60 }
61
62 /**
63 * Cancels work for a specific id.
64 *
65 * @param id The id to cancel
66 * @param workManagerImpl The [WorkManagerImpl] to use
67 * @return A [Operation]
68 */
forIdnull69 fun forId(id: UUID, workManagerImpl: WorkManagerImpl): Operation =
70 launchOperation(
71 tracer = workManagerImpl.configuration.tracer,
72 label = "CancelWorkById",
73 workManagerImpl.workTaskExecutor.serialTaskExecutor
74 ) {
75 val workDatabase = workManagerImpl.workDatabase
76 workDatabase.runInTransaction { cancel(workManagerImpl, id.toString()) }
77 reschedulePendingWorkers(workManagerImpl)
78 }
79
80 /**
81 * Cancels work for a specific tag.
82 *
83 * @param tag The tag to cancel
84 * @param workManagerImpl The [WorkManagerImpl] to use
85 * @return A [Operation]
86 */
forTagnull87 fun forTag(tag: String, workManagerImpl: WorkManagerImpl): Operation =
88 launchOperation(
89 tracer = workManagerImpl.configuration.tracer,
90 label = "CancelWorkByTag_$tag",
91 executor = workManagerImpl.workTaskExecutor.serialTaskExecutor
92 ) {
93 val workDatabase = workManagerImpl.workDatabase
94 workDatabase.runInTransaction {
95 val workSpecDao = workDatabase.workSpecDao()
96 val workSpecIds = workSpecDao.getUnfinishedWorkWithTag(tag)
97 for (workSpecId in workSpecIds) {
98 cancel(workManagerImpl, workSpecId)
99 }
100 }
101 reschedulePendingWorkers(workManagerImpl)
102 }
103
104 /**
105 * Cancels work labelled with a specific name.
106 *
107 * @param name The name to cancel
108 * @param workManagerImpl The [WorkManagerImpl] to use
109 * @return A [Operation]
110 */
forNamenull111 fun forName(name: String, workManagerImpl: WorkManagerImpl): Operation =
112 launchOperation(
113 tracer = workManagerImpl.configuration.tracer,
114 label = "CancelWorkByName_$name",
115 workManagerImpl.workTaskExecutor.serialTaskExecutor
116 ) {
117 forNameInline(name, workManagerImpl)
118 reschedulePendingWorkers(workManagerImpl)
119 }
120
forNameInlinenull121 fun forNameInline(name: String, workManagerImpl: WorkManagerImpl) {
122 val workDatabase = workManagerImpl.workDatabase
123 workDatabase.runInTransaction {
124 val workSpecDao = workDatabase.workSpecDao()
125 val workSpecIds = workSpecDao.getUnfinishedWorkWithName(name)
126 for (workSpecId in workSpecIds) {
127 cancel(workManagerImpl, workSpecId)
128 }
129 }
130 }
131
132 /**
133 * Cancels all work.
134 *
135 * @param workManagerImpl The [WorkManagerImpl] to use
136 * @return A [Operation] that cancels all work
137 */
forAllnull138 fun forAll(workManagerImpl: WorkManagerImpl): Operation =
139 launchOperation(
140 tracer = workManagerImpl.configuration.tracer,
141 label = "CancelAllWork",
142 workManagerImpl.workTaskExecutor.serialTaskExecutor
143 ) {
144 val workDatabase = workManagerImpl.workDatabase
145 workDatabase.runInTransaction {
146 val workSpecDao = workDatabase.workSpecDao()
147 val workSpecIds = workSpecDao.getAllUnfinishedWork()
148 for (workSpecId in workSpecIds) {
149 cancel(workManagerImpl, workSpecId)
150 }
151 // Update the last cancelled time in Preference.
152 PreferenceUtils(workDatabase)
153 .setLastCancelAllTimeMillis(workManagerImpl.configuration.clock.currentTimeMillis())
154 }
155 // No need to call reschedule pending workers here as we just cancelled everything.
156 }
157