/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.work.impl.utils; import static androidx.work.State.CANCELLED; import static androidx.work.State.FAILED; import static androidx.work.State.SUCCEEDED; import android.support.annotation.NonNull; import android.support.annotation.RestrictTo; import android.support.annotation.WorkerThread; import androidx.work.State; import androidx.work.impl.Processor; import androidx.work.impl.Scheduler; import androidx.work.impl.Schedulers; import androidx.work.impl.WorkDatabase; import androidx.work.impl.WorkManagerImpl; import androidx.work.impl.model.DependencyDao; import androidx.work.impl.model.WorkSpecDao; import java.util.List; import java.util.UUID; /** * A {@link Runnable} to cancel work. * * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public abstract class CancelWorkRunnable implements Runnable { void cancel(WorkManagerImpl workManagerImpl, String workSpecId) { recursivelyCancelWorkAndDependents(workManagerImpl.getWorkDatabase(), workSpecId); Processor processor = workManagerImpl.getProcessor(); processor.stopAndCancelWork(workSpecId); for (Scheduler scheduler : workManagerImpl.getSchedulers()) { scheduler.cancel(workSpecId); } } void reschedulePendingWorkers(WorkManagerImpl workManagerImpl) { Schedulers.schedule( workManagerImpl.getConfiguration(), workManagerImpl.getWorkDatabase(), workManagerImpl.getSchedulers()); } private void recursivelyCancelWorkAndDependents(WorkDatabase workDatabase, String workSpecId) { WorkSpecDao workSpecDao = workDatabase.workSpecDao(); DependencyDao dependencyDao = workDatabase.dependencyDao(); List dependentIds = dependencyDao.getDependentWorkIds(workSpecId); for (String id : dependentIds) { recursivelyCancelWorkAndDependents(workDatabase, id); } State state = workSpecDao.getState(workSpecId); if (state != SUCCEEDED && state != FAILED) { workSpecDao.setState(CANCELLED, workSpecId); } } /** * Creates a {@link CancelWorkRunnable} that cancels work for a specific id. * * @param id The id to cancel * @param workManagerImpl The {@link WorkManagerImpl} to use * @return A {@link Runnable} that cancels work for a specific id */ public static Runnable forId( @NonNull final UUID id, @NonNull final WorkManagerImpl workManagerImpl) { return new CancelWorkRunnable() { @WorkerThread @Override public void run() { cancel(workManagerImpl, id.toString()); reschedulePendingWorkers(workManagerImpl); } }; } /** * Creates a {@link CancelWorkRunnable} that cancels work for a specific tag. * * @param tag The tag to cancel * @param workManagerImpl The {@link WorkManagerImpl} to use * @return A {@link Runnable} that cancels work for a specific tag */ public static Runnable forTag( @NonNull final String tag, @NonNull final WorkManagerImpl workManagerImpl) { return new CancelWorkRunnable() { @WorkerThread @Override public void run() { WorkDatabase workDatabase = workManagerImpl.getWorkDatabase(); workDatabase.beginTransaction(); try { WorkSpecDao workSpecDao = workDatabase.workSpecDao(); List workSpecIds = workSpecDao.getUnfinishedWorkWithTag(tag); for (String workSpecId : workSpecIds) { cancel(workManagerImpl, workSpecId); } workDatabase.setTransactionSuccessful(); } finally { workDatabase.endTransaction(); } reschedulePendingWorkers(workManagerImpl); } }; } /** * Creates a {@link CancelWorkRunnable} that cancels work labelled with a specific name. * * @param name The name to cancel * @param workManagerImpl The {@link WorkManagerImpl} to use * @return A {@link Runnable} that cancels work labelled with a specific name */ public static Runnable forName( @NonNull final String name, @NonNull final WorkManagerImpl workManagerImpl) { return new CancelWorkRunnable() { @WorkerThread @Override public void run() { WorkDatabase workDatabase = workManagerImpl.getWorkDatabase(); workDatabase.beginTransaction(); try { WorkSpecDao workSpecDao = workDatabase.workSpecDao(); List workSpecIds = workSpecDao.getUnfinishedWorkWithName(name); for (String workSpecId : workSpecIds) { cancel(workManagerImpl, workSpecId); } workDatabase.setTransactionSuccessful(); } finally { workDatabase.endTransaction(); } reschedulePendingWorkers(workManagerImpl); } }; } /** * Creates a {@link CancelWorkRunnable} that cancels all work. * * @param workManagerImpl The {@link WorkManagerImpl} to use * @return A {@link Runnable} that cancels all work */ public static Runnable forAll(@NonNull final WorkManagerImpl workManagerImpl) { return new CancelWorkRunnable() { @Override public void run() { WorkDatabase workDatabase = workManagerImpl.getWorkDatabase(); workDatabase.beginTransaction(); try { WorkSpecDao workSpecDao = workDatabase.workSpecDao(); List workSpecIds = workSpecDao.getAllUnfinishedWork(); for (String workSpecId : workSpecIds) { cancel(workManagerImpl, workSpecId); } workDatabase.setTransactionSuccessful(); // Update the preferences new Preferences(workManagerImpl.getApplicationContext()) .setLastCancelAllTimeMillis(System.currentTimeMillis()); } finally { workDatabase.endTransaction(); } // No need to call reschedule pending workers here as we just cancelled everything. } }; } }