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 17 package androidx.work.impl.utils; 18 19 import static androidx.work.State.CANCELLED; 20 import static androidx.work.State.FAILED; 21 import static androidx.work.State.SUCCEEDED; 22 23 import android.support.annotation.NonNull; 24 import android.support.annotation.RestrictTo; 25 import android.support.annotation.WorkerThread; 26 27 import androidx.work.State; 28 import androidx.work.impl.Processor; 29 import androidx.work.impl.Scheduler; 30 import androidx.work.impl.Schedulers; 31 import androidx.work.impl.WorkDatabase; 32 import androidx.work.impl.WorkManagerImpl; 33 import androidx.work.impl.model.DependencyDao; 34 import androidx.work.impl.model.WorkSpecDao; 35 36 import java.util.List; 37 import java.util.UUID; 38 39 /** 40 * A {@link Runnable} to cancel work. 41 * 42 * @hide 43 */ 44 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 45 public abstract class CancelWorkRunnable implements Runnable { 46 cancel(WorkManagerImpl workManagerImpl, String workSpecId)47 void cancel(WorkManagerImpl workManagerImpl, String workSpecId) { 48 recursivelyCancelWorkAndDependents(workManagerImpl.getWorkDatabase(), workSpecId); 49 50 Processor processor = workManagerImpl.getProcessor(); 51 processor.stopAndCancelWork(workSpecId); 52 53 for (Scheduler scheduler : workManagerImpl.getSchedulers()) { 54 scheduler.cancel(workSpecId); 55 } 56 } 57 reschedulePendingWorkers(WorkManagerImpl workManagerImpl)58 void reschedulePendingWorkers(WorkManagerImpl workManagerImpl) { 59 Schedulers.schedule( 60 workManagerImpl.getConfiguration(), 61 workManagerImpl.getWorkDatabase(), 62 workManagerImpl.getSchedulers()); 63 } 64 recursivelyCancelWorkAndDependents(WorkDatabase workDatabase, String workSpecId)65 private void recursivelyCancelWorkAndDependents(WorkDatabase workDatabase, String workSpecId) { 66 67 WorkSpecDao workSpecDao = workDatabase.workSpecDao(); 68 DependencyDao dependencyDao = workDatabase.dependencyDao(); 69 70 List<String> dependentIds = dependencyDao.getDependentWorkIds(workSpecId); 71 for (String id : dependentIds) { 72 recursivelyCancelWorkAndDependents(workDatabase, id); 73 } 74 75 State state = workSpecDao.getState(workSpecId); 76 if (state != SUCCEEDED && state != FAILED) { 77 workSpecDao.setState(CANCELLED, workSpecId); 78 } 79 } 80 81 /** 82 * Creates a {@link CancelWorkRunnable} that cancels work for a specific id. 83 * 84 * @param id The id to cancel 85 * @param workManagerImpl The {@link WorkManagerImpl} to use 86 * @return A {@link Runnable} that cancels work for a specific id 87 */ forId( @onNull final UUID id, @NonNull final WorkManagerImpl workManagerImpl)88 public static Runnable forId( 89 @NonNull final UUID id, 90 @NonNull final WorkManagerImpl workManagerImpl) { 91 return new CancelWorkRunnable() { 92 @WorkerThread 93 @Override 94 public void run() { 95 cancel(workManagerImpl, id.toString()); 96 reschedulePendingWorkers(workManagerImpl); 97 } 98 }; 99 } 100 101 /** 102 * Creates a {@link CancelWorkRunnable} that cancels work for a specific tag. 103 * 104 * @param tag The tag to cancel 105 * @param workManagerImpl The {@link WorkManagerImpl} to use 106 * @return A {@link Runnable} that cancels work for a specific tag 107 */ 108 public static Runnable forTag( 109 @NonNull final String tag, 110 @NonNull final WorkManagerImpl workManagerImpl) { 111 return new CancelWorkRunnable() { 112 @WorkerThread 113 @Override 114 public void run() { 115 WorkDatabase workDatabase = workManagerImpl.getWorkDatabase(); 116 workDatabase.beginTransaction(); 117 try { 118 WorkSpecDao workSpecDao = workDatabase.workSpecDao(); 119 List<String> workSpecIds = workSpecDao.getUnfinishedWorkWithTag(tag); 120 for (String workSpecId : workSpecIds) { 121 cancel(workManagerImpl, workSpecId); 122 } 123 workDatabase.setTransactionSuccessful(); 124 } finally { 125 workDatabase.endTransaction(); 126 } 127 reschedulePendingWorkers(workManagerImpl); 128 } 129 }; 130 } 131 132 /** 133 * Creates a {@link CancelWorkRunnable} that cancels work labelled with a specific name. 134 * 135 * @param name The name to cancel 136 * @param workManagerImpl The {@link WorkManagerImpl} to use 137 * @return A {@link Runnable} that cancels work labelled with a specific name 138 */ 139 public static Runnable forName( 140 @NonNull final String name, 141 @NonNull final WorkManagerImpl workManagerImpl) { 142 return new CancelWorkRunnable() { 143 @WorkerThread 144 @Override 145 public void run() { 146 WorkDatabase workDatabase = workManagerImpl.getWorkDatabase(); 147 workDatabase.beginTransaction(); 148 try { 149 WorkSpecDao workSpecDao = workDatabase.workSpecDao(); 150 List<String> workSpecIds = workSpecDao.getUnfinishedWorkWithName(name); 151 for (String workSpecId : workSpecIds) { 152 cancel(workManagerImpl, workSpecId); 153 } 154 workDatabase.setTransactionSuccessful(); 155 } finally { 156 workDatabase.endTransaction(); 157 } 158 reschedulePendingWorkers(workManagerImpl); 159 } 160 }; 161 } 162 163 /** 164 * Creates a {@link CancelWorkRunnable} that cancels all work. 165 * 166 * @param workManagerImpl The {@link WorkManagerImpl} to use 167 * @return A {@link Runnable} that cancels all work 168 */ 169 public static Runnable forAll(@NonNull final WorkManagerImpl workManagerImpl) { 170 return new CancelWorkRunnable() { 171 @Override 172 public void run() { 173 WorkDatabase workDatabase = workManagerImpl.getWorkDatabase(); 174 workDatabase.beginTransaction(); 175 try { 176 WorkSpecDao workSpecDao = workDatabase.workSpecDao(); 177 List<String> workSpecIds = workSpecDao.getAllUnfinishedWork(); 178 for (String workSpecId : workSpecIds) { 179 cancel(workManagerImpl, workSpecId); 180 } 181 workDatabase.setTransactionSuccessful(); 182 // Update the preferences 183 new Preferences(workManagerImpl.getApplicationContext()) 184 .setLastCancelAllTimeMillis(System.currentTimeMillis()); 185 } finally { 186 workDatabase.endTransaction(); 187 } 188 // No need to call reschedule pending workers here as we just cancelled everything. 189 } 190 }; 191 } 192 } 193