Merge "Scheduling start alarm when job starts off out of quota." into qt-dev

This commit is contained in:
Kweku Adams
2019-04-27 01:39:34 +00:00
committed by Android (Google) Code Review
3 changed files with 113 additions and 4 deletions

View File

@@ -459,6 +459,7 @@ message StateControllerProto {
optional bool is_charging = 1;
optional bool is_in_parole = 2;
optional int64 elapsed_realtime = 6;
// List of UIDs currently in the foreground.
repeated int32 foreground_uids = 3;
@@ -478,6 +479,16 @@ message StateControllerProto {
}
repeated TrackedJob tracked_jobs = 4;
message AlarmListener {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Whether the listener is waiting for an alarm or not.
optional bool is_waiting = 1;
// The time at which the alarm should go off, in the elapsed realtime timebase. Only
// valid if is_waiting is true.
optional int64 trigger_time_elapsed = 2;
}
message ExecutionStats {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -567,6 +578,8 @@ message StateControllerProto {
repeated TimingSession saved_sessions = 3;
repeated ExecutionStats execution_stats = 4;
optional AlarmListener in_quota_alarm_listener = 5;
}
repeated PackageStats package_stats = 5;
}

View File

@@ -511,17 +511,28 @@ public final class QuotaController extends StateController {
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
// Still need to track jobs even if mShouldThrottle is false in case it's set to true at
// some point.
ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName);
if (jobs == null) {
jobs = new ArraySet<>();
mTrackedJobs.add(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), jobs);
mTrackedJobs.add(userId, pkgName, jobs);
}
jobs.add(jobStatus);
jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
jobStatus.setQuotaConstraintSatisfied(!mShouldThrottle || isWithinQuotaLocked(jobStatus));
if (mShouldThrottle) {
final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
jobStatus.setQuotaConstraintSatisfied(isWithinQuota);
if (!isWithinQuota) {
maybeScheduleStartAlarmLocked(userId, pkgName,
getEffectiveStandbyBucket(jobStatus));
}
} else {
// QuotaController isn't throttling, so always set to true.
jobStatus.setQuotaConstraintSatisfied(true);
}
}
@Override
@@ -1628,6 +1639,9 @@ public final class QuotaController extends StateController {
if (isActive()) {
pw.print("started at ");
pw.print(mStartTimeElapsed);
pw.print(" (");
pw.print(sElapsedRealtimeClock.millis() - mStartTimeElapsed);
pw.print("ms ago)");
} else {
pw.print("NOT active");
}
@@ -1937,6 +1951,7 @@ public final class QuotaController extends StateController {
pw.println("Is throttling: " + mShouldThrottle);
pw.println("Is charging: " + mChargeTracker.isCharging());
pw.println("In parole: " + mInParole);
pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
pw.println();
pw.print("Foreground UIDs: ");
@@ -2030,6 +2045,26 @@ public final class QuotaController extends StateController {
}
}
pw.decreaseIndent();
pw.println();
pw.println("In quota alarms:");
pw.increaseIndent();
for (int u = 0; u < mInQuotaAlarmListeners.numUsers(); ++u) {
final int userId = mInQuotaAlarmListeners.keyAt(u);
for (int p = 0; p < mInQuotaAlarmListeners.numPackagesForUser(userId); ++p) {
final String pkgName = mInQuotaAlarmListeners.keyAt(u, p);
QcAlarmListener alarmListener = mInQuotaAlarmListeners.valueAt(u, p);
pw.print(string(userId, pkgName));
pw.print(": ");
if (alarmListener.isWaiting()) {
pw.println(alarmListener.getTriggerTimeElapsed());
} else {
pw.println("NOT WAITING");
}
}
}
pw.decreaseIndent();
}
@Override
@@ -2040,6 +2075,8 @@ public final class QuotaController extends StateController {
proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging());
proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole);
proto.write(StateControllerProto.QuotaController.ELAPSED_REALTIME,
sElapsedRealtimeClock.millis());
for (int i = 0; i < mForegroundUids.size(); ++i) {
proto.write(StateControllerProto.QuotaController.FOREGROUND_UIDS,
@@ -2132,6 +2169,18 @@ public final class QuotaController extends StateController {
}
}
QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, pkgName);
if (alarmListener != null) {
final long alToken = proto.start(
StateControllerProto.QuotaController.PackageStats.IN_QUOTA_ALARM_LISTENER);
proto.write(StateControllerProto.QuotaController.AlarmListener.IS_WAITING,
alarmListener.isWaiting());
proto.write(
StateControllerProto.QuotaController.AlarmListener.TRIGGER_TIME_ELAPSED,
alarmListener.getTriggerTimeElapsed());
proto.end(alToken);
}
proto.end(psToken);
}
}

View File

@@ -2197,4 +2197,51 @@ public class QuotaControllerTest {
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
verify(handler, never()).sendMessageDelayed(any(), anyInt());
}
/**
* Tests that the start alarm is properly scheduled when a job has been throttled due to the job
* count quota.
*/
@Test
public void testStartAlarmScheduled_JobCount_AllowedTime() {
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
// because it schedules an alarm too. Prevent it from doing so.
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
final long start = JobSchedulerService.sElapsedRealtimeClock.millis();
final int standbyBucket = WORKING_INDEX;
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
// No sessions saved yet.
mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
standbyBucket);
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Ran jobs up to the job limit. All of them should be allowed to run.
for (int i = 0; i < mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) {
JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
mQuotaController.maybeStartTrackingJobLocked(job, null);
assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
mQuotaController.prepareForExecutionLocked(job);
advanceElapsedClock(SECOND_IN_MILLIS);
mQuotaController.maybeStopTrackingJobLocked(job, null, false);
advanceElapsedClock(SECOND_IN_MILLIS);
}
// Start alarm shouldn't have been scheduled since the app was in quota up until this point.
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// The app is now out of job count quota
JobStatus throttledJob = createJobStatus(
"testStartAlarmScheduled_JobCount_AllowedTime", 42);
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
final long expectedWorkingAlarmTime =
stats.jobCountExpirationTimeElapsed + mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
}