Introduce JobRestrictions for JobSchedulerService
Introducing a JobRestriction class - an abstraction for imposing additional restrictions regarding whether JobSchedulerService jobs should schedule a particlular job based on the state of the system/device. Bug: 133217178 Change-Id: I497ada3708ca1bb20d1acf4ec674cc5e0e0f201c
This commit is contained in:
@@ -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);
|
||||
pw.println();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user