Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +80 −13 Original line number Diff line number Diff line Loading @@ -159,6 +159,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -301,6 +302,8 @@ public class JobSchedulerService extends com.android.server.SystemService private final ConnectivityController mConnectivityController; /** Need directly for sending uid state changes */ private final DeviceIdleJobsController mDeviceIdleJobsController; /** Need directly for sending exempted bucket changes */ private final FlexibilityController mFlexibilityController; /** Needed to get next estimated launch time. */ private final PrefetchController mPrefetchController; /** Needed to get remaining quota time. */ Loading Loading @@ -513,6 +516,10 @@ public class JobSchedulerService extends com.android.server.SystemService if (name == null) { continue; } if (DEBUG) { Slog.d(TAG, "DeviceConfig " + name + " changed to " + properties.getString(name, null)); } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: Loading Loading @@ -2532,17 +2539,17 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers = new ArrayList<StateController>(); mPrefetchController = new PrefetchController(this); mControllers.add(mPrefetchController); final FlexibilityController flexibilityController = mFlexibilityController = new FlexibilityController(this, mPrefetchController); mControllers.add(flexibilityController); mControllers.add(mFlexibilityController); mConnectivityController = new ConnectivityController(this, flexibilityController); new ConnectivityController(this, mFlexibilityController); mControllers.add(mConnectivityController); mControllers.add(new TimeController(this)); final IdleController idleController = new IdleController(this, flexibilityController); final IdleController idleController = new IdleController(this, mFlexibilityController); mControllers.add(idleController); final BatteryController batteryController = new BatteryController(this, flexibilityController); new BatteryController(this, mFlexibilityController); mControllers.add(batteryController); mStorageController = new StorageController(this); mControllers.add(mStorageController); Loading Loading @@ -3190,6 +3197,13 @@ public class JobSchedulerService extends com.android.server.SystemService } } @Override public void onExemptedBucketChanged(@NonNull ArraySet<JobStatus> changedJobs) { if (changedJobs.size() > 0) { mFlexibilityController.onExemptedBucketChanged(changedJobs); } } @Override public void onRestrictionStateChanged(@NonNull JobRestriction restriction, boolean stopOvertimeJobs) { Loading Loading @@ -3497,7 +3511,10 @@ public class JobSchedulerService extends com.android.server.SystemService } final boolean shouldForceBatchJob; if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { if (job.overrideState > JobStatus.OVERRIDE_NONE) { // The job should run for some test. Don't force batch it. shouldForceBatchJob = false; } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { // Never batch expedited or user-initiated jobs, even for RESTRICTED apps. shouldForceBatchJob = false; } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { Loading Loading @@ -4950,6 +4967,8 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId + " " + jobId + " s=" + satisfied + " f=" + force); final CountDownLatch delayLatch = new CountDownLatch(1); final JobStatus js; try { final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); Loading @@ -4958,7 +4977,7 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); if (js == null) { return JobSchedulerShellCommand.CMD_ERR_NO_JOB; } Loading @@ -4969,23 +4988,71 @@ public class JobSchedulerService extends com.android.server.SystemService // Re-evaluate constraints after the override is set in case one of the overridden // constraints was preventing another constraint from thinking it needed to update. for (int c = mControllers.size() - 1; c >= 0; --c) { mControllers.get(c).reevaluateStateLocked(uid); mControllers.get(c).evaluateStateLocked(js); } if (!js.isConstraintsSatisfied()) { if (js.hasConnectivityConstraint() && !js.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY) && js.wouldBeReadyWithConstraint(JobStatus.CONSTRAINT_CONNECTIVITY)) { // Because of how asynchronous the connectivity signals are, JobScheduler // may not get the connectivity satisfaction signal immediately. In this // case, wait a few seconds to see if it comes in before saying the // connectivity constraint isn't satisfied. mHandler.postDelayed( checkConstraintRunnableForTesting( mHandler, js, delayLatch, 5, 1000), 1000); } else { // There's no asynchronous signal to wait for. We can immediately say the // job's constraints aren't satisfied and return. js.overrideState = JobStatus.OVERRIDE_NONE; return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; } queueReadyJobsForExecutionLocked(); maybeRunPendingJobsLocked(); } else { delayLatch.countDown(); } } } catch (RemoteException e) { // can't happen return 0; } // Choose to block the return until we're sure about the state of the connectivity job // so that tests can expect a reliable state after calling the run command. try { delayLatch.await(7L, TimeUnit.SECONDS); } catch (InterruptedException e) { Slog.e(TAG, "Couldn't wait for asynchronous constraint change", e); } synchronized (mLock) { if (!js.isConstraintsSatisfied()) { js.overrideState = JobStatus.OVERRIDE_NONE; return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; } queueReadyJobsForExecutionLocked(); maybeRunPendingJobsLocked(); } return 0; } private static Runnable checkConstraintRunnableForTesting(@NonNull final Handler handler, @NonNull final JobStatus js, @NonNull final CountDownLatch latch, final int remainingAttempts, final long delayMs) { return () -> { if (remainingAttempts <= 0 || js.isConstraintsSatisfied()) { latch.countDown(); return; } handler.postDelayed( checkConstraintRunnableForTesting( handler, js, latch, remainingAttempts - 1, delayMs), delayMs); }; } // Shell command infrastructure: immediately timeout currently executing jobs int executeStopCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, boolean hasJobId, int jobId, Loading apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java +6 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ public interface StateChangedListener { void onNetworkChanged(JobStatus jobStatus, Network newNetwork); /** * Called when these jobs are added or removed from the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_EXEMPTED} bucket. */ void onExemptedBucketChanged(@NonNull ArraySet<JobStatus> jobs); /** * Called when these jobs are added or removed from the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. Loading apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +52 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; Loading Loading @@ -180,8 +181,12 @@ public final class FlexibilityController extends StateController { } }; private static final int MSG_UPDATE_JOBS = 0; private static final int MSG_UPDATE_JOB = 1; private static final int MSG_CHECK_ALL_JOBS = 0; /** Check the jobs in {@link #mJobsToCheck} */ private static final int MSG_CHECK_JOBS = 1; @GuardedBy("mLock") private final ArraySet<JobStatus> mJobsToCheck = new ArraySet<>(); public FlexibilityController( JobSchedulerService service, PrefetchController prefetchController) { Loading Loading @@ -266,7 +271,14 @@ public final class FlexibilityController extends StateController { @GuardedBy("mLock") boolean isFlexibilitySatisfiedLocked(JobStatus js) { return !mFlexibilityEnabled // Exclude all jobs of the TOP app || mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP // Only exclude DEFAULT+ priority jobs for BFGS+ apps || (mService.getUidBias(js.getSourceUid()) >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) // Only exclude DEFAULT+ priority jobs for EXEMPTED apps || (js.getStandbyBucket() == EXEMPTED_INDEX && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) || hasEnoughSatisfiedConstraintsLocked(js) || mService.isCurrentlyRunningLocked(js); } Loading Loading @@ -371,8 +383,16 @@ public final class FlexibilityController extends StateController { // Push the job update to the handler to avoid blocking other controllers and // potentially batch back-to-back controller state updates together. mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget(); mHandler.obtainMessage(MSG_CHECK_ALL_JOBS).sendToTarget(); } } } /** Called with a set of apps who have been added to or removed from the exempted bucket. */ public void onExemptedBucketChanged(@NonNull ArraySet<JobStatus> changedJobs) { synchronized (mLock) { mJobsToCheck.addAll(changedJobs); mHandler.sendEmptyMessage(MSG_CHECK_JOBS); } } Loading Loading @@ -485,7 +505,9 @@ public final class FlexibilityController extends StateController { @Override @GuardedBy("mLock") public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { if (prevBias != JobInfo.BIAS_TOP_APP && newBias != JobInfo.BIAS_TOP_APP) { if (prevBias < JobInfo.BIAS_BOUND_FOREGROUND_SERVICE && newBias < JobInfo.BIAS_BOUND_FOREGROUND_SERVICE) { // All changes are below BFGS. There's no significant change to care about. return; } final long nowElapsed = sElapsedRealtimeClock.millis(); Loading Loading @@ -710,7 +732,8 @@ public final class FlexibilityController extends StateController { } mFlexibilityTracker.setNumDroppedFlexibleConstraints(js, js.getNumAppliedFlexibleConstraints()); mHandler.obtainMessage(MSG_UPDATE_JOB, js).sendToTarget(); mJobsToCheck.add(js); mHandler.sendEmptyMessage(MSG_CHECK_JOBS); return; } if (nextTimeElapsed == NO_LIFECYCLE_END) { Loading Loading @@ -761,10 +784,11 @@ public final class FlexibilityController extends StateController { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_JOBS: removeMessages(MSG_UPDATE_JOBS); case MSG_CHECK_ALL_JOBS: removeMessages(MSG_CHECK_ALL_JOBS); synchronized (mLock) { mJobsToCheck.clear(); final long nowElapsed = sElapsedRealtimeClock.millis(); final ArraySet<JobStatus> changedJobs = new ArraySet<>(); Loading @@ -790,19 +814,25 @@ public final class FlexibilityController extends StateController { } break; case MSG_UPDATE_JOB: case MSG_CHECK_JOBS: synchronized (mLock) { final JobStatus js = (JobStatus) msg.obj; final long nowElapsed = sElapsedRealtimeClock.millis(); ArraySet<JobStatus> changedJobs = new ArraySet<>(); for (int i = mJobsToCheck.size() - 1; i >= 0; --i) { final JobStatus js = mJobsToCheck.valueAt(i); if (DEBUG) { Slog.d("blah", "Checking on " + js.toShortString()); Slog.d(TAG, "Checking on " + js.toShortString()); } final long nowElapsed = sElapsedRealtimeClock.millis(); if (js.setFlexibilityConstraintSatisfied( nowElapsed, isFlexibilitySatisfiedLocked(js))) { // TODO(141645789): add method that will take a single job ArraySet<JobStatus> changedJob = new ArraySet<>(); changedJob.add(js); mStateChangedListener.onControllerStateChanged(changedJob); changedJobs.add(js); } } mJobsToCheck.clear(); if (changedJobs.size() > 0) { mStateChangedListener.onControllerStateChanged(changedJobs); } } break; Loading Loading @@ -985,7 +1015,10 @@ public final class FlexibilityController extends StateController { pw.println(":"); pw.increaseIndent(); pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS).println(); pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS); pw.print("("); JobStatus.dumpConstraints(pw, APPLIED_CONSTRAINTS); pw.println(")"); pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println(); pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println(); pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS, Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +1 −4 Original line number Diff line number Diff line Loading @@ -106,11 +106,8 @@ public final class JobStatus { public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) Loading Loading @@ -2194,7 +2191,7 @@ public final class JobStatus { * @return Whether or not this job would be ready to run if it had the specified constraint * granted, based on its requirements. */ boolean wouldBeReadyWithConstraint(int constraint) { public boolean wouldBeReadyWithConstraint(int constraint) { return readinessStatusWithConstraint(constraint, true); } Loading apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +8 −0 Original line number Diff line number Diff line Loading @@ -2511,6 +2511,7 @@ public final class QuotaController extends StateController { + " to bucketIndex " + bucketIndex); } List<JobStatus> restrictedChanges = new ArrayList<>(); ArraySet<JobStatus> exemptedChanges = new ArraySet<>(); synchronized (mLock) { ShrinkableDebits debits = mEJStats.get(userId, packageName); if (debits != null) { Loading @@ -2530,6 +2531,10 @@ public final class QuotaController extends StateController { && bucketIndex != js.getStandbyBucket()) { restrictedChanges.add(js); } if ((bucketIndex == EXEMPTED_INDEX || js.getStandbyBucket() == EXEMPTED_INDEX) && bucketIndex != js.getStandbyBucket()) { exemptedChanges.add(js); } js.setStandbyBucket(bucketIndex); } Timer timer = mPkgTimers.get(userId, packageName); Loading @@ -2544,6 +2549,9 @@ public final class QuotaController extends StateController { maybeUpdateConstraintForPkgLocked( sElapsedRealtimeClock.millis(), userId, packageName)); } if (exemptedChanges.size() > 0) { mStateChangedListener.onExemptedBucketChanged(exemptedChanges); } if (restrictedChanges.size() > 0) { mStateChangedListener.onRestrictedBucketChanged(restrictedChanges); } Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +80 −13 Original line number Diff line number Diff line Loading @@ -159,6 +159,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -301,6 +302,8 @@ public class JobSchedulerService extends com.android.server.SystemService private final ConnectivityController mConnectivityController; /** Need directly for sending uid state changes */ private final DeviceIdleJobsController mDeviceIdleJobsController; /** Need directly for sending exempted bucket changes */ private final FlexibilityController mFlexibilityController; /** Needed to get next estimated launch time. */ private final PrefetchController mPrefetchController; /** Needed to get remaining quota time. */ Loading Loading @@ -513,6 +516,10 @@ public class JobSchedulerService extends com.android.server.SystemService if (name == null) { continue; } if (DEBUG) { Slog.d(TAG, "DeviceConfig " + name + " changed to " + properties.getString(name, null)); } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: Loading Loading @@ -2532,17 +2539,17 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers = new ArrayList<StateController>(); mPrefetchController = new PrefetchController(this); mControllers.add(mPrefetchController); final FlexibilityController flexibilityController = mFlexibilityController = new FlexibilityController(this, mPrefetchController); mControllers.add(flexibilityController); mControllers.add(mFlexibilityController); mConnectivityController = new ConnectivityController(this, flexibilityController); new ConnectivityController(this, mFlexibilityController); mControllers.add(mConnectivityController); mControllers.add(new TimeController(this)); final IdleController idleController = new IdleController(this, flexibilityController); final IdleController idleController = new IdleController(this, mFlexibilityController); mControllers.add(idleController); final BatteryController batteryController = new BatteryController(this, flexibilityController); new BatteryController(this, mFlexibilityController); mControllers.add(batteryController); mStorageController = new StorageController(this); mControllers.add(mStorageController); Loading Loading @@ -3190,6 +3197,13 @@ public class JobSchedulerService extends com.android.server.SystemService } } @Override public void onExemptedBucketChanged(@NonNull ArraySet<JobStatus> changedJobs) { if (changedJobs.size() > 0) { mFlexibilityController.onExemptedBucketChanged(changedJobs); } } @Override public void onRestrictionStateChanged(@NonNull JobRestriction restriction, boolean stopOvertimeJobs) { Loading Loading @@ -3497,7 +3511,10 @@ public class JobSchedulerService extends com.android.server.SystemService } final boolean shouldForceBatchJob; if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { if (job.overrideState > JobStatus.OVERRIDE_NONE) { // The job should run for some test. Don't force batch it. shouldForceBatchJob = false; } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { // Never batch expedited or user-initiated jobs, even for RESTRICTED apps. shouldForceBatchJob = false; } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { Loading Loading @@ -4950,6 +4967,8 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId + " " + jobId + " s=" + satisfied + " f=" + force); final CountDownLatch delayLatch = new CountDownLatch(1); final JobStatus js; try { final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); Loading @@ -4958,7 +4977,7 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); if (js == null) { return JobSchedulerShellCommand.CMD_ERR_NO_JOB; } Loading @@ -4969,23 +4988,71 @@ public class JobSchedulerService extends com.android.server.SystemService // Re-evaluate constraints after the override is set in case one of the overridden // constraints was preventing another constraint from thinking it needed to update. for (int c = mControllers.size() - 1; c >= 0; --c) { mControllers.get(c).reevaluateStateLocked(uid); mControllers.get(c).evaluateStateLocked(js); } if (!js.isConstraintsSatisfied()) { if (js.hasConnectivityConstraint() && !js.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY) && js.wouldBeReadyWithConstraint(JobStatus.CONSTRAINT_CONNECTIVITY)) { // Because of how asynchronous the connectivity signals are, JobScheduler // may not get the connectivity satisfaction signal immediately. In this // case, wait a few seconds to see if it comes in before saying the // connectivity constraint isn't satisfied. mHandler.postDelayed( checkConstraintRunnableForTesting( mHandler, js, delayLatch, 5, 1000), 1000); } else { // There's no asynchronous signal to wait for. We can immediately say the // job's constraints aren't satisfied and return. js.overrideState = JobStatus.OVERRIDE_NONE; return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; } queueReadyJobsForExecutionLocked(); maybeRunPendingJobsLocked(); } else { delayLatch.countDown(); } } } catch (RemoteException e) { // can't happen return 0; } // Choose to block the return until we're sure about the state of the connectivity job // so that tests can expect a reliable state after calling the run command. try { delayLatch.await(7L, TimeUnit.SECONDS); } catch (InterruptedException e) { Slog.e(TAG, "Couldn't wait for asynchronous constraint change", e); } synchronized (mLock) { if (!js.isConstraintsSatisfied()) { js.overrideState = JobStatus.OVERRIDE_NONE; return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; } queueReadyJobsForExecutionLocked(); maybeRunPendingJobsLocked(); } return 0; } private static Runnable checkConstraintRunnableForTesting(@NonNull final Handler handler, @NonNull final JobStatus js, @NonNull final CountDownLatch latch, final int remainingAttempts, final long delayMs) { return () -> { if (remainingAttempts <= 0 || js.isConstraintsSatisfied()) { latch.countDown(); return; } handler.postDelayed( checkConstraintRunnableForTesting( handler, js, latch, remainingAttempts - 1, delayMs), delayMs); }; } // Shell command infrastructure: immediately timeout currently executing jobs int executeStopCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, boolean hasJobId, int jobId, Loading
apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java +6 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ public interface StateChangedListener { void onNetworkChanged(JobStatus jobStatus, Network newNetwork); /** * Called when these jobs are added or removed from the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_EXEMPTED} bucket. */ void onExemptedBucketChanged(@NonNull ArraySet<JobStatus> jobs); /** * Called when these jobs are added or removed from the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +52 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; Loading Loading @@ -180,8 +181,12 @@ public final class FlexibilityController extends StateController { } }; private static final int MSG_UPDATE_JOBS = 0; private static final int MSG_UPDATE_JOB = 1; private static final int MSG_CHECK_ALL_JOBS = 0; /** Check the jobs in {@link #mJobsToCheck} */ private static final int MSG_CHECK_JOBS = 1; @GuardedBy("mLock") private final ArraySet<JobStatus> mJobsToCheck = new ArraySet<>(); public FlexibilityController( JobSchedulerService service, PrefetchController prefetchController) { Loading Loading @@ -266,7 +271,14 @@ public final class FlexibilityController extends StateController { @GuardedBy("mLock") boolean isFlexibilitySatisfiedLocked(JobStatus js) { return !mFlexibilityEnabled // Exclude all jobs of the TOP app || mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP // Only exclude DEFAULT+ priority jobs for BFGS+ apps || (mService.getUidBias(js.getSourceUid()) >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) // Only exclude DEFAULT+ priority jobs for EXEMPTED apps || (js.getStandbyBucket() == EXEMPTED_INDEX && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) || hasEnoughSatisfiedConstraintsLocked(js) || mService.isCurrentlyRunningLocked(js); } Loading Loading @@ -371,8 +383,16 @@ public final class FlexibilityController extends StateController { // Push the job update to the handler to avoid blocking other controllers and // potentially batch back-to-back controller state updates together. mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget(); mHandler.obtainMessage(MSG_CHECK_ALL_JOBS).sendToTarget(); } } } /** Called with a set of apps who have been added to or removed from the exempted bucket. */ public void onExemptedBucketChanged(@NonNull ArraySet<JobStatus> changedJobs) { synchronized (mLock) { mJobsToCheck.addAll(changedJobs); mHandler.sendEmptyMessage(MSG_CHECK_JOBS); } } Loading Loading @@ -485,7 +505,9 @@ public final class FlexibilityController extends StateController { @Override @GuardedBy("mLock") public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { if (prevBias != JobInfo.BIAS_TOP_APP && newBias != JobInfo.BIAS_TOP_APP) { if (prevBias < JobInfo.BIAS_BOUND_FOREGROUND_SERVICE && newBias < JobInfo.BIAS_BOUND_FOREGROUND_SERVICE) { // All changes are below BFGS. There's no significant change to care about. return; } final long nowElapsed = sElapsedRealtimeClock.millis(); Loading Loading @@ -710,7 +732,8 @@ public final class FlexibilityController extends StateController { } mFlexibilityTracker.setNumDroppedFlexibleConstraints(js, js.getNumAppliedFlexibleConstraints()); mHandler.obtainMessage(MSG_UPDATE_JOB, js).sendToTarget(); mJobsToCheck.add(js); mHandler.sendEmptyMessage(MSG_CHECK_JOBS); return; } if (nextTimeElapsed == NO_LIFECYCLE_END) { Loading Loading @@ -761,10 +784,11 @@ public final class FlexibilityController extends StateController { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_JOBS: removeMessages(MSG_UPDATE_JOBS); case MSG_CHECK_ALL_JOBS: removeMessages(MSG_CHECK_ALL_JOBS); synchronized (mLock) { mJobsToCheck.clear(); final long nowElapsed = sElapsedRealtimeClock.millis(); final ArraySet<JobStatus> changedJobs = new ArraySet<>(); Loading @@ -790,19 +814,25 @@ public final class FlexibilityController extends StateController { } break; case MSG_UPDATE_JOB: case MSG_CHECK_JOBS: synchronized (mLock) { final JobStatus js = (JobStatus) msg.obj; final long nowElapsed = sElapsedRealtimeClock.millis(); ArraySet<JobStatus> changedJobs = new ArraySet<>(); for (int i = mJobsToCheck.size() - 1; i >= 0; --i) { final JobStatus js = mJobsToCheck.valueAt(i); if (DEBUG) { Slog.d("blah", "Checking on " + js.toShortString()); Slog.d(TAG, "Checking on " + js.toShortString()); } final long nowElapsed = sElapsedRealtimeClock.millis(); if (js.setFlexibilityConstraintSatisfied( nowElapsed, isFlexibilitySatisfiedLocked(js))) { // TODO(141645789): add method that will take a single job ArraySet<JobStatus> changedJob = new ArraySet<>(); changedJob.add(js); mStateChangedListener.onControllerStateChanged(changedJob); changedJobs.add(js); } } mJobsToCheck.clear(); if (changedJobs.size() > 0) { mStateChangedListener.onControllerStateChanged(changedJobs); } } break; Loading Loading @@ -985,7 +1015,10 @@ public final class FlexibilityController extends StateController { pw.println(":"); pw.increaseIndent(); pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS).println(); pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS); pw.print("("); JobStatus.dumpConstraints(pw, APPLIED_CONSTRAINTS); pw.println(")"); pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println(); pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println(); pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS, Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +1 −4 Original line number Diff line number Diff line Loading @@ -106,11 +106,8 @@ public final class JobStatus { public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) Loading Loading @@ -2194,7 +2191,7 @@ public final class JobStatus { * @return Whether or not this job would be ready to run if it had the specified constraint * granted, based on its requirements. */ boolean wouldBeReadyWithConstraint(int constraint) { public boolean wouldBeReadyWithConstraint(int constraint) { return readinessStatusWithConstraint(constraint, true); } Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +8 −0 Original line number Diff line number Diff line Loading @@ -2511,6 +2511,7 @@ public final class QuotaController extends StateController { + " to bucketIndex " + bucketIndex); } List<JobStatus> restrictedChanges = new ArrayList<>(); ArraySet<JobStatus> exemptedChanges = new ArraySet<>(); synchronized (mLock) { ShrinkableDebits debits = mEJStats.get(userId, packageName); if (debits != null) { Loading @@ -2530,6 +2531,10 @@ public final class QuotaController extends StateController { && bucketIndex != js.getStandbyBucket()) { restrictedChanges.add(js); } if ((bucketIndex == EXEMPTED_INDEX || js.getStandbyBucket() == EXEMPTED_INDEX) && bucketIndex != js.getStandbyBucket()) { exemptedChanges.add(js); } js.setStandbyBucket(bucketIndex); } Timer timer = mPkgTimers.get(userId, packageName); Loading @@ -2544,6 +2549,9 @@ public final class QuotaController extends StateController { maybeUpdateConstraintForPkgLocked( sElapsedRealtimeClock.millis(), userId, packageName)); } if (exemptedChanges.size() > 0) { mStateChangedListener.onExemptedBucketChanged(exemptedChanges); } if (restrictedChanges.size() > 0) { mStateChangedListener.onRestrictedBucketChanged(restrictedChanges); } Loading