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

Commit 8072b42b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "TimeController skips alarms for jobs that wouldn't be ready."

parents 5c35986b bffea5a1
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -220,6 +220,13 @@ message ConstantsProto {
    // will use heartbeats, false will use a rolling window.
    optional bool use_heartbeats = 23;

    message TimeController {
        // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
        // ready now.
        optional bool skip_not_ready_jobs = 1;
    }
    optional TimeController time_controller = 25;

    message QuotaController {
        // How much time each app will have to run jobs within their standby bucket window.
        optional int64 allowed_time_per_period_ms = 1;
@@ -246,6 +253,8 @@ message ConstantsProto {
        optional int64 max_execution_time_ms = 7;
    }
    optional QuotaController quota_controller = 24;

    // Next tag: 26
}

message StateControllerProto {
+25 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import android.os.SystemClock;
import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
@@ -364,6 +365,8 @@ public class JobSchedulerService extends com.android.server.SystemService
        private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
        private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
        private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
        private static final String KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
                "tc_skip_not_ready_jobs";
        private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                "qc_allowed_time_per_period_ms";
        private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
@@ -404,6 +407,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
        private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
        private static final boolean DEFAULT_USE_HEARTBEATS = true;
        private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
        private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                10 * 60 * 1000L; // 10 minutes
        private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
@@ -542,6 +546,13 @@ public class JobSchedulerService extends com.android.server.SystemService
         */
        public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;

        /**
         * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
         * ready now.
         */
        public boolean TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
                DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS;

        /** How much time each app will have to run jobs within their standby bucket window. */
        public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
                DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
@@ -663,6 +674,9 @@ public class JobSchedulerService extends com.android.server.SystemService
            CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
                    DEFAULT_CONN_PREFETCH_RELAX_FRAC);
            USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
            TIME_CONTROLLER_SKIP_NOT_READY_JOBS = mParser.getBoolean(
                    KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
                    DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
            QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
                    KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
                    DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
@@ -718,6 +732,8 @@ public class JobSchedulerService extends com.android.server.SystemService
            pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
            pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
            pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
            pw.printPair(KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
                    TIME_CONTROLLER_SKIP_NOT_READY_JOBS).println();
            pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
                    QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
            pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
@@ -763,6 +779,11 @@ public class JobSchedulerService extends com.android.server.SystemService
            proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
            proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);

            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
            proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS,
                    TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
            proto.end(tcToken);

            final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
            proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
                    QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
@@ -969,6 +990,10 @@ public class JobSchedulerService extends com.android.server.SystemService
        return mConstants;
    }

    public boolean isChainedAttributionEnabled() {
        return WorkSource.isChainedBatteryAttributionEnabled(getContext());
    }

    @Override
    public void onStartUser(int userHandle) {
        synchronized (mLock) {
+80 −16
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -68,7 +69,7 @@ public final class TimeController extends StateController {

        mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
        mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
        mChainedAttributionEnabled = WorkSource.isChainedBatteryAttributionEnabled(mContext);
        mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
    }

    /**
@@ -110,11 +111,24 @@ public final class TimeController extends StateController {
                it.next();
            }
            it.add(job);

            job.setTrackingController(JobStatus.TRACKING_TIME);
            maybeUpdateAlarmsLocked(
                    job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
                    job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE,
                    deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
            WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
            final long deadlineExpiredElapsed =
                    job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE;
            final long delayExpiredElapsed =
                    job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE;
            if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) {
                if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                    maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
                }
                if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
                    maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws);
                }
            } else {
                maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
                maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws);
            }
        }
    }

@@ -133,6 +147,34 @@ public final class TimeController extends StateController {
        }
    }

    @Override
    public void onConstantsUpdatedLocked() {
        checkExpiredDelaysAndResetAlarm();
        checkExpiredDeadlinesAndResetAlarm();
    }

    @Override
    public void evaluateStateLocked(JobStatus job) {
        if (!mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) {
            return;
        }

        if (job.hasTimingDelayConstraint()
                && job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) {
            checkExpiredDelaysAndResetAlarm();
        }
        if (job.hasDeadlineConstraint()
                && job.getLatestRunTimeElapsed() <= mNextJobExpiredElapsedMillis) {
            checkExpiredDeadlinesAndResetAlarm();
        }
    }

    @Override
    public void reevaluateStateLocked(int uid) {
        checkExpiredDelaysAndResetAlarm();
        checkExpiredDeadlinesAndResetAlarm();
    }

    /**
     * Determines whether this controller can stop tracking the given job.
     * The controller is no longer interested in a job once its time constraint is satisfied, and
@@ -156,14 +198,15 @@ public final class TimeController extends StateController {
     * Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler
     * if so, removing them from this list, and updating the alarm for the next expiry time.
     */
    private void checkExpiredDeadlinesAndResetAlarm() {
    @VisibleForTesting
    void checkExpiredDeadlinesAndResetAlarm() {
        synchronized (mLock) {
            long nextExpiryTime = Long.MAX_VALUE;
            int nextExpiryUid = 0;
            String nextExpiryPackageName = null;
            final long nowElapsedMillis = sElapsedRealtimeClock.millis();

            Iterator<JobStatus> it = mTrackedJobs.iterator();
            ListIterator<JobStatus> it = mTrackedJobs.listIterator();
            while (it.hasNext()) {
                JobStatus job = it.next();
                if (!job.hasDeadlineConstraint()) {
@@ -171,9 +214,22 @@ public final class TimeController extends StateController {
                }

                if (evaluateDeadlineConstraint(job, nowElapsedMillis)) {
                    if (job.isReady()) {
                        // If the job still isn't ready, there's no point trying to rush the
                        // Scheduler.
                        mStateChangedListener.onRunJobNow(job);
                    }
                    it.remove();
                } else {  // Sorted by expiry time, so take the next one and stop.
                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
                            && !wouldBeReadyWithConstraintLocked(
                            job, JobStatus.CONSTRAINT_DEADLINE)) {
                        if (DEBUG) {
                            Slog.i(TAG,
                                    "Skipping " + job + " because deadline won't make it ready.");
                        }
                        continue;
                    }
                    nextExpiryTime = job.getLatestRunTimeElapsed();
                    nextExpiryUid = job.getSourceUid();
                    nextExpiryPackageName = job.getSourcePackageName();
@@ -202,7 +258,8 @@ public final class TimeController extends StateController {
     * Handles alarm that notifies us that a job's delay has expired. Iterates through the list of
     * tracked jobs and marks them as ready as appropriate.
     */
    private void checkExpiredDelaysAndResetAlarm() {
    @VisibleForTesting
    void checkExpiredDelaysAndResetAlarm() {
        synchronized (mLock) {
            final long nowElapsedMillis = sElapsedRealtimeClock.millis();
            long nextDelayTime = Long.MAX_VALUE;
@@ -223,6 +280,15 @@ public final class TimeController extends StateController {
                        ready = true;
                    }
                } else if (!job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) {
                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
                            && !wouldBeReadyWithConstraintLocked(
                            job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                        if (DEBUG) {
                            Slog.i(TAG,
                                    "Skipping " + job + " because delay won't make it ready.");
                        }
                        continue;
                    }
                    // If this job still doesn't have its delay constraint satisfied,
                    // then see if it is the next upcoming delay time for the alarm.
                    final long jobDelayTime = job.getEarliestRunTime();
@@ -262,11 +328,13 @@ public final class TimeController extends StateController {
        return false;
    }

    private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed,
            WorkSource ws) {
    private void maybeUpdateDelayAlarmLocked(long delayExpiredElapsed, WorkSource ws) {
        if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
            setDelayExpiredAlarmLocked(delayExpiredElapsed, ws);
        }
    }

    private void maybeUpdateDeadlineAlarmLocked(long deadlineExpiredElapsed, WorkSource ws) {
        if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
            setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, ws);
        }
@@ -297,11 +365,7 @@ public final class TimeController extends StateController {
    }

    private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
        final long earliestWakeupTimeElapsed = sElapsedRealtimeClock.millis();
        if (proposedAlarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
            return earliestWakeupTimeElapsed;
        }
        return proposedAlarmTimeElapsedMillis;
        return Math.max(proposedAlarmTimeElapsedMillis, sElapsedRealtimeClock.millis());
    }

    private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
+2 −2
Original line number Diff line number Diff line
@@ -908,8 +908,8 @@ public class QuotaControllerTest {

        // Start in ACTIVE bucket.
        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
        inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
                any());
        inOrder.verify(mAlarmManager, never())
                .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
        inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));

        // And down from there.
+886 −0

File added.

Preview size limit exceeded, changes collapsed.