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

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

Merge "Make TimeController alarm skipping permanent."

parents 5f8feab3 066154a6
Loading
Loading
Loading
Loading
+9 −9
Original line number Diff line number Diff line
@@ -231,15 +231,6 @@ message ConstantsProto {
    // will use heartbeats, false will use a rolling window.
    optional bool use_heartbeats = 23;

    message TimeController {
        option (.android.msg_privacy).dest = DEST_AUTOMATIC;

        // 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 {
        option (.android.msg_privacy).dest = DEST_AUTOMATIC;

@@ -304,6 +295,15 @@ message ConstantsProto {
    }
    optional QuotaController quota_controller = 24;

    message TimeController {
        option (.android.msg_privacy).dest = DEST_AUTOMATIC;

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

    // Max number of jobs, when screen is ON.
    optional MaxJobCountsPerMemoryTrimLevelProto max_job_counts_screen_on = 26;

+12 −128
Original line number Diff line number Diff line
@@ -18,20 +18,13 @@ package com.android.server.job.controllers;

import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
@@ -39,7 +32,6 @@ import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.job.ConstantsProto;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;

@@ -63,9 +55,6 @@ public final class TimeController extends StateController {
    /** Delay alarm tag for logging purposes */
    private final String DELAY_TAG = "*job.delay*";

    private final Handler mHandler;
    private final TcConstants mTcConstants;

    private long mNextJobExpiredElapsedMillis;
    private long mNextDelayExpiredElapsedMillis;

@@ -81,14 +70,6 @@ public final class TimeController extends StateController {
        mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
        mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
        mChainedAttributionEnabled = mService.isChainedAttributionEnabled();

        mHandler = new Handler(mContext.getMainLooper());
        mTcConstants = new TcConstants(mHandler);
    }

    @Override
    public void onSystemServicesReady() {
        mTcConstants.start(mContext.getContentResolver());
    }

    /**
@@ -133,20 +114,16 @@ public final class TimeController extends StateController {

            job.setTrackingController(JobStatus.TRACKING_TIME);
            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 (mTcConstants.SKIP_NOT_READY_JOBS) {
                if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                    maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
                }
                if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
                    maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws);

            // Only update alarms if the job would be ready with the relevant timing constraint
            // satisfied.
            if (job.hasTimingDelayConstraint()
                    && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                maybeUpdateDelayAlarmLocked(job.getEarliestRunTime(), ws);
            }
            } else {
                maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
                maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws);
            if (job.hasDeadlineConstraint()
                    && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
                maybeUpdateDeadlineAlarmLocked(job.getLatestRunTimeElapsed(), ws);
            }
        }
    }
@@ -168,10 +145,6 @@ public final class TimeController extends StateController {

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

        final long nowElapsedMillis = sElapsedRealtimeClock.millis();

        // Check deadline constraint first because if it's satisfied, we avoid a little bit of
@@ -261,9 +234,7 @@ public final class TimeController extends StateController {
                    }
                    it.remove();
                } else {  // Sorted by expiry time, so take the next one and stop.
                    if (mTcConstants.SKIP_NOT_READY_JOBS
                            && !wouldBeReadyWithConstraintLocked(
                            job, JobStatus.CONSTRAINT_DEADLINE)) {
                    if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
                        if (DEBUG) {
                            Slog.i(TAG,
                                    "Skipping " + job + " because deadline won't make it ready.");
@@ -321,9 +292,7 @@ public final class TimeController extends StateController {
                        ready = true;
                    }
                } else {
                    if (mTcConstants.SKIP_NOT_READY_JOBS
                            && !wouldBeReadyWithConstraintLocked(
                            job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                    if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                        if (DEBUG) {
                            Slog.i(TAG,
                                    "Skipping " + job + " because delay won't make it ready.");
@@ -458,81 +427,6 @@ public final class TimeController extends StateController {
        checkExpiredDelaysAndResetAlarm();
    }

    @VisibleForTesting
    class TcConstants extends ContentObserver {
        private ContentResolver mResolver;
        private final KeyValueListParser mParser = new KeyValueListParser(',');

        private static final String KEY_SKIP_NOT_READY_JOBS = "skip_not_ready_jobs";

        private static final boolean DEFAULT_SKIP_NOT_READY_JOBS = true;

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

        /**
         * Creates a content observer.
         *
         * @param handler The handler to run {@link #onChange} on, or null if none.
         */
        TcConstants(Handler handler) {
            super(handler);
        }

        private void start(ContentResolver resolver) {
            mResolver = resolver;
            mResolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
            onChange(true, null);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            final String constants = Settings.Global.getString(
                    mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);

            try {
                mParser.setString(constants);
            } catch (Exception e) {
                // Failed to parse the settings string, log this and move on with defaults.
                Slog.e(TAG, "Bad jobscheduler time controller settings", e);
            }

            final boolean oldVal = SKIP_NOT_READY_JOBS;
            SKIP_NOT_READY_JOBS = mParser.getBoolean(
                    KEY_SKIP_NOT_READY_JOBS, DEFAULT_SKIP_NOT_READY_JOBS);

            if (oldVal != SKIP_NOT_READY_JOBS) {
                synchronized (mLock) {
                    recheckAlarmsLocked();
                }
            }
        }

        private void dump(IndentingPrintWriter pw) {
            pw.println();
            pw.println("TimeController:");
            pw.increaseIndent();
            pw.printPair(KEY_SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS).println();
            pw.decreaseIndent();
        }

        private void dump(ProtoOutputStream proto) {
            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
            proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS);
            proto.end(tcToken);
        }
    }

    @VisibleForTesting
    @NonNull
    TcConstants getTcConstants() {
        return mTcConstants;
    }

    @Override
    public void dumpControllerStateLocked(IndentingPrintWriter pw,
            Predicate<JobStatus> predicate) {
@@ -607,14 +501,4 @@ public final class TimeController extends StateController {
        proto.end(mToken);
        proto.end(token);
    }

    @Override
    public void dumpConstants(IndentingPrintWriter pw) {
        mTcConstants.dump(pw);
    }

    @Override
    public void dumpConstants(ProtoOutputStream proto) {
        mTcConstants.dump(proto);
    }
}
+20 −160
Original line number Diff line number Diff line
@@ -71,7 +71,6 @@ public class TimeControllerTest {
    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
    private static final int SOURCE_USER_ID = 0;

    private TimeController.TcConstants mConstants;
    private TimeController mTimeController;

    private MockitoSession mMockingSession;
@@ -111,7 +110,6 @@ public class TimeControllerTest {

        // Initialize real objects.
        mTimeController = new TimeController(mJobSchedulerService);
        mConstants = mTimeController.getTcConstants();
        spyOn(mTimeController);
    }

@@ -159,18 +157,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DelayInOrder_NoSkipping() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.onConstantsUpdatedLocked();

        runTestMaybeStartTrackingJobLocked_DelayInOrder();
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_AllReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();

    public void testMaybeStartTrackingJobLocked_DelayInOrder_AllReady() {
        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());

        runTestMaybeStartTrackingJobLocked_DelayInOrder();
@@ -201,9 +188,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_SomeNotReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();
    public void testMaybeStartTrackingJobLocked_DelayInOrder_SomeNotReady() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder",
@@ -235,18 +220,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_NoSkipping() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.onConstantsUpdatedLocked();

        runTestMaybeStartTrackingJobLocked_DelayReverseOrder();
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_AllReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();

    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_AllReady() {
        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());

        runTestMaybeStartTrackingJobLocked_DelayReverseOrder();
@@ -279,9 +253,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_SomeNotReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();
    public void testMaybeStartTrackingJobLocked_DelayReverseOrder_SomeNotReady() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder",
@@ -315,18 +287,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_NoSkipping() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.onConstantsUpdatedLocked();

        runTestMaybeStartTrackingJobLocked_DeadlineInOrder();
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_AllReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();

    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_AllReady() {
        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());

        runTestMaybeStartTrackingJobLocked_DeadlineInOrder();
@@ -357,9 +318,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_SomeNotReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();
    public void testMaybeStartTrackingJobLocked_DeadlineInOrder_SomeNotReady() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder",
@@ -391,18 +350,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_NoSkipping() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.onConstantsUpdatedLocked();

        runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder();
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_AllReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();

    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_AllReady() {
        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());

        runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder();
@@ -438,9 +386,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_SomeNotReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.onConstantsUpdatedLocked();
    public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_SomeNotReady() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus(
@@ -478,62 +424,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testJobSkipToggling() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus(
                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
        JobStatus jobEarliest = createJobStatus(
                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));

        doReturn(true).when(mTimeController)
                .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt());
        doReturn(false).when(mTimeController)
                .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());

        // Starting off with the skipping off, we should still set an alarm for the earlier job.
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.recheckAlarmsLocked();
        InOrder inOrder = inOrder(mAlarmManager);

        mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
        mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
        inOrder.verify(mAlarmManager, times(1))
                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
                        eq(TAG_DEADLINE), any(), any(), any());

        // Turn it on, use alarm for later job.
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();

        inOrder.verify(mAlarmManager, times(1))
                .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
                        any(), any(), any());

        // Back off, use alarm for earlier job.
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.recheckAlarmsLocked();

        inOrder.verify(mAlarmManager, times(1))
                .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
                        eq(TAG_DEADLINE), any(), any(), any());
    }

    @Test
    public void testCheckExpiredDelaysAndResetAlarm_NoSkipping() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.recheckAlarmsLocked();

        runTestCheckExpiredDelaysAndResetAlarm();
    }

    @Test
    public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_AllReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();

    public void testCheckExpiredDelaysAndResetAlarm_AllReady() {
        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());

        runTestCheckExpiredDelaysAndResetAlarm();
