Merge "First version of HPJs."

This commit is contained in:
Kweku Adams
2020-12-03 17:57:15 +00:00
committed by Android (Google) Code Review
12 changed files with 2860 additions and 202 deletions

View File

@@ -274,12 +274,19 @@ public class JobInfo implements Parcelable {
/**
* This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
* can set it. Jobs with a time constrant must not have it.
* can set it. Jobs with a time constraint must not have it.
*
* @hide
*/
public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3;
/**
* Whether it's a so-called "HPJ" or not.
*
* @hide
*/
public static final int FLAG_FOREGROUND_JOB = 1 << 4;
/**
* @hide
*/
@@ -571,12 +578,20 @@ public class JobInfo implements Parcelable {
/**
* Return the backoff policy of this job.
*
* @see JobInfo.Builder#setBackoffCriteria(long, int)
*/
public @BackoffPolicy int getBackoffPolicy() {
return backoffPolicy;
}
/**
* @see JobInfo.Builder#setForeground(boolean)
*/
public boolean isForegroundJob() {
return (flags & FLAG_FOREGROUND_JOB) != 0;
}
/**
* @see JobInfo.Builder#setImportantWhileForeground(boolean)
*/
@@ -1441,6 +1456,41 @@ public class JobInfo implements Parcelable {
return this;
}
/**
* Setting this to true indicates that this job is important and needs to run as soon as
* possible with stronger guarantees than regular jobs. These "foreground" jobs will:
* <ol>
* <li>Run as soon as possible</li>
* <li>Be exempted from Doze and battery saver restrictions</li>
* <li>Have network access</li>
* </ol>
*
* Since these jobs have stronger guarantees than regular jobs, they will be subject to
* stricter quotas. As long as an app has available foreground quota, jobs scheduled with
* this set to true will run with these guarantees. If an app has run out of available
* foreground quota, any pending foreground jobs will run as regular jobs.
* {@link JobParameters#isForegroundJob()} can be used to know whether the executing job
* has foreground guarantees or not. In addition, {@link JobScheduler#schedule(JobInfo)}
* will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have
* available quota (and the job will not be successfully scheduled).
*
* Foreground jobs may only set network constraints. No other constraints are allowed.
*
* Note: Even though foreground jobs are meant to run as soon as possible, they may be
* deferred if the system is under heavy load or the network constraint is satisfied
*
* @see JobInfo#isForegroundJob()
*/
@NonNull
public Builder setForeground(boolean foreground) {
if (foreground) {
mFlags |= FLAG_FOREGROUND_JOB;
} else {
mFlags &= (~FLAG_FOREGROUND_JOB);
}
return this;
}
/**
* Setting this to true indicates that this job is important while the scheduling app
* is in the foreground or on the temporary whitelist for background restrictions.
@@ -1456,7 +1506,9 @@ public class JobInfo implements Parcelable {
* @param importantWhileForeground whether to relax doze restrictions for this job when the
* app is in the foreground. False by default.
* @see JobInfo#isImportantWhileForeground()
* @deprecated Use {@link #setForeground(boolean)} instead.
*/
@Deprecated
public Builder setImportantWhileForeground(boolean importantWhileForeground) {
if (importantWhileForeground) {
mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
@@ -1580,6 +1632,29 @@ public class JobInfo implements Parcelable {
throw new IllegalArgumentException(
"An important while foreground job cannot have a time delay");
}
if ((flags & FLAG_FOREGROUND_JOB) != 0) {
if (hasEarlyConstraint) {
throw new IllegalArgumentException("A foreground job cannot have a time delay");
}
if (hasLateConstraint) {
throw new IllegalArgumentException("A foreground job cannot have a deadline");
}
if (isPeriodic) {
throw new IllegalArgumentException("A foreground job cannot be periodic");
}
if (isPersisted) {
throw new IllegalArgumentException("A foreground job cannot be persisted");
}
if (constraintFlags != 0 || (flags & ~FLAG_FOREGROUND_JOB) != 0) {
throw new IllegalArgumentException(
"A foreground job can only have network constraints");
}
if (triggerContentUris != null && triggerContentUris.length > 0) {
throw new IllegalArgumentException(
"Can't call addTriggerContentUri() on a foreground job");
}
}
}
/**

View File

@@ -111,6 +111,9 @@ public class JobParameters implements Parcelable {
@UnsupportedAppUsage
private final IBinder callback;
private final boolean overrideDeadlineExpired;
// HPJs = foreground jobs.
// TODO(171305774): clean up naming
private final boolean mIsHpj;
private final Uri[] mTriggeredContentUris;
private final String[] mTriggeredContentAuthorities;
private final Network network;
@@ -121,7 +124,7 @@ public class JobParameters implements Parcelable {
/** @hide */
public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
Bundle transientExtras, ClipData clipData, int clipGrantFlags,
boolean overrideDeadlineExpired, Uri[] triggeredContentUris,
boolean overrideDeadlineExpired, boolean isHpj, Uri[] triggeredContentUris,
String[] triggeredContentAuthorities, Network network) {
this.jobId = jobId;
this.extras = extras;
@@ -130,6 +133,7 @@ public class JobParameters implements Parcelable {
this.clipGrantFlags = clipGrantFlags;
this.callback = callback;
this.overrideDeadlineExpired = overrideDeadlineExpired;
this.mIsHpj = isHpj;
this.mTriggeredContentUris = triggeredContentUris;
this.mTriggeredContentAuthorities = triggeredContentAuthorities;
this.network = network;
@@ -194,6 +198,17 @@ public class JobParameters implements Parcelable {
return clipGrantFlags;
}
/**
* @return Whether this job is running as a foreground job or not. A job is guaranteed to have
* all foreground job guarantees for the duration of the job execution if this returns
* {@code true}. This will return {@code false} if the job that wasn't requested to run as a
* foreground job, or if it was requested to run as a foreground job but the app didn't have
* any remaining foreground job quota at the time of execution.
*/
public boolean isForegroundJob() {
return mIsHpj;
}
/**
* For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
* provides an easy way to tell whether the job is being executed due to the deadline
@@ -337,6 +352,7 @@ public class JobParameters implements Parcelable {
}
callback = in.readStrongBinder();
overrideDeadlineExpired = in.readInt() == 1;
mIsHpj = in.readBoolean();
mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
mTriggeredContentAuthorities = in.createStringArray();
if (in.readInt() != 0) {
@@ -373,6 +389,7 @@ public class JobParameters implements Parcelable {
}
dest.writeStrongBinder(callback);
dest.writeInt(overrideDeadlineExpired ? 1 : 0);
dest.writeBoolean(mIsHpj);
dest.writeTypedArray(mTriggeredContentUris, flags);
dest.writeStringArray(mTriggeredContentAuthorities);
if (network != null) {

View File

@@ -205,7 +205,9 @@ class JobConcurrencyManager {
}
private boolean isFgJob(JobStatus job) {
return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP;
// (It's super confusing PRIORITY_BOUND_FOREGROUND_SERVICE isn't FG here)
return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
|| job.shouldTreatAsForegroundJob();
}
@GuardedBy("mLock")
@@ -336,6 +338,8 @@ class JobConcurrencyManager {
continue;
}
// TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them
final boolean isPendingFg = isFgJob(nextPending);
// Find an available slot for nextPending. The context should be available OR

View File

@@ -834,6 +834,13 @@ public class JobSchedulerService extends com.android.server.SystemService
// Higher override state (OVERRIDE_FULL) should be before lower state (OVERRIDE_SOFT)
return o2.overrideState - o1.overrideState;
}
if (o1.getSourceUid() == o2.getSourceUid()) {
final boolean o1FGJ = o1.isRequestedForegroundJob();
if (o1FGJ != o2.isRequestedForegroundJob()) {
// Attempt to run requested HPJs ahead of regular jobs, regardless of HPJ quota.
return o1FGJ ? -1 : 1;
}
}
if (o1.enqueueTime < o2.enqueueTime) {
return -1;
}
@@ -1136,6 +1143,12 @@ public class JobSchedulerService extends com.android.server.SystemService
JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
// Return failure early if HPJ quota used up.
if (jobStatus.isRequestedForegroundJob()
&& !mQuotaController.isWithinHpjQuotaLocked(jobStatus)) {
return JobScheduler.RESULT_FAILURE;
}
// Give exemption if the source is in the foreground just now.
// Note if it's a sync job, this method is called on the handler so it's not exactly
// the state when requestSync() was called, but that should be fine because of the
@@ -1791,9 +1804,9 @@ public class JobSchedulerService extends com.android.server.SystemService
* time of the job to be the time of completion (i.e. the time at which this function is
* called).
* <p>This could be inaccurate b/c the job can run for as long as
* {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
* to underscheduling at least, rather than if we had taken the last execution time to be the
* start of the execution.
* {@link com.android.server.job.JobServiceContext#DEFAULT_EXECUTING_TIMESLICE_MILLIS}, but
* will lead to underscheduling at least, rather than if we had taken the last execution time
* to be the start of the execution.
*
* @return A new job representing the execution criteria for this instantiation of the
* recurring job.
@@ -1884,6 +1897,10 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
// Intentionally not checking HPJ quota here. An app can't find out if it's run out of quota
// when it asks JS to reschedule an HPJ. Instead, the rescheduled HPJ will just be demoted
// to a regular job if the app has no HPJ quota left.
// If the job wants to be rescheduled, we first need to make the next upcoming
// job so we can transfer any appropriate state over from the previous job when
// we stop it.
@@ -2382,7 +2399,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public long getMaxJobExecutionTimeMs(JobStatus job) {
synchronized (mLock) {
return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job),
JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS);
}
}
@@ -2600,7 +2617,6 @@ public class JobSchedulerService extends com.android.server.SystemService
// job that runs one of the app's services, as well as verifying that the
// named service properly requires the BIND_JOB_SERVICE permission
private void enforceValidJobRequest(int uid, JobInfo job) {
job.enforceValidity();
final PackageManager pm = getContext()
.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
.getPackageManager();
@@ -2649,6 +2665,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
private void validateJobFlags(JobInfo job, int callingUid) {
job.enforceValidity();
if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);

View File

@@ -16,6 +16,7 @@
package com.android.server.job;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.job.IJobCallback;
@@ -73,7 +74,13 @@ public final class JobServiceContext implements ServiceConnection {
private static final String TAG = "JobServiceContext";
/** Amount of time a job is allowed to execute for before being considered timed-out. */
public static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
public static final long DEFAULT_EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
/**
* Amount of time a RESTRICTED HPJ is allowed to execute for before being considered
* timed-out.
*/
public static final long DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS =
DEFAULT_EXECUTING_TIMESLICE_MILLIS / 2;
/** Amount of time the JobScheduler waits for the initial service launch+bind. */
private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
/** Amount of time the JobScheduler will wait for a response from an app for a message. */
@@ -224,7 +231,8 @@ public final class JobServiceContext implements ServiceConnection {
final JobInfo ji = job.getJob();
mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
isDeadlineExpired, job.shouldTreatAsForegroundJob(),
triggeredUris, triggeredAuthorities, job.network);
mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
final long whenDeferred = job.getWhenStandbyDeferred();
@@ -250,9 +258,18 @@ public final class JobServiceContext implements ServiceConnection {
final Intent intent = new Intent().setComponent(job.getServiceComponent());
boolean binding = false;
try {
binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_NOT_PERCEPTIBLE,
final int bindFlags;
if (job.shouldTreatAsForegroundJob()) {
// Add BIND_FOREGROUND_SERVICE to make it BFGS. Without it, it'll be
// PROCESS_STATE_IMPORTANT_FOREGROUND. Unclear which is better here.
// TODO(171305774): The job should run on the little cores. We'll probably need
// another binding flag for that.
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
} else {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_NOT_PERCEPTIBLE;
}
binding = mContext.bindServiceAsUser(intent, this, bindFlags,
UserHandle.of(job.getUserId()));
} catch (SecurityException e) {
// Some permission policy, for example INTERACT_ACROSS_USERS and
@@ -848,7 +865,10 @@ public final class JobServiceContext implements ServiceConnection {
final long timeoutMillis;
switch (mVerb) {
case VERB_EXECUTING:
timeoutMillis = EXECUTING_TIMESLICE_MILLIS;
timeoutMillis = mRunningJob.shouldTreatAsForegroundJob()
&& mRunningJob.getStandbyBucket() == RESTRICTED_INDEX
? DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS
: DEFAULT_EXECUTING_TIMESLICE_MILLIS;
break;
case VERB_BINDING:

View File

@@ -85,6 +85,7 @@ public final class JobStatus {
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
static final int CONSTRAINT_WITHIN_HPJ_QUOTA = 1 << 23; // Implicit constraint
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
/**
@@ -376,6 +377,9 @@ public final class JobStatus {
/** The job is within its quota based on its standby bucket. */
private boolean mReadyWithinQuota;
/** The job is a foreground job with sufficient quota to run as a foreground job. */
private boolean mReadyWithinHpjQuota;
/** The job's dynamic requirements have been satisfied. */
private boolean mReadyDynamicSatisfied;
@@ -1036,20 +1040,34 @@ public final class JobStatus {
mPersistedUtcTimes = null;
}
/** @return true if the app has requested that this run as a foreground job. */
public boolean isRequestedForegroundJob() {
return (getFlags() & JobInfo.FLAG_FOREGROUND_JOB) != 0;
}
/**
* @return true if all foreground job requirements are satisfied and therefore this should be
* treated as a foreground job.
*/
public boolean shouldTreatAsForegroundJob() {
return mReadyWithinHpjQuota && isRequestedForegroundJob();
}
/**
* @return true if the job is exempted from Doze restrictions and therefore allowed to run
* in Doze.
*/
public boolean canRunInDoze() {
return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsForegroundJob();
}
boolean canRunInBatterySaver() {
return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0;
return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
|| shouldTreatAsForegroundJob();
}
boolean shouldIgnoreNetworkBlocking() {
return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsForegroundJob();
}
/** @return true if the constraint was changed, false otherwise. */
@@ -1128,6 +1146,16 @@ public final class JobStatus {
return false;
}
/** @return true if the constraint was changed, false otherwise. */
boolean setForegroundJobQuotaConstraintSatisfied(boolean state) {
if (setConstraintSatisfied(CONSTRAINT_WITHIN_HPJ_QUOTA, state)) {
// The constraint was changed. Update the ready flag.
mReadyWithinHpjQuota = state;
return true;
}
return false;
}
/** @return true if the state was changed, false otherwise. */
boolean setUidActive(final boolean newActiveState) {
if (newActiveState != uidActive) {
@@ -1257,6 +1285,10 @@ public final class JobStatus {
oldValue = mReadyWithinQuota;
mReadyWithinQuota = true;
break;
case CONSTRAINT_WITHIN_HPJ_QUOTA:
oldValue = mReadyWithinHpjQuota;
mReadyWithinHpjQuota = true;
break;
default:
satisfied |= constraint;
mReadyDynamicSatisfied = mDynamicConstraints != 0
@@ -1279,6 +1311,9 @@ public final class JobStatus {
case CONSTRAINT_WITHIN_QUOTA:
mReadyWithinQuota = oldValue;
break;
case CONSTRAINT_WITHIN_HPJ_QUOTA:
mReadyWithinHpjQuota = oldValue;
break;
default:
mReadyDynamicSatisfied = mDynamicConstraints != 0
&& mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
@@ -1293,7 +1328,7 @@ public final class JobStatus {
// sessions (exempt from dynamic restrictions), we need the additional check to ensure
// that NEVER jobs don't run.
// TODO: cleanup quota and standby bucket management so we don't need the additional checks
if ((!mReadyWithinQuota && !mReadyDynamicSatisfied)
if ((!mReadyWithinQuota && !mReadyDynamicSatisfied && !shouldTreatAsForegroundJob())
|| getEffectiveStandbyBucket() == NEVER_INDEX) {
return false;
}
@@ -1506,6 +1541,9 @@ public final class JobStatus {
if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
pw.print(" WITHIN_QUOTA");
}
if ((constraints & CONSTRAINT_WITHIN_HPJ_QUOTA) != 0) {
pw.print(" WITHIN_HPJ_QUOTA");
}
if (constraints != 0) {
pw.print(" [0x");
pw.print(Integer.toHexString(constraints));
@@ -1578,6 +1616,9 @@ public final class JobStatus {
if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
}
if ((constraints & CONSTRAINT_WITHIN_HPJ_QUOTA) != 0) {
proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_HPJ_QUOTA);
}
}
private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
@@ -1795,6 +1836,11 @@ public final class JobStatus {
pw.print(prefix);
pw.print(" readyComponentEnabled: ");
pw.println(serviceInfo != null);
if ((getFlags() & JobInfo.FLAG_FOREGROUND_JOB) != 0) {
pw.print(prefix);
pw.print(" mReadyWithinHpjQuota: ");
pw.println(mReadyWithinHpjQuota);
}
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");

View File

@@ -7686,6 +7686,7 @@ package android.app.job {
method public long getTriggerContentMaxDelay();
method public long getTriggerContentUpdateDelay();
method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
method public boolean isForegroundJob();
method public boolean isImportantWhileForeground();
method public boolean isPeriodic();
method public boolean isPersisted();
@@ -7717,7 +7718,8 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setClipData(@Nullable android.content.ClipData, int);
method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
method @NonNull public android.app.job.JobInfo.Builder setForeground(boolean);
method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
@@ -7757,6 +7759,7 @@ package android.app.job {
method @NonNull public android.os.Bundle getTransientExtras();
method @Nullable public String[] getTriggeredContentAuthorities();
method @Nullable public android.net.Uri[] getTriggeredContentUris();
method public boolean isForegroundJob();
method public boolean isOverrideDeadlineExpired();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR;

View File

@@ -383,6 +383,10 @@ public final class UsageEvents implements Parcelable {
public int mClassToken = UNASSIGNED_TOKEN;
/**
* Uniquely identifies an activity. It's possible for two activities with the same
* pkg/class name to be in lifecycle at the same time. The mInstanceId is guaranteed to be
* unique per activity across all apps (not just within a single app).
*
* {@hide}
*/
public int mInstanceId;

View File

@@ -313,7 +313,41 @@ message ConstantsProto {
// The minimum amount of time between quota check alarms.
optional int64 min_quota_check_delay_ms = 23;
// Next tag: 24
// The total session limit of the particular standby bucket. Apps in this standby bucket can
// only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
// HPJs).
optional int64 hpj_limit_active_ms = 24;
// The total session limit of the particular standby bucket. Apps in this standby bucket can
// only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
// HPJs).
optional int64 hpj_limit_working_ms = 25;
// The total session limit of the particular standby bucket. Apps in this standby bucket can
// only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
// HPJs).
optional int64 hpj_limit_frequent_ms = 26;
// The total session limit of the particular standby bucket. Apps in this standby bucket can
// only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
// HPJs).
optional int64 hpj_limit_rare_ms = 27;
// The total session limit of the particular standby bucket. Apps in this standby bucket can
// only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
// HPJs).
optional int64 hpj_limit_restricted_ms = 28;
// The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions
// totalling HPJ_LIMIT_<bucket>_MS within this period of time (without factoring in any
// rewards or free HPJs).
optional int64 hpj_window_size_ms = 29;
// Length of time used to split an app's top time into chunks.
optional int64 hpj_top_app_time_chunk_size_ms = 30;
// How much HPJ quota to give back to an app based on the number of top app time chunks
// it had.
optional int64 hpj_reward_top_app_ms = 31;
// How much HPJ quota to give back to an app based on each non-top user interaction.
optional int64 hpj_reward_interaction_ms = 32;
// How much HPJ quota to give back to an app based on each notification seen event.
optional int64 hpj_reward_notification_seen_ms = 33;
// Next tag: 34
}
optional QuotaController quota_controller = 24;
@@ -560,6 +594,11 @@ message StateControllerProto {
// The amount of time that this job has remaining in its quota. This
// can be negative if the job is out of quota.
optional int64 remaining_quota_ms = 6;
// True if the app has requested that this be a foreground job.
optional bool is_requested_foreground_job = 7;
// True if this job is within the foreground quota bounds and is therefore allowed to
// run as a foreground job. Valid only if is_foreground_requested_job is true.
optional bool is_within_fg_job_quota = 8;
}
repeated TrackedJob tracked_jobs = 4;
@@ -665,6 +704,19 @@ message StateControllerProto {
repeated JobStatusShortInfoProto running_jobs = 5;
}
message TopAppTimer {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional Package pkg = 1;
// True if the Timer is actively tracking jobs.
optional bool is_active = 2;
// The time this timer last became active. Only valid if is_active is true.
optional int64 start_time_elapsed = 3;
// How many activities are currently in the RESUMED state. Valid only if is_active is
// true.
optional int32 activity_count = 4;
}
message PackageStats {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -677,6 +729,8 @@ message StateControllerProto {
repeated ExecutionStats execution_stats = 4;
reserved 5; // in_quota_alarm_listener
optional Timer fg_job_timer = 6;
}
repeated PackageStats package_stats = 5;

View File

@@ -145,7 +145,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = new ConnectivityController(mService);
when(mService.getMaxJobExecutionTimeMs(any()))
.thenReturn(JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
.thenReturn(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS);
// Slow network is too slow
assertFalse(controller.isSatisfied(createJobStatus(job), net,