Fix ConnectivityController running job counting.
Use a Set of running jobs so that ConnectivityController has a clear
understanding of how many jobs are running. In the process, also fix the
issue where controllers would think that a job was running even though
it had actually failed to start.
Bug: 184740668
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/job
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job
Test: atest CtsJobSchedulerTestCases
Change-Id: Iff142e63b2a3cb92c9467608f95401c7a0f287ee
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index d94d638..a6386d5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -705,12 +705,16 @@
private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
@WorkType final int workType) {
final List<StateController> controllers = mService.mControllers;
- for (int ic = 0; ic < controllers.size(); ic++) {
+ final int numControllers = controllers.size();
+ for (int ic = 0; ic < numControllers; ic++) {
controllers.get(ic).prepareForExecutionLocked(jobStatus);
}
if (!worker.executeRunnableJob(jobStatus, workType)) {
Slog.e(TAG, "Error executing " + jobStatus);
mWorkCountTracker.onStagedJobFailed(workType);
+ for (int ic = 0; ic < numControllers; ic++) {
+ controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ }
} else {
mRunningJobs.add(jobStatus);
mWorkCountTracker.onJobStarted(workType);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 11a8b3b..22f1ff6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -147,7 +147,8 @@
// 9. Enqueue time
// TODO: maybe consider number of jobs
// TODO: consider IMPORTANT_WHILE_FOREGROUND bit
- final int runningPriority = prioritizeExistenceOver(0, us1.numRunning, us2.numRunning);
+ final int runningPriority = prioritizeExistenceOver(0,
+ us1.runningJobs.size(), us2.runningJobs.size());
if (runningPriority != 0) {
return runningPriority;
}
@@ -254,7 +255,18 @@
if (jobStatus.hasConnectivityConstraint()) {
final UidStats uidStats =
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
- uidStats.numRunning++;
+ uidStats.runningJobs.add(jobStatus);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint()) {
+ final UidStats uidStats =
+ getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
+ uidStats.runningJobs.remove(jobStatus);
+ postAdjustCallbacks();
}
}
@@ -270,12 +282,7 @@
final UidStats uidStats =
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
uidStats.numReadyWithConnectivity--;
- if (jobStatus.madeActive != 0) {
- // numRunning would be 0 if the UidStats object didn't exist before this method
- // was called. getUidStats() handles logging, so just make sure we don't save a
- // negative value.
- uidStats.numRunning = Math.max(0, uidStats.numRunning - 1);
- }
+ uidStats.runningJobs.remove(jobStatus);
maybeRevokeStandbyExceptionLocked(jobStatus);
postAdjustCallbacks();
}
@@ -1131,7 +1138,7 @@
private static class UidStats {
public final int uid;
public int basePriority;
- public int numRunning;
+ public final ArraySet<JobStatus> runningJobs = new ArraySet<>();
public int numReadyWithConnectivity;
public int numRequestedNetworkAvailable;
public int numEJs;
@@ -1148,7 +1155,7 @@
pw.print("UidStats{");
pw.print("uid", uid);
pw.print("pri", basePriority);
- pw.print("#run", numRunning);
+ pw.print("#run", runningJobs.size());
pw.print("#readyWithConn", numReadyWithConnectivity);
pw.print("#netAvail", numRequestedNetworkAvailable);
pw.print("#EJs", numEJs);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index 8b0da34..e64233f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -136,6 +136,29 @@
}
@Override
+ public void unprepareFromExecutionLocked(JobStatus taskStatus) {
+ if (taskStatus.hasContentTriggerConstraint()) {
+ if (taskStatus.contentObserverJobInstance != null) {
+ if (taskStatus.contentObserverJobInstance.mChangedUris == null) {
+ taskStatus.contentObserverJobInstance.mChangedUris = taskStatus.changedUris;
+ } else {
+ taskStatus.contentObserverJobInstance.mChangedUris
+ .addAll(taskStatus.changedUris);
+ }
+ if (taskStatus.contentObserverJobInstance.mChangedAuthorities == null) {
+ taskStatus.contentObserverJobInstance.mChangedAuthorities =
+ taskStatus.changedAuthorities;
+ } else {
+ taskStatus.contentObserverJobInstance.mChangedAuthorities
+ .addAll(taskStatus.changedAuthorities);
+ }
+ taskStatus.changedUris = null;
+ taskStatus.changedAuthorities = null;
+ }
+ }
+ }
+
+ @Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
boolean forUpdate) {
if (taskStatus.clearTrackingController(JobStatus.TRACKING_CONTENT)) {
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 d4ce437..aace645 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
@@ -34,7 +34,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
@@ -677,27 +676,30 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
- boolean forUpdate) {
- if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
- Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(),
- jobStatus.getSourcePackageName());
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ if (timer != null) {
+ timer.stopTrackingJob(jobStatus);
+ }
+ if (jobStatus.isRequestedExpeditedJob()) {
+ timer = mEJPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
if (timer != null) {
timer.stopTrackingJob(jobStatus);
}
- if (jobStatus.isRequestedExpeditedJob()) {
- timer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
- jobStatus.getSourcePackageName());
- if (timer != null) {
- timer.stopTrackingJob(jobStatus);
- }
- }
+ }
+ mTopStartedJobs.remove(jobStatus);
+ }
+
+ @Override
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
+ unprepareFromExecutionLocked(jobStatus);
ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
if (jobs != null) {
jobs.remove(jobStatus);
}
- mTopStartedJobs.remove(jobStatus);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 334876f..f0fc3b0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -76,6 +76,12 @@
}
/**
+ * Optionally implement logic here for when a job that was about to be executed failed to start.
+ */
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ }
+
+ /**
* Remove task - this will happen if the task is cancelled, completed, etc.
*/
public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,