Make sure jobs started from TOP app don't consume credits.

If a job is started while an app is in the TOP state, then it shouldn't
consume any of the app's credits, even if the app leaves the TOP state.

Bug: 158300259
Test: Android builds
Change-Id: Ie66a70a5a9342fb7e25129f7701ee72697e9a87b
This commit is contained in:
Kweku Adams
2021-12-13 17:35:51 -08:00
parent a38d92b819
commit 523a7fb6b3
3 changed files with 96 additions and 27 deletions

View File

@@ -1559,6 +1559,13 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
/** Return the current bias of the given UID. */
public int getUidBias(int uid) {
synchronized (mLock) {
return mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT);
}
}
@Override
public void onDeviceIdleStateChanged(boolean deviceIdle) {
synchronized (mLock) {

View File

@@ -366,9 +366,6 @@ public final class JobServiceContext implements ServiceConnection {
} catch (RemoteException e) {
// Whatever.
}
mEconomyManagerInternal.noteOngoingEventStarted(
job.getSourceUserId(), job.getSourcePackageName(),
getRunningActionId(job), String.valueOf(job.getJobId()));
final String jobPackage = job.getSourcePackageName();
final int jobUserId = job.getSourceUserId();
UsageStatsManagerInternal usageStats =
@@ -401,25 +398,6 @@ public final class JobServiceContext implements ServiceConnection {
}
}
@EconomicPolicy.AppAction
private static int getRunningActionId(@NonNull JobStatus job) {
switch (job.getEffectivePriority()) {
case JobInfo.PRIORITY_MAX:
return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
case JobInfo.PRIORITY_HIGH:
return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
case JobInfo.PRIORITY_LOW:
return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
case JobInfo.PRIORITY_MIN:
return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
default:
Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
// Intentional fallthrough
case JobInfo.PRIORITY_DEFAULT:
return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
}
}
/**
* Used externally to query the running job. Will return null if there is no job running.
*/
@@ -1043,9 +1021,6 @@ public final class JobServiceContext implements ServiceConnection {
} catch (RemoteException e) {
// Whatever.
}
mEconomyManagerInternal.noteOngoingEventStopped(
mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
getRunningActionId(mRunningJob), String.valueOf(mRunningJob.getJobId()));
if (mParams.getStopReason() == JobParameters.STOP_REASON_TIMEOUT) {
mEconomyManagerInternal.noteInstantaneousEvent(
mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),

View File

@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
import static android.app.job.JobInfo.getPriorityString;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.NonNull;
@@ -31,6 +33,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.tare.EconomicPolicy;
import com.android.server.tare.EconomyManagerInternal;
import com.android.server.tare.EconomyManagerInternal.ActionBill;
import com.android.server.tare.JobSchedulerEconomicPolicy;
@@ -195,7 +198,7 @@ public class TareController extends StateController {
JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1_000L)
));
/**
/**
* Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
* eligible to run unless it can pay for a job start and at least some period of execution time.
*/
@@ -285,6 +288,14 @@ public class TareController extends StateController {
}
};
/**
* List of jobs that started while the UID was in the TOP state. There will be no more than
* 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
* fine.
*/
@GuardedBy("mLock")
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
@GuardedBy("mLock")
private boolean mIsEnabled;
@@ -299,6 +310,7 @@ public class TareController extends StateController {
}
@Override
@GuardedBy("mLock")
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
final long nowElapsed = sElapsedRealtimeClock.millis();
jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus));
@@ -312,6 +324,7 @@ public class TareController extends StateController {
}
@Override
@GuardedBy("mLock")
public void prepareForExecutionLocked(JobStatus jobStatus) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
@@ -325,12 +338,29 @@ public class TareController extends StateController {
}
}
addJobToBillList(jobStatus, getRunningBill(jobStatus));
final int uid = jobStatus.getSourceUid();
if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
mTopStartedJobs.add(jobStatus);
// Top jobs won't count towards quota so there's no need to involve the EconomyManager.
} else {
mEconomyManagerInternal.noteOngoingEventStarted(userId, pkgName,
getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
}
}
@Override
@GuardedBy("mLock")
public void unprepareFromExecutionLocked(JobStatus jobStatus) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
mTopStartedJobs.remove(jobStatus);
final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
mRegisteredBillsAndJobs.get(userId, pkgName);
@@ -347,10 +377,14 @@ public class TareController extends StateController {
}
@Override
@GuardedBy("mLock")
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
mTopStartedJobs.remove(jobStatus);
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
mRegisteredBillsAndJobs.get(userId, pkgName);
if (billToJobMap != null) {
@@ -391,7 +425,16 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return true;
}
return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED);
if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED);
}
return canAffordBillLocked(jobStatus, BILL_JOB_START_HIGH_EXPEDITED);
}
/** @return true if the job was started while the app was in the TOP state. */
@GuardedBy("mLock")
private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
return mTopStartedJobs.contains(jobStatus);
}
@GuardedBy("mLock")
@@ -459,6 +502,9 @@ public class TareController extends StateController {
}
}
switch (jobStatus.getEffectivePriority()) {
case JobInfo.PRIORITY_MAX:
bills.add(BILL_JOB_START_MAX);
break;
case JobInfo.PRIORITY_HIGH:
bills.add(BILL_JOB_START_HIGH);
break;
@@ -471,6 +517,10 @@ public class TareController extends StateController {
case JobInfo.PRIORITY_MIN:
bills.add(BILL_JOB_START_MIN);
break;
default:
Slog.wtf(TAG, "Unexpected priority: "
+ JobInfo.getPriorityString(jobStatus.getEffectivePriority()));
break;
}
return bills;
}
@@ -502,11 +552,36 @@ public class TareController extends StateController {
}
}
@EconomicPolicy.AppAction
private static int getRunningActionId(@NonNull JobStatus job) {
switch (job.getEffectivePriority()) {
case JobInfo.PRIORITY_MAX:
return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
case JobInfo.PRIORITY_HIGH:
return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
case JobInfo.PRIORITY_LOW:
return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
case JobInfo.PRIORITY_MIN:
return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
default:
Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
// Intentional fallthrough
case JobInfo.PRIORITY_DEFAULT:
return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
}
}
@GuardedBy("mLock")
private boolean canAffordBillLocked(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
if (!mIsEnabled) {
return true;
}
if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
|| isTopStartedJobLocked(jobStatus)) {
// Jobs for the top app should always be allowed to run, and any jobs started while
// the app is on top shouldn't consume any credits.
return true;
}
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
ArrayMap<ActionBill, Boolean> actionAffordability =
@@ -533,6 +608,12 @@ public class TareController extends StateController {
if (!jobStatus.isRequestedExpeditedJob()) {
return false;
}
if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
|| isTopStartedJobLocked(jobStatus)) {
// Jobs for the top app should always be allowed to run, and any jobs started while
// the app is on top shouldn't consume any credits.
return true;
}
if (mService.isCurrentlyRunningLocked(jobStatus)) {
return canAffordBillLocked(jobStatus, getRunningBill(jobStatus));
}
@@ -548,6 +629,12 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return true;
}
if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
|| isTopStartedJobLocked(jobStatus)) {
// Jobs for the top app should always be allowed to run, and any jobs started while
// the app is on top shouldn't consume any credits.
return true;
}
if (mService.isCurrentlyRunningLocked(jobStatus)) {
if (jobStatus.isRequestedExpeditedJob()) {
return canAffordBillLocked(jobStatus, getRunningBill(jobStatus))