@@ -589,9 +480,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_SomeNotReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();
    public void testCheckExpiredDelaysAndResetAlarm_SomeNotReady() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
@@ -639,18 +528,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testCheckExpiredDeadlinesAndResetAlarm_NoSkipping() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.recheckAlarmsLocked();

        runTestCheckExpiredDeadlinesAndResetAlarm();
    }

    @Test
    public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_AllReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();

    public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() {
        doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());

        runTestCheckExpiredDeadlinesAndResetAlarm();
@@ -706,9 +584,7 @@ public class TimeControllerTest {
    }

    @Test
    public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_SomeNotReady() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();
    public void testCheckExpiredDeadlinesAndResetAlarm_SomeNotReady() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
@@ -756,28 +632,14 @@ public class TimeControllerTest {
    }

    @Test
    public void testEvaluateStateLocked_SkippingOff() {
        mConstants.SKIP_NOT_READY_JOBS = false;
        mTimeController.recheckAlarmsLocked();
        JobStatus job = createJobStatus("testEvaluateStateLocked_SkippingOff",
                createJob().setOverrideDeadline(HOUR_IN_MILLIS));

        mTimeController.evaluateStateLocked(job);
        verify(mAlarmManager, never())
                .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
    }

    @Test
    public void testEvaluateStateLocked_SkippingOn_Delay() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();
    public void testEvaluateStateLocked_Delay() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
        JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_Delay",
                createJob().setMinimumLatency(HOUR_IN_MILLIS));
        JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
        JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_Delay",
                createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS));
        JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
        JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_Delay",
                createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS));

        doReturn(false).when(mTimeController)
@@ -827,16 +689,14 @@ public class TimeControllerTest {
    }

    @Test
    public void testEvaluateStateLocked_SkippingOn_Deadline() {
        mConstants.SKIP_NOT_READY_JOBS = true;
        mTimeController.recheckAlarmsLocked();
    public void testEvaluateStateLocked_Deadline() {
        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();

        JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
        JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_Deadline",
                createJob().setOverrideDeadline(HOUR_IN_MILLIS));
        JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
        JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_Deadline",
                createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS));
        JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
        JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_Deadline",
                createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS));

        doReturn(false).when(mTimeController)