package androidx.work.impl.background.greedy; import android.app.ActivityManager; import android.app.Application; import android.content.Context; import android.os.Build; import android.os.Process; import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.appcompat.widget.ActivityChooserModel; import androidx.work.Configuration; import androidx.work.Logger; import androidx.work.WorkInfo; import androidx.work.impl.ExecutionListener; import androidx.work.impl.Scheduler; import androidx.work.impl.WorkManagerImpl; import androidx.work.impl.constraints.WorkConstraintsCallback; import androidx.work.impl.constraints.WorkConstraintsTracker; import androidx.work.impl.model.WorkSpec; import androidx.work.impl.utils.taskexecutor.TaskExecutor; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) public class GreedyScheduler implements Scheduler, WorkConstraintsCallback, ExecutionListener { private static final String TAG = Logger.tagWithPrefix("GreedyScheduler"); private final Set mConstrainedWorkSpecs = new HashSet(); private final Context mContext; private DelayedWorkTracker mDelayedWorkTracker; public Boolean mIsMainProcess; private final Object mLock; private boolean mRegisteredExecutionListener; private final WorkConstraintsTracker mWorkConstraintsTracker; private final WorkManagerImpl mWorkManagerImpl; public GreedyScheduler(@NonNull Context context, @NonNull Configuration configuration, @NonNull TaskExecutor taskExecutor, @NonNull WorkManagerImpl workManagerImpl) { this.mContext = context; this.mWorkManagerImpl = workManagerImpl; this.mWorkConstraintsTracker = new WorkConstraintsTracker(context, taskExecutor, this); this.mDelayedWorkTracker = new DelayedWorkTracker(this, configuration.getRunnableScheduler()); this.mLock = new Object(); } @VisibleForTesting public GreedyScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManagerImpl, @NonNull WorkConstraintsTracker workConstraintsTracker) { this.mContext = context; this.mWorkManagerImpl = workManagerImpl; this.mWorkConstraintsTracker = workConstraintsTracker; this.mLock = new Object(); } @Nullable private String getProcessName() { List runningAppProcesses; if (Build.VERSION.SDK_INT >= 28) { return Application.getProcessName(); } try { Method declaredMethod = Class.forName("android.app.ActivityThread", false, GreedyScheduler.class.getClassLoader()).getDeclaredMethod("currentProcessName", new Class[0]); declaredMethod.setAccessible(true); Object invoke = declaredMethod.invoke(null, new Object[0]); if (invoke instanceof String) { return (String) invoke; } } catch (Throwable th) { Logger.get().debug(TAG, "Unable to check ActivityThread for processName", th); } int myPid = Process.myPid(); ActivityManager activityManager = (ActivityManager) this.mContext.getSystemService(ActivityChooserModel.ATTRIBUTE_ACTIVITY); if (activityManager == null || (runningAppProcesses = activityManager.getRunningAppProcesses()) == null || runningAppProcesses.isEmpty()) { return null; } for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) { if (runningAppProcessInfo.pid == myPid) { return runningAppProcessInfo.processName; } } return null; } private void registerExecutionListenerIfNeeded() { if (!this.mRegisteredExecutionListener) { this.mWorkManagerImpl.getProcessor().addExecutionListener(this); this.mRegisteredExecutionListener = true; } } private void removeConstraintTrackingFor(@NonNull String str) { synchronized (this.mLock) { Iterator it = this.mConstrainedWorkSpecs.iterator(); while (true) { if (!it.hasNext()) { break; } WorkSpec next = it.next(); if (next.f29id.equals(str)) { Logger.get().debug(TAG, String.format("Stopping tracking for %s", str), new Throwable[0]); this.mConstrainedWorkSpecs.remove(next); this.mWorkConstraintsTracker.replace(this.mConstrainedWorkSpecs); break; } } } } @Override // androidx.work.impl.Scheduler public void cancel(@NonNull String str) { if (this.mIsMainProcess == null) { this.mIsMainProcess = Boolean.valueOf(TextUtils.equals(this.mContext.getPackageName(), getProcessName())); } if (!this.mIsMainProcess.booleanValue()) { Logger.get().info(TAG, "Ignoring schedule request in non-main process", new Throwable[0]); return; } registerExecutionListenerIfNeeded(); Logger.get().debug(TAG, String.format("Cancelling work ID %s", str), new Throwable[0]); DelayedWorkTracker delayedWorkTracker = this.mDelayedWorkTracker; if (delayedWorkTracker != null) { delayedWorkTracker.unschedule(str); } this.mWorkManagerImpl.stopWork(str); } @Override // androidx.work.impl.Scheduler public boolean hasLimitedSchedulingSlots() { return false; } @Override // androidx.work.impl.constraints.WorkConstraintsCallback public void onAllConstraintsMet(@NonNull List list) { for (String str : list) { Logger.get().debug(TAG, String.format("Constraints met: Scheduling work ID %s", str), new Throwable[0]); this.mWorkManagerImpl.startWork(str); } } @Override // androidx.work.impl.constraints.WorkConstraintsCallback public void onAllConstraintsNotMet(@NonNull List list) { for (String str : list) { Logger.get().debug(TAG, String.format("Constraints not met: Cancelling work ID %s", str), new Throwable[0]); this.mWorkManagerImpl.stopWork(str); } } @Override // androidx.work.impl.ExecutionListener public void onExecuted(@NonNull String str, boolean z2) { removeConstraintTrackingFor(str); } @Override // androidx.work.impl.Scheduler public void schedule(@NonNull WorkSpec... workSpecArr) { if (this.mIsMainProcess == null) { this.mIsMainProcess = Boolean.valueOf(TextUtils.equals(this.mContext.getPackageName(), getProcessName())); } if (!this.mIsMainProcess.booleanValue()) { Logger.get().info(TAG, "Ignoring schedule request in non-main process", new Throwable[0]); return; } registerExecutionListenerIfNeeded(); HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); for (WorkSpec workSpec : workSpecArr) { long calculateNextRunTime = workSpec.calculateNextRunTime(); long currentTimeMillis = System.currentTimeMillis(); if (workSpec.state == WorkInfo.State.ENQUEUED) { if (currentTimeMillis < calculateNextRunTime) { DelayedWorkTracker delayedWorkTracker = this.mDelayedWorkTracker; if (delayedWorkTracker != null) { delayedWorkTracker.schedule(workSpec); } } else if (workSpec.hasConstraints()) { int i = Build.VERSION.SDK_INT; if (i >= 23 && workSpec.constraints.requiresDeviceIdle()) { Logger.get().debug(TAG, String.format("Ignoring WorkSpec %s, Requires device idle.", workSpec), new Throwable[0]); } else if (i < 24 || !workSpec.constraints.hasContentUriTriggers()) { hashSet.add(workSpec); hashSet2.add(workSpec.f29id); } else { Logger.get().debug(TAG, String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.", workSpec), new Throwable[0]); } } else { Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.f29id), new Throwable[0]); this.mWorkManagerImpl.startWork(workSpec.f29id); } } } synchronized (this.mLock) { if (!hashSet.isEmpty()) { Logger.get().debug(TAG, String.format("Starting tracking for [%s]", TextUtils.join(",", hashSet2)), new Throwable[0]); this.mConstrainedWorkSpecs.addAll(hashSet); this.mWorkConstraintsTracker.replace(this.mConstrainedWorkSpecs); } } } @VisibleForTesting public void setDelayedWorkTracker(@NonNull DelayedWorkTracker delayedWorkTracker) { this.mDelayedWorkTracker = delayedWorkTracker; } }