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

Commit 8e391042 authored by Kweku Adams's avatar Kweku Adams Committed by android-build-merger
Browse files

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

am: fbd41f6a

Change-Id: I82f10bb971e302126ba5f30c2ee47a1ae6758f2c
parents 9c60ad27 fbd41f6a
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());
    }
}