Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fbd41f6a authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

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

parents 8f24a0fb 9a4fc55c
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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;
    }
+53 −4
Original line number Diff line number Diff line
@@ -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);
            }
        }
+47 −0
Original line number Diff line number Diff line
@@ -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());
    }
}