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

Commit c88fcb41 authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov Committed by Android (Google) Code Review
Browse files

Merge "Introduce JobRestrictions for JobSchedulerService"

parents 4e13461e 2ad8af14
Loading
Loading
Loading
Loading
+85 −49
Original line number Diff line number Diff line
@@ -56,8 +56,6 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -66,7 +64,6 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.WorkSource;
@@ -81,7 +78,6 @@ import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
@@ -105,6 +101,8 @@ import com.android.server.job.controllers.QuotaController;
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
import com.android.server.job.restrictions.JobRestriction;
import com.android.server.job.restrictions.ThermalStatusRestriction;

import libcore.util.EmptyArray;

@@ -186,12 +184,12 @@ public class JobSchedulerService extends com.android.server.SystemService
    private final DeviceIdleJobsController mDeviceIdleJobsController;
    /** Needed to get remaining quota time. */
    private final QuotaController mQuotaController;

    /** Need directly for receiving thermal events */
    private IThermalService mThermalService;
    /** Thermal constraint. */
    @GuardedBy("mLock")
    private boolean mThermalConstraint = false;
    /**
     * List of restrictions.
     * Note: do not add to or remove from this list at runtime except in the constructor, because we
     * do not synchronize access to this list.
     */
    private final List<JobRestriction> mJobRestrictions;

    /**
     * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -285,19 +283,6 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
    }

    /**
     *  Thermal event received from Thermal Service
     */
    private final class ThermalStatusListener extends IThermalStatusListener.Stub {
        @Override public void onStatusChange(int status) {
            // Throttle for Temperature.THROTTLING_SEVERE and above
            synchronized (mLock) {
                mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
            }
            onControllerStateChanged();
        }
    }

    static class MaxJobCounts {
        private final KeyValueListParser.IntValue mTotal;
        private final KeyValueListParser.IntValue mMaxBg;
@@ -1292,6 +1277,10 @@ public class JobSchedulerService extends com.android.server.SystemService
        mQuotaController = new QuotaController(this);
        mControllers.add(mQuotaController);

        // Create restrictions
        mJobRestrictions = new ArrayList<>();
        mJobRestrictions.add(new ThermalStatusRestriction(this));

        // If the job store determined that it can't yet reschedule persisted jobs,
        // we need to start watching the clock.
        if (!mJobs.jobTimesInflatedValid()) {
@@ -1383,15 +1372,9 @@ public class JobSchedulerService extends com.android.server.SystemService

            // Remove any jobs that are not associated with any of the current users.
            cancelJobsForNonExistentUsers();
            // Register thermal callback
            mThermalService = IThermalService.Stub.asInterface(
                    ServiceManager.getService(Context.THERMAL_SERVICE));
            if (mThermalService != null) {
                try {
                    mThermalService.registerThermalStatusListener(new ThermalStatusListener());
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to register thermal callback.", e);
                }

            for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
                mJobRestrictions.get(i).onSystemServicesReady();
            }
        } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
            synchronized (mLock) {
@@ -1833,9 +1816,28 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
    }

    private boolean isJobThermalConstrainedLocked(JobStatus job) {
        return mThermalConstraint && job.hasConnectivityConstraint()
                && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
    /**
     * Check if a job is restricted by any of the declared {@link JobRestriction}s.
     * Note, that the jobs with {@link JobInfo#PRIORITY_FOREGROUND_APP} priority or higher may not
     * be restricted, thus we won't even perform the check, but simply return null early.
     *
     * @param job to be checked
     * @return the first {@link JobRestriction} restricting the given job that has been found; null
     * - if passes all the restrictions or has priority {@link JobInfo#PRIORITY_FOREGROUND_APP}
     * or higher.
     */
    private JobRestriction checkIfRestricted(JobStatus job) {
        if (evaluateJobPriorityLocked(job) >= JobInfo.PRIORITY_FOREGROUND_APP) {
            // Jobs with PRIORITY_FOREGROUND_APP or higher should not be restricted
            return null;
        }
        for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
            final JobRestriction restriction = mJobRestrictions.get(i);
            if (restriction.isJobRestricted(job)) {
                return restriction;
            }
        }
        return null;
    }

    private void stopNonReadyActiveJobsLocked() {
@@ -1849,10 +1851,13 @@ public class JobSchedulerService extends com.android.server.SystemService
                serviceContext.cancelExecutingJobLocked(
                        JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
                        "cancelled due to unsatisfied constraints");
            } else if (isJobThermalConstrainedLocked(running)) {
                serviceContext.cancelExecutingJobLocked(
                        JobParameters.REASON_DEVICE_THERMAL,
                        "cancelled due to thermal condition");
            } else {
                final JobRestriction restriction = checkIfRestricted(running);
                if (restriction != null) {
                    final int reason = restriction.getReason();
                    serviceContext.cancelExecutingJobLocked(reason,
                            "restricted due to " + JobParameters.getReasonName(reason));
                }
            }
        }
    }
