Fix issue #15681802: Missing RESET:TIME in complete battery histories

But wait, there's more!

- Keep track of sync durations in the aggregated stats.
- Add events for users that are running and in the foreground.
- Rework the activity manager's tracking of stuff using
  battery in the background to be based on proc stats, which
  allows it to be better about determing when it should reset
  its tracking of background work.
- Also add tracking of scheduled job execution, like we are
  doing for syncs.
- And once I started hooking battery stats in to
  JobSchedulerService, I found a few things I couldn't stop myself
  from changing: (1) make it very explicit that it doesn't start
  scheduling jobs until we have reached the point in system boot
  where third party apps are allowed to run, and (2) adjust
  the various for loops to not use iterators.

Change-Id: I69d812e27bcfee9e58a614f0f6b1c7545d7530b1
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7fd1660..aab7b1f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -120,6 +120,16 @@
     public static final int PROCESS_STATE = 12;
 
     /**
+     * A constant indicating a sync timer
+     */
+    public static final int SYNC = 13;
+
+    /**
+     * A constant indicating a job timer
+     */
+    public static final int JOB = 14;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -157,6 +167,8 @@
     private static final String FOREGROUND_DATA = "fg";
     private static final String STATE_TIME_DATA = "st";
     private static final String WAKELOCK_DATA = "wl";
+    private static final String SYNC_DATA = "sy";
+    private static final String JOB_DATA = "jb";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
     private static final String WAKEUP_REASON_DATA = "wr";
     private static final String NETWORK_DATA = "nt";
@@ -273,6 +285,20 @@
         public abstract Map<String, ? extends Wakelock> getWakelockStats();
 
         /**
+         * Returns a mapping containing sync statistics.
+         *
+         * @return a Map from Strings to Timer objects.
+         */
+        public abstract Map<String, ? extends Timer> getSyncStats();
+
+        /**
+         * Returns a mapping containing scheduled job statistics.
+         *
+         * @return a Map from Strings to Timer objects.
+         */
+        public abstract Map<String, ? extends Timer> getJobStats();
+
+        /**
          * The statistics associated with a particular wake lock.
          */
         public static abstract class Wakelock {
@@ -660,13 +686,19 @@
         public static final int EVENT_FOREGROUND = 0x0002;
         // Event is about an application package that is at the top of the screen.
         public static final int EVENT_TOP = 0x0003;
-        // Event is about an application package that is at the top of the screen.
+        // Event is about active sync operations.
         public static final int EVENT_SYNC = 0x0004;
         // Events for all additional wake locks aquired/release within a wake block.
         // These are not generated by default.
         public static final int EVENT_WAKE_LOCK = 0x0005;
+        // Event is about an application executing a scheduled job.
+        public static final int EVENT_JOB = 0x0006;
+        // Events for users running.
+        public static final int EVENT_USER_RUNNING = 0x0007;
+        // Events for foreground user.
+        public static final int EVENT_USER_FOREGROUND = 0x0008;
         // Number of event types.
-        public static final int EVENT_COUNT = 0x0006;
+        public static final int EVENT_COUNT = 0x0009;
         // Mask to extract out only the type part of the event.
         public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
@@ -680,6 +712,14 @@
         public static final int EVENT_SYNC_FINISH = EVENT_SYNC | EVENT_FLAG_FINISH;
         public static final int EVENT_WAKE_LOCK_START = EVENT_WAKE_LOCK | EVENT_FLAG_START;
         public static final int EVENT_WAKE_LOCK_FINISH = EVENT_WAKE_LOCK | EVENT_FLAG_FINISH;
+        public static final int EVENT_JOB_START = EVENT_JOB | EVENT_FLAG_START;
+        public static final int EVENT_JOB_FINISH = EVENT_JOB | EVENT_FLAG_FINISH;
+        public static final int EVENT_USER_RUNNING_START = EVENT_USER_RUNNING | EVENT_FLAG_START;
+        public static final int EVENT_USER_RUNNING_FINISH = EVENT_USER_RUNNING | EVENT_FLAG_FINISH;
+        public static final int EVENT_USER_FOREGROUND_START =
+                EVENT_USER_FOREGROUND | EVENT_FLAG_START;
+        public static final int EVENT_USER_FOREGROUND_FINISH =
+                EVENT_USER_FOREGROUND | EVENT_FLAG_FINISH;
 
         // For CMD_EVENT.
         public int eventCode;
@@ -1269,11 +1309,11 @@
     };
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
-            "null", "proc", "fg", "top", "sync", "wake_lock_in"
+            "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg"
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
-            "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl"
+            "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf"
     };
 
     /**
@@ -2080,10 +2120,9 @@
                 }
             }
             
-            Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
+            Map<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
             if (wakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
-                        : wakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
                     Uid.Wakelock wl = ent.getValue();
                     String linePrefix = "";
                     sb.setLength(0);
@@ -2105,6 +2144,32 @@
                 }
             }
 
+            Map<String, ? extends Timer> syncs = u.getSyncStats();
+            if (syncs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    if (totalTime != 0) {
+                        dumpLine(pw, uid, category, SYNC_DATA, ent.getKey(), totalTime, count);
+                    }
+                }
+            }
+
+            Map<String, ? extends Timer> jobs = u.getJobStats();
+            if (jobs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    if (totalTime != 0) {
+                        dumpLine(pw, uid, category, JOB_DATA, ent.getKey(), totalTime, count);
+                    }
+                }
+            }
+
             SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
             int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
@@ -2937,8 +3002,7 @@
             if (wakelocks.size() > 0) {
                 long totalFull = 0, totalPartial = 0, totalWindow = 0;
                 int count = 0;
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
-                    : wakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
                     Uid.Wakelock wl = ent.getValue();
                     String linePrefix = ": ";
                     sb.setLength(0);
@@ -2998,6 +3062,56 @@
                 }
             }
 
+            Map<String, ? extends Timer> syncs = u.getSyncStats();
+            if (syncs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    Sync ");
+                    sb.append(ent.getKey());
+                    sb.append(": ");
+                    if (totalTime != 0) {
+                        formatTimeMs(sb, totalTime);
+                        sb.append("realtime (");
+                        sb.append(count);
+                        sb.append(" times)");
+                    } else {
+                        sb.append("(not used)");
+                    }
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
+            Map<String, ? extends Timer> jobs = u.getJobStats();
+            if (syncs.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
+                    Timer timer = ent.getValue();
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    Job ");
+                    sb.append(ent.getKey());
+                    sb.append(": ");
+                    if (totalTime != 0) {
+                        formatTimeMs(sb, totalTime);
+                        sb.append("realtime (");
+                        sb.append(count);
+                        sb.append(" times)");
+                    } else {
+                        sb.append("(not used)");
+                    }
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
             SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
             int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
@@ -3260,7 +3374,6 @@
         int oldTemp = -1;
         int oldVolt = -1;
         long lastTime = -1;
-        long firstTime = -1;
 
         void reset() {
             oldState = oldState2 = 0;