Merge "Apply thermal throttling to long-running jobs."
This commit is contained in:
@@ -542,6 +542,30 @@ class JobConcurrencyManager {
|
||||
return mRunningJobs.contains(job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the specified job has been executing for longer than the minimum
|
||||
* execution guarantee.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
boolean isJobLongRunningLocked(@NonNull JobStatus job) {
|
||||
if (!mRunningJobs.contains(job)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = mActiveServices.size() - 1; i >= 0; --i) {
|
||||
final JobServiceContext jsc = mActiveServices.get(i);
|
||||
final JobStatus jobStatus = jsc.getRunningJobLocked();
|
||||
|
||||
if (jobStatus == job) {
|
||||
return !jsc.isWithinExecutionGuaranteeTime();
|
||||
}
|
||||
}
|
||||
|
||||
Slog.wtf(TAG, "Couldn't find long running job on a context");
|
||||
mRunningJobs.remove(job);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a job that is "similar" to the provided job is currently running.
|
||||
* "Similar" in this context means any job that the {@link JobStore} would consider equivalent
|
||||
@@ -989,6 +1013,26 @@ class JobConcurrencyManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any jobs that have run for more than their minimum execution guarantee and are
|
||||
* restricted by the given {@link JobRestriction}.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
void maybeStopLongRunningJobsLocked(@NonNull JobRestriction restriction) {
|
||||
for (int i = mActiveServices.size() - 1; i >= 0; --i) {
|
||||
final JobServiceContext jsc = mActiveServices.get(i);
|
||||
final JobStatus jobStatus = jsc.getRunningJobLocked();
|
||||
|
||||
if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()
|
||||
&& restriction.isJobRestricted(jobStatus)) {
|
||||
jsc.cancelExecutingJobLocked(restriction.getReason(),
|
||||
restriction.getInternalReason(),
|
||||
JobParameters.getInternalReasonCodeDescription(
|
||||
restriction.getInternalReason()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
void stopNonReadyActiveJobsLocked() {
|
||||
for (int i = 0; i < mActiveServices.size(); i++) {
|
||||
@@ -1349,6 +1393,12 @@ class JobConcurrencyManager {
|
||||
if (mPowerManager.isDeviceIdleMode()) {
|
||||
return "deep doze";
|
||||
}
|
||||
final JobRestriction jobRestriction;
|
||||
if ((jobRestriction = mService.checkIfRestricted(js)) != null) {
|
||||
return "restriction:"
|
||||
+ JobParameters.getInternalReasonCodeDescription(
|
||||
jobRestriction.getInternalReason());
|
||||
}
|
||||
|
||||
// Update config in case memory usage has changed significantly.
|
||||
updateCounterConfigLocked();
|
||||
|
||||
@@ -1772,6 +1772,12 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
return mConcurrencyManager.isJobRunningLocked(job);
|
||||
}
|
||||
|
||||
/** @see JobConcurrencyManager#isJobLongRunningLocked(JobStatus) */
|
||||
@GuardedBy("mLock")
|
||||
public boolean isLongRunningLocked(JobStatus job) {
|
||||
return mConcurrencyManager.isJobLongRunningLocked(job);
|
||||
}
|
||||
|
||||
private void noteJobPending(JobStatus job) {
|
||||
mJobPackageTracker.notePending(job);
|
||||
}
|
||||
@@ -2049,6 +2055,17 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestrictionStateChanged(@NonNull JobRestriction restriction,
|
||||
boolean stopLongRunningJobs) {
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
if (stopLongRunningJobs) {
|
||||
synchronized (mLock) {
|
||||
mConcurrencyManager.maybeStopLongRunningJobsLocked(restriction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunJobNow(JobStatus jobStatus) {
|
||||
if (jobStatus == null) {
|
||||
|
||||
@@ -736,10 +736,10 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
@GuardedBy("mLock")
|
||||
private void doCancelLocked(@JobParameters.StopReason int stopReasonCode,
|
||||
int internalStopReasonCode, @Nullable String debugReason) {
|
||||
if (mVerb == VERB_FINISHED) {
|
||||
if (mVerb == VERB_FINISHED || mVerb == VERB_STOPPING) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"Trying to process cancel for torn-down context, ignoring.");
|
||||
"Too late to process cancel for context (verb=" + mVerb + "), ignoring.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.server.job.controllers.JobStatus;
|
||||
import com.android.server.job.restrictions.JobRestriction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -36,6 +37,16 @@ public interface StateChangedListener {
|
||||
*/
|
||||
void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs);
|
||||
|
||||
/**
|
||||
* Called by a {@link com.android.server.job.restrictions.JobRestriction} to notify the
|
||||
* JobScheduler that it should check on the state of all jobs.
|
||||
*
|
||||
* @param stopLongRunningJobs Whether to stop any jobs that have run for more than their minimum
|
||||
* execution guarantee and are restricted by the changed restriction
|
||||
*/
|
||||
void onRestrictionStateChanged(@NonNull JobRestriction restriction,
|
||||
boolean stopLongRunningJobs);
|
||||
|
||||
/**
|
||||
* Called by the controller to notify the JobManager that regardless of the state of the task,
|
||||
* it must be run immediately.
|
||||
|
||||
@@ -73,9 +73,10 @@ public class ThermalStatusRestriction extends JobRestriction {
|
||||
// bucket (thus resulting in us beginning to enforce the tightest
|
||||
// restrictions).
|
||||
|| (mThermalStatus < UPPER_THRESHOLD && status > UPPER_THRESHOLD);
|
||||
final boolean increased = mThermalStatus < status;
|
||||
mThermalStatus = status;
|
||||
if (significantChange) {
|
||||
mService.onControllerStateChanged(null);
|
||||
mService.onRestrictionStateChanged(ThermalStatusRestriction.this, increased);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -89,16 +90,19 @@ public class ThermalStatusRestriction extends JobRestriction {
|
||||
final int priority = job.getEffectivePriority();
|
||||
if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) {
|
||||
// For moderate throttling, only let expedited jobs and high priority regular jobs that
|
||||
// are already running run.
|
||||
// haven't been running for long run.
|
||||
return !job.shouldTreatAsExpeditedJob()
|
||||
&& !(priority == JobInfo.PRIORITY_HIGH
|
||||
&& mService.isCurrentlyRunningLocked(job));
|
||||
&& mService.isCurrentlyRunningLocked(job)
|
||||
&& !mService.isLongRunningLocked(job));
|
||||
}
|
||||
if (mThermalStatus >= LOW_PRIORITY_THRESHOLD) {
|
||||
// For light throttling, throttle all min priority jobs and all low priority jobs that
|
||||
// aren't already running.
|
||||
return (priority == JobInfo.PRIORITY_LOW && !mService.isCurrentlyRunningLocked(job))
|
||||
|| priority == JobInfo.PRIORITY_MIN;
|
||||
// aren't already running or have been running for long enough.
|
||||
return priority == JobInfo.PRIORITY_MIN
|
||||
|| (priority == JobInfo.PRIORITY_LOW
|
||||
&& (!mService.isCurrentlyRunningLocked(job)
|
||||
|| mService.isLongRunningLocked(job)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -111,42 +113,46 @@ public class ThermalStatusRestrictionTest {
|
||||
InOrder inOrder = inOrder(mJobSchedulerService);
|
||||
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
|
||||
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService, never())
|
||||
.onRestrictionStateChanged(any(), anyBoolean());
|
||||
assertEquals(THERMAL_STATUS_NONE, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
// Moving within LOW and UPPER thresholds
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_LIGHT);
|
||||
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
|
||||
assertEquals(THERMAL_STATUS_LIGHT, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_MODERATE);
|
||||
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
|
||||
assertEquals(THERMAL_STATUS_MODERATE, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SEVERE);
|
||||
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
|
||||
assertEquals(THERMAL_STATUS_SEVERE, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
// Changing outside of range
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_CRITICAL);
|
||||
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService, never())
|
||||
.onRestrictionStateChanged(any(), eq(true));
|
||||
assertEquals(THERMAL_STATUS_CRITICAL, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_EMERGENCY);
|
||||
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService, never())
|
||||
.onRestrictionStateChanged(any(), anyBoolean());
|
||||
assertEquals(THERMAL_STATUS_EMERGENCY, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SHUTDOWN);
|
||||
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService, never())
|
||||
.onRestrictionStateChanged(any(), anyBoolean());
|
||||
assertEquals(THERMAL_STATUS_SHUTDOWN, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
// Cross values we care about
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
|
||||
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(false));
|
||||
assertEquals(THERMAL_STATUS_NONE, mThermalStatusRestriction.getThermalStatus());
|
||||
|
||||
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_EMERGENCY);
|
||||
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
|
||||
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
|
||||
assertEquals(THERMAL_STATUS_EMERGENCY, mThermalStatusRestriction.getThermalStatus());
|
||||
}
|
||||
|
||||
@@ -160,12 +166,16 @@ public class ThermalStatusRestrictionTest {
|
||||
createJobBuilder(2).setPriority(JobInfo.PRIORITY_LOW).build());
|
||||
final JobStatus jobLowPriorityRunning = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(3).setPriority(JobInfo.PRIORITY_LOW).build());
|
||||
final JobStatus jobLowPriorityRunningLong = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(9).setPriority(JobInfo.PRIORITY_LOW).build());
|
||||
final JobStatus jobDefaultPriority = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(4).setPriority(JobInfo.PRIORITY_DEFAULT).build());
|
||||
final JobStatus jobHighPriority = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(5).setPriority(JobInfo.PRIORITY_HIGH).build());
|
||||
final JobStatus jobHighPriorityRunning = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(6).setPriority(JobInfo.PRIORITY_HIGH).build());
|
||||
final JobStatus jobHighPriorityRunningLong = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(10).setPriority(JobInfo.PRIORITY_HIGH).build());
|
||||
final JobStatus ejDowngraded = createJobStatus("testIsJobRestricted",
|
||||
createJobBuilder(7).setExpedited(true).build());
|
||||
final JobStatus ej = spy(createJobStatus("testIsJobRestricted",
|
||||
@@ -174,13 +184,17 @@ public class ThermalStatusRestrictionTest {
|
||||
when(mJobSchedulerService.isCurrentlyRunningLocked(jobLowPriorityRunning)).thenReturn(true);
|
||||
when(mJobSchedulerService.isCurrentlyRunningLocked(jobHighPriorityRunning))
|
||||
.thenReturn(true);
|
||||
when(mJobSchedulerService.isLongRunningLocked(jobLowPriorityRunningLong)).thenReturn(true);
|
||||
when(mJobSchedulerService.isLongRunningLocked(jobHighPriorityRunningLong)).thenReturn(true);
|
||||
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
|
||||
|
||||
@@ -189,9 +203,11 @@ public class ThermalStatusRestrictionTest {
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
|
||||
|
||||
@@ -200,9 +216,11 @@ public class ThermalStatusRestrictionTest {
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
|
||||
assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
|
||||
|
||||
@@ -211,9 +229,11 @@ public class ThermalStatusRestrictionTest {
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(ej));
|
||||
|
||||
@@ -222,9 +242,11 @@ public class ThermalStatusRestrictionTest {
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
|
||||
assertTrue(mThermalStatusRestriction.isJobRestricted(ej));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user