@@ -2089,7 +2094,7 @@ public class JobSchedulerService extends com.android.server.SystemService
            return false;
        }

        if (isJobThermalConstrainedLocked(job)) {
        if (checkIfRestricted(job) != null) {
            return false;
        }

@@ -2170,7 +2175,7 @@ public class JobSchedulerService extends com.android.server.SystemService
            return false;
        }

        if (isJobThermalConstrainedLocked(job)) {
        if (checkIfRestricted(job) != null) {
            return false;
        }

@@ -2982,9 +2987,12 @@ public class JobSchedulerService extends com.android.server.SystemService
            pw.print("    In parole?: ");
            pw.print(mInParole);
            pw.println();
            pw.print("    In thermal throttling?: ");
            pw.print(mThermalConstraint);

            for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
                pw.print("    ");
                mJobRestrictions.get(i).dumpConstants(pw);
                pw.println();
            }
            pw.println();

            pw.println("Started users: " + Arrays.toString(mStartedUsers));
@@ -3005,14 +3013,30 @@ public class JobSchedulerService extends com.android.server.SystemService

                    job.dump(pw, "    ", true, nowElapsed);


                    pw.print("    Restricted due to:");
                    final boolean isRestricted = checkIfRestricted(job) != null;
                    if (isRestricted) {
                        for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
                            final JobRestriction restriction = mJobRestrictions.get(i);
                            if (restriction.isJobRestricted(job)) {
                                final int reason = restriction.getReason();
                                pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]");
                            }
                        }
                    } else {
                        pw.print(" none");
                    }
                    pw.println(".");

                    pw.print("    Ready: ");
                    pw.print(isReadyToBeExecutedLocked(job));
                    pw.print(" (job=");
                    pw.print(job.isReady());
                    pw.print(" user=");
                    pw.print(areUsersStartedLocked(job));
                    pw.print(" !thermal=");
                    pw.print(!isJobThermalConstrainedLocked(job));
                    pw.print(" !restricted=");
                    pw.print(!isRestricted);
                    pw.print(" !pending=");
                    pw.print(!mPendingJobs.contains(job));
                    pw.print(" !active=");
@@ -3152,7 +3176,9 @@ public class JobSchedulerService extends com.android.server.SystemService
            proto.end(settingsToken);

            proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
            proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
            for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
                mJobRestrictions.get(i).dumpConstants(proto);
            }

            for (int u : mStartedUsers) {
                proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
@@ -3179,8 +3205,8 @@ public class JobSchedulerService extends com.android.server.SystemService
                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED,
                            areUsersStartedLocked(job));
                    proto.write(
                            JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_THERMAL_CONSTRAINED,
                            isJobThermalConstrainedLocked(job));
                            JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
                            checkIfRestricted(job) != null);
                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
                            mPendingJobs.contains(job));
                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
@@ -3190,6 +3216,16 @@ public class JobSchedulerService extends com.android.server.SystemService
                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
                            isComponentUsable(job));

                    for (JobRestriction restriction : mJobRestrictions) {
                        final long restrictionsToken = proto.start(
                                JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
                        proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
                                restriction.getReason());
                        proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
                                restriction.isJobRestricted(job));
                        proto.end(restrictionsToken);
                    }

                    proto.end(rjToken);
                }
            }
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.job.restrictions;

import android.app.job.JobInfo;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.IndentingPrintWriter;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.controllers.JobStatus;

/**
 * Used by {@link JobSchedulerService} to impose additional restrictions regarding whether jobs
 * should be scheduled or not based on the state of the system/device.
 * Every restriction is associated with exactly one reason (from {@link
 * android.app.job.JobParameters#JOB_STOP_REASON_CODES}), which could be retrieved using {@link
 * #getReason()}.
 * Note, that this is not taken into account for the jobs that have priority
 * {@link JobInfo#PRIORITY_FOREGROUND_APP} or higher.
 */
public abstract class JobRestriction {

    final JobSchedulerService mService;
    private final int mReason;

    JobRestriction(JobSchedulerService service, int reason) {
        mService = service;
        mReason = reason;
    }

    /**
     * Called when the system boot phase has reached
     * {@link com.android.server.SystemService#PHASE_SYSTEM_SERVICES_READY}.
     */
    public void onSystemServicesReady() {
    }

    /**
     * Called by {@link JobSchedulerService} to check if it may proceed with scheduling the job (in
     * case all constraints are satisfied and all other {@link JobRestriction}s are fine with it)
     *
     * @param job to be checked
     * @return false if the {@link JobSchedulerService} should not schedule this job at the moment,
     * true - otherwise
     */
    public abstract boolean isJobRestricted(JobStatus job);

    /** Dump any internal constants the Restriction may have. */
    public abstract void dumpConstants(IndentingPrintWriter pw);

    /** Dump any internal constants the Restriction may have. */
    public abstract void dumpConstants(ProtoOutputStream proto);

    /** @return reason code for the Restriction. */
    public final int getReason() {
        return mReason;
    }
}
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.job.restrictions;

import android.app.job.JobParameters;
import android.content.Context;
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Temperature;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.IndentingPrintWriter;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerServiceDumpProto;
import com.android.server.job.controllers.JobStatus;

public class ThermalStatusRestriction extends JobRestriction {
    private static final String TAG = "ThermalStatusRestriction";

    private volatile boolean mIsThermalRestricted = false;

    public ThermalStatusRestriction(JobSchedulerService service) {
        super(service, JobParameters.REASON_DEVICE_THERMAL);
    }

    @Override
    public void onSystemServicesReady() {
        final IThermalService thermalService = IThermalService.Stub.asInterface(
                ServiceManager.getService(Context.THERMAL_SERVICE));
        if (thermalService != null) {
            try {
                thermalService.registerThermalStatusListener(new IThermalStatusListener.Stub() {
                    @Override
                    public void onStatusChange(int status) {
                        final boolean shouldBeActive = status >= Temperature.THROTTLING_SEVERE;
                        if (mIsThermalRestricted == shouldBeActive) {
                            return;
                        }
                        mIsThermalRestricted = shouldBeActive;
                        mService.onControllerStateChanged();
                    }
                });
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to register thermal callback.", e);
            }
        }
    }

    @Override
    public boolean isJobRestricted(JobStatus job) {
        return mIsThermalRestricted && job.hasConnectivityConstraint();
    }

    @Override
    public void dumpConstants(IndentingPrintWriter pw) {
        pw.print("In thermal throttling?: ");
        pw.print(mIsThermalRestricted);
    }

    @Override
    public void dumpConstants(ProtoOutputStream proto) {
        proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mIsThermalRestricted);
    }
}
+12 −3
Original line number Diff line number Diff line
@@ -48,6 +48,13 @@ message JobSchedulerServiceDumpProto {

    repeated int32 started_users = 2;

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

        optional .android.app.job.StopReasonEnum reason = 1;
        optional bool is_restricting = 2;
    }

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

@@ -56,20 +63,22 @@ message JobSchedulerServiceDumpProto {

        optional bool is_job_ready_to_be_executed = 10;
        // A job is ready to be executed if:
        // is_job_ready && are_users_started && !is_job_thermal_constrained && !is_job_pending &&
        // is_job_ready && are_users_started && !is_job_restricted && !is_job_pending &&
        // !is_job_currently_active && !is_uid_backing_up &&
        // is_component_usable.
        optional bool is_job_ready = 3;
        optional bool are_users_started = 4;
        optional bool is_job_thermal_constrained = 11;
        optional bool is_job_restricted = 11;
        optional bool is_job_pending = 5;
        optional bool is_job_currently_active = 6;
        optional bool is_uid_backing_up = 7;
        optional bool is_component_usable = 8;

        repeated JobRestriction restrictions = 12;

        reserved 9; // last_run_heartbeat

        // Next tag: 12
        // Next tag: 13
    }
    repeated RegisteredJob registered_jobs = 3;