From 87a4cd5b8a6bbc245bbaec8bdf49250c41cb85f9 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Mon, 24 Feb 2020 17:20:04 -0800 Subject: [PATCH] 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 --- .../server/job/JobSchedulerService.java | 4 ++ .../server/job/JobSchedulerShellCommand.java | 36 ++++++++++++++++++ .../controllers/BackgroundJobsController.java | 5 +++ .../server/job/controllers/JobStatus.java | 3 +- .../job/controllers/QuotaController.java | 38 ++++++++++--------- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index c1e529f3f9660..f36d5551bfeb2 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -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(); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index 6becf04deb981..957170890a8f3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java @@ -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"); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index aa3d74a51ff27..d7b1e0793f673 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -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; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index b63cc1918a7a5..a1cb840b932e8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -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 diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 8eefac8abdac7..4393a95af5baf 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -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;