Fix JobThrottling tests, take 2.

1. Reset execution quotas between tests so earlier run tests don't
affect later ones.
2. Ensure uidActive overrides even the NEVER bucket. Also log a wtf if
we ever get into this situation.

Bug: 138253466
Bug: 149931359
Test: atest android.jobscheduler.cts.JobThrottlingTest
Change-Id: I1caf92fe8dda3c661ef68ca56c32fcd709e26856
This commit is contained in:
Kweku Adams
2020-02-24 17:20:04 -08:00
parent 27f83cdbce
commit 87a4cd5b8a
5 changed files with 68 additions and 18 deletions

View File

@@ -3015,6 +3015,10 @@ public class JobSchedulerService extends com.android.server.SystemService
return 0;
}
void resetExecutionQuota(@NonNull String pkgName, int userId) {
mQuotaController.clearAppStats(pkgName, userId);
}
void resetScheduleQuota() {
mQuotaTracker.clear();
}

View File

@@ -66,6 +66,8 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
return getJobState(pw);
case "heartbeat":
return doHeartbeat(pw);
case "reset-execution-quota":
return resetExecutionQuota(pw);
case "reset-schedule-quota":
return resetScheduleQuota(pw);
case "trigger-dock-state":
@@ -346,6 +348,40 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
return -1;
}
private int resetExecutionQuota(PrintWriter pw) throws Exception {
checkPermission("reset execution quota");
int userId = UserHandle.USER_SYSTEM;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "-u":
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
default:
pw.println("Error: unknown option '" + opt + "'");
return -1;
}
}
if (userId == UserHandle.USER_CURRENT) {
userId = ActivityManager.getCurrentUser();
}
final String pkgName = getNextArgRequired();
final long ident = Binder.clearCallingIdentity();
try {
mInternal.resetExecutionQuota(pkgName, userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
return 0;
}
private int resetScheduleQuota(PrintWriter pw) throws Exception {
checkPermission("reset schedule quota");

View File

@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -196,6 +198,9 @@ public final class BackgroundJobsController extends StateController {
} else {
isActive = (activeState == KNOWN_ACTIVE);
}
if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) {
Slog.wtf(TAG, "App became active but still in NEVER bucket");
}
boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
didChange |= jobStatus.setUidActive(isActive);
return didChange;

View File

@@ -1271,7 +1271,8 @@ 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) || standbyBucket == NEVER_INDEX) {
if ((!mReadyWithinQuota && !mReadyDynamicSatisfied)
|| getEffectiveStandbyBucket() == NEVER_INDEX) {
return false;
}
// Deadline constraint trumps other constraints besides quota and dynamic (except for

View File

@@ -579,23 +579,7 @@ public final class QuotaController extends StateController {
Slog.wtf(TAG, "Told app removed but given null package name.");
return;
}
final int userId = UserHandle.getUserId(uid);
mTrackedJobs.delete(userId, packageName);
Timer timer = mPkgTimers.get(userId, packageName);
if (timer != null) {
if (timer.isActive()) {
Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off.");
timer.dropEverythingLocked();
}
mPkgTimers.delete(userId, packageName);
}
mTimingSessions.delete(userId, packageName);
QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
if (alarmListener != null) {
mAlarmManager.cancel(alarmListener);
mInQuotaAlarmListeners.delete(userId, packageName);
}
mExecutionStatsCache.delete(userId, packageName);
clearAppStats(packageName, UserHandle.getUserId(uid));
mForegroundUids.delete(uid);
mUidToPackageCache.remove(uid);
}
@@ -610,6 +594,26 @@ public final class QuotaController extends StateController {
mUidToPackageCache.clear();
}
/** Drop all historical stats and stop tracking any active sessions for the specified app. */
public void clearAppStats(@NonNull String packageName, int userId) {
mTrackedJobs.delete(userId, packageName);
Timer timer = mPkgTimers.get(userId, packageName);
if (timer != null) {
if (timer.isActive()) {
Slog.e(TAG, "clearAppStats called before Timer turned off.");
timer.dropEverythingLocked();
}
mPkgTimers.delete(userId, packageName);
}
mTimingSessions.delete(userId, packageName);
QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
if (alarmListener != null) {
mAlarmManager.cancel(alarmListener);
mInQuotaAlarmListeners.delete(userId, packageName);
}
mExecutionStatsCache.delete(userId, packageName);
}
private boolean isUidInForeground(int uid) {
if (UserHandle.isCore(uid)) {
return true;