Print heartbeat information in job dumpsys

Sample:
=======================
  Heartbeat:
    Current:    1
    Next
      ACTIVE:   0
      WORKING:  11
      FREQUENT: 43
      RARE:     130
    Last heartbeat: -1m21s575ms
    Next heartbeat: +9m38s425ms

Started users: [0, 10]
Registered 82 jobs:
  JOB #1000/800: 341134 android/com.android.server.pm.BackgroundDexOptService
       :
    Last run heartbeat: 1
    Ready: false (job=false user=true !pending=true !active=true !backingup=true comp=true)
=======================

Also:
=======================
    current_heartbeat=1
    next_heartbeat=[
      0
      11
      43
      130
    ]
    last_heartbeat_time=30828
    next_heartbeat_time=690828
  :
        last_run_heartbeat=1
=======================

Test: boot
Test: dumpsys jobscheduler
Test: incident_report jobscheduler
Change-Id: I2bf5d6e4189d3938e04a4790a6b1d55fb44f68e3
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 2d31c5a..69abed3 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -36,6 +36,11 @@
 
     optional ConstantsProto settings = 1;
 
+    optional int32 current_heartbeat = 14;
+    repeated int32 next_heartbeat = 15;
+    optional int64 last_heartbeat_time_millis = 16;
+    optional int64 next_heartbeat_time_millis = 17;
+
     repeated int32 started_users = 2;
 
     message RegisteredJob {
@@ -54,6 +59,8 @@
         optional bool is_job_currently_active = 6;
         optional bool is_uid_backing_up = 7;
         optional bool is_component_present = 8;
+
+        optional int64 last_run_heartbeat = 9;
     }
     repeated RegisteredJob registered_jobs = 3;
 
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 017fada..24adf2b 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -87,6 +87,7 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
+import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
 import com.android.server.job.controllers.AppIdleController;
 import com.android.server.job.controllers.BackgroundJobsController;
 import com.android.server.job.controllers.BatteryController;
@@ -2908,6 +2909,23 @@
         synchronized (mLock) {
             mConstants.dump(pw);
             pw.println();
+
+            pw.println("  Heartbeat:");
+            pw.print("    Current:    "); pw.println(mHeartbeat);
+            pw.println("    Next");
+            pw.print("      ACTIVE:   "); pw.println(mNextBucketHeartbeat[0]);
+            pw.print("      WORKING:  "); pw.println(mNextBucketHeartbeat[1]);
+            pw.print("      FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
+            pw.print("      RARE:     "); pw.println(mNextBucketHeartbeat[3]);
+            pw.print("    Last heartbeat: ");
+            TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
+            pw.println();
+            pw.print("    Next heartbeat: ");
+            TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
+                    nowElapsed, pw);
+            pw.println();
+            pw.println();
+
             pw.println("Started users: " + Arrays.toString(mStartedUsers));
             pw.print("Registered ");
             pw.print(mJobs.size());
@@ -2925,6 +2943,10 @@
                     }
 
                     job.dump(pw, "    ", true, nowElapsed);
+                    pw.print("    Last run heartbeat: ");
+                    pw.print(heartbeatWhenJobsLastRun(job));
+                    pw.println();
+
                     pw.print("    Ready: ");
                     pw.print(isReadyToBeExecutedLocked(job));
                     pw.print(" (job=");
@@ -3067,6 +3089,16 @@
 
         synchronized (mLock) {
             mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
+            proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
+            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
+            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
+            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
+            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
+            proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
+                    mLastHeartbeatTime - nowUptime);
+            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
+                    mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
+
             for (int u : mStartedUsers) {
                 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
             }
@@ -3105,6 +3137,7 @@
                     }
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
                             componentPresent);
+                    proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
 
                     proto.end(rjToken);
                 }