Update jobscheduler dumpsys
- Show job IDs in the history.
- Show "last successful/failed run time" for existing jobs.
Bug 62052247
Test: Manual test with dumpsys jobscheduler
Change-Id: Ic0cdab58e4c5b454a3df8300607e9c24c4b1f438
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
index 8ad1bea..ba92295 100644
--- a/services/core/java/com/android/server/job/JobPackageTracker.java
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -39,19 +39,23 @@
public static final int EVENT_NULL = 0;
public static final int EVENT_START_JOB = 1;
public static final int EVENT_STOP_JOB = 2;
+ public static final int EVENT_START_PERIODIC_JOB = 3;
+ public static final int EVENT_STOP_PERIODIC_JOB = 4;
private final RingBufferIndices mEventIndices = new RingBufferIndices(EVENT_BUFFER_SIZE);
private final int[] mEventCmds = new int[EVENT_BUFFER_SIZE];
private final long[] mEventTimes = new long[EVENT_BUFFER_SIZE];
private final int[] mEventUids = new int[EVENT_BUFFER_SIZE];
private final String[] mEventTags = new String[EVENT_BUFFER_SIZE];
+ private final int[] mEventJobIds = new int[EVENT_BUFFER_SIZE];
- public void addEvent(int cmd, int uid, String tag) {
+ public void addEvent(int cmd, int uid, String tag, int jobId) {
int index = mEventIndices.add();
mEventCmds[index] = cmd;
mEventTimes[index] = SystemClock.elapsedRealtime();
mEventUids[index] = uid;
mEventTags[index] = tag;
+ mEventJobIds[index] = jobId;
}
DataSet mCurDataSet = new DataSet();
@@ -365,7 +369,8 @@
} else {
mCurDataSet.incActive(job.getSourceUid(), job.getSourcePackageName(), now);
}
- addEvent(EVENT_START_JOB, job.getSourceUid(), job.getBatteryName());
+ addEvent(job.getJob().isPeriodic() ? EVENT_START_PERIODIC_JOB : EVENT_START_JOB,
+ job.getSourceUid(), job.getBatteryName(), job.getJobId());
}
public void noteInactive(JobStatus job) {
@@ -376,7 +381,8 @@
mCurDataSet.decActive(job.getSourceUid(), job.getSourcePackageName(), now);
}
rebatchIfNeeded(now);
- addEvent(EVENT_STOP_JOB, job.getSourceUid(), job.getBatteryName());
+ addEvent(job.getJob().isPeriodic() ? EVENT_STOP_JOB : EVENT_STOP_PERIODIC_JOB,
+ job.getSourceUid(), job.getBatteryName(), job.getJobId());
}
public void noteConcurrency(int totalActive, int fgActive) {
@@ -448,16 +454,20 @@
}
final String label;
switch (mEventCmds[index]) {
- case EVENT_START_JOB: label = "START"; break;
- case EVENT_STOP_JOB: label = " STOP"; break;
- default: label = " ??"; break;
+ case EVENT_START_JOB: label = " START"; break;
+ case EVENT_STOP_JOB: label = " STOP"; break;
+ case EVENT_START_PERIODIC_JOB: label = "START-P"; break;
+ case EVENT_STOP_PERIODIC_JOB: label = " STOP-P"; break;
+ default: label = " ??"; break;
}
pw.print(prefix);
TimeUtils.formatDuration(mEventTimes[index]-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
pw.print(" ");
pw.print(label);
- pw.print(": ");
+ pw.print(": #");
UserHandle.formatUid(pw, uid);
+ pw.print("/");
+ pw.print(mEventJobIds[index]);
pw.print(" ");
pw.println(mEventTags[index]);
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b8fe884..98c65fd 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1122,7 +1122,8 @@
delayMillis =
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
- JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
+ JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
+ failureToReschedule.getLastSuccessfulRunTime(), System.currentTimeMillis());
for (int ic=0; ic<mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailureLocked(newJob, failureToReschedule);
@@ -1160,7 +1161,9 @@
newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
}
return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
- newLatestRuntimeElapsed, 0 /* backoffAttempt */);
+ newLatestRuntimeElapsed, 0 /* backoffAttempt */,
+ System.currentTimeMillis() /* lastSuccessfulRunTime */,
+ periodicToReschedule.getLastFailedRunTime());
}
// JobCompletedListener implementations.
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f0cd8a8..84810be 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -345,6 +345,11 @@
out.attribute(null, "uid", Integer.toString(jobStatus.getUid()));
out.attribute(null, "priority", String.valueOf(jobStatus.getPriority()));
out.attribute(null, "flags", String.valueOf(jobStatus.getFlags()));
+
+ out.attribute(null, "lastSuccessfulRunTime",
+ String.valueOf(jobStatus.getLastSuccessfulRunTime()));
+ out.attribute(null, "lastFailedRunTime",
+ String.valueOf(jobStatus.getLastFailedRunTime()));
}
private void writeBundleToXml(PersistableBundle extras, XmlSerializer out)
@@ -555,6 +560,8 @@
IOException {
JobInfo.Builder jobBuilder;
int uid, sourceUserId;
+ long lastSuccessfulRunTime;
+ long lastFailedRunTime;
// Read out job identifier attributes and priority.
try {
@@ -572,6 +579,12 @@
}
val = parser.getAttributeValue(null, "sourceUserId");
sourceUserId = val == null ? -1 : Integer.parseInt(val);
+
+ val = parser.getAttributeValue(null, "lastSuccessfulRunTime");
+ lastSuccessfulRunTime = val == null ? 0 : Long.parseLong(val);
+
+ val = parser.getAttributeValue(null, "lastFailedRunTime");
+ lastFailedRunTime = val == null ? 0 : Long.parseLong(val);
} catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing job's required fields, skipping");
return null;
@@ -708,7 +721,8 @@
// And now we're done
JobStatus js = new JobStatus(
jobBuilder.build(), uid, sourcePackageName, sourceUserId, sourceTag,
- elapsedRuntimes.first, elapsedRuntimes.second);
+ elapsedRuntimes.first, elapsedRuntimes.second,
+ lastSuccessfulRunTime, lastFailedRunTime);
return js;
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 446b0d6..9658da7 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -26,6 +26,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.format.Time;
import android.util.ArraySet;
import android.util.Slog;
import android.util.TimeUtils;
@@ -184,6 +185,17 @@
public long madeActive;
/**
+ * Last time a job finished successfully for a periodic job, in the currentTimeMillis time,
+ * for dumpsys.
+ */
+ private long mLastSuccessfulRunTime;
+
+ /**
+ * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys.
+ */
+ private long mLastFailedRunTime;
+
+ /**
* For use only by ContentObserverController: state it is maintaining about content URIs
* being observed.
*/
@@ -196,7 +208,7 @@
private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis,
- long latestRunTimeElapsedMillis) {
+ long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime) {
this.job = job;
this.callingUid = callingUid;
@@ -263,6 +275,9 @@
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
}
this.requiredConstraints = requiredConstraints;
+
+ mLastSuccessfulRunTime = lastSuccessfulRunTime;
+ mLastFailedRunTime = lastFailedRunTime;
}
/** Copy constructor. */
@@ -270,7 +285,8 @@
this(jobStatus.getJob(), jobStatus.getUid(),
jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
- jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed());
+ jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
+ jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime());
}
/**
@@ -281,18 +297,22 @@
* We consider a freshly loaded job to no longer be in back-off.
*/
public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
- String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+ String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
+ long lastSuccessfulRunTime, long lastFailedRunTime) {
this(job, callingUid, sourcePackageName, sourceUserId, sourceTag, 0,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
+ lastSuccessfulRunTime, lastFailedRunTime);
}
/** Create a new job to be rescheduled with the provided parameters. */
public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
- long newLatestRuntimeElapsedMillis, int backoffAttempt) {
+ long newLatestRuntimeElapsedMillis, int backoffAttempt,
+ long lastSuccessfulRunTime, long lastFailedRunTime) {
this(rescheduling.job, rescheduling.getUid(),
rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
- newLatestRuntimeElapsedMillis);
+ newLatestRuntimeElapsedMillis,
+ lastSuccessfulRunTime, lastFailedRunTime);
}
/**
@@ -316,7 +336,8 @@
elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
}
return new JobStatus(job, callingUid, sourcePackageName, sourceUserId, tag, 0,
- earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
+ earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
+ 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */);
}
public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
@@ -669,6 +690,14 @@
trackingControllers |= which;
}
+ public long getLastSuccessfulRunTime() {
+ return mLastSuccessfulRunTime;
+ }
+
+ public long getLastFailedRunTime() {
+ return mLastFailedRunTime;
+ }
+
public boolean shouldDump(int filterUid) {
return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid
|| UserHandle.getAppId(getSourceUid()) == filterUid;
@@ -1041,5 +1070,17 @@
if (numFailures != 0) {
pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
}
+ final Time t = new Time();
+ final String format = "%Y-%m-%d %H:%M:%S";
+ if (mLastSuccessfulRunTime != 0) {
+ pw.print(prefix); pw.print("Last successful run: ");
+ t.set(mLastSuccessfulRunTime);
+ pw.println(t.format(format));
+ }
+ if (mLastFailedRunTime != 0) {
+ pw.print(prefix); pw.print("Last failed run: ");
+ t.set(mLastFailedRunTime);
+ pw.println(t.format(format));
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 33e1a16..689c8f7 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -206,7 +206,8 @@
invalidLateRuntimeElapsedMillis - TWO_HOURS; // Early is (late - period).
final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage",
0 /* sourceUserId */, "someTag",
- invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
+ invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
+ 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */);
mTaskStoreUnderTest.add(js);
Thread.sleep(IO_WAIT);