Track foreground activities in battery stats.

To help correlate battery usage against actual foreground app usage,
start tracking time on a per UID basis.  It uses the nesting feature
of StopwatchTimer to track multiple PIDs inside the same UID.

Improves units on docs, and persists foreground CPU time with a
consistent ordering.  Reports foreground activities time and
foreground CPU time.

Change-Id: I95d12963923e3fd421730f6fbfc842dfd8d3a055
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 499ec77..29e8d9c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -98,6 +98,11 @@
     public static final int VIBRATOR_ON = 9;
 
     /**
+     * A constant indicating a foreground activity timer
+     */
+    public static final int FOREGROUND_ACTIVITY = 10;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -125,7 +130,7 @@
     /**
      * Bump the version on this if the checkin format changes.
      */
-    private static final int BATTERY_STATS_CHECKIN_VERSION = 5;
+    private static final int BATTERY_STATS_CHECKIN_VERSION = 6;
     
     private static final long BYTES_PER_KB = 1024;
     private static final long BYTES_PER_MB = 1048576; // 1024^2
@@ -137,6 +142,7 @@
     private static final String PROCESS_DATA = "pr";
     private static final String SENSOR_DATA = "sr";
     private static final String VIBRATOR_DATA = "vib";
+    private static final String FOREGROUND_DATA = "fg";
     private static final String WAKELOCK_DATA = "wl";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
     private static final String NETWORK_DATA = "nt";
@@ -276,6 +282,8 @@
         public abstract void noteAudioTurnedOffLocked();
         public abstract void noteVideoTurnedOnLocked();
         public abstract void noteVideoTurnedOffLocked();
+        public abstract void noteActivityResumedLocked();
+        public abstract void noteActivityPausedLocked();
         public abstract long getWifiRunningTime(long batteryRealtime, int which);
         public abstract long getFullWifiLockTime(long batteryRealtime, int which);
         public abstract long getWifiScanTime(long batteryRealtime, int which);
@@ -283,6 +291,7 @@
                                                   int which);
         public abstract long getAudioTurnedOnTime(long batteryRealtime, int which);
         public abstract long getVideoTurnedOnTime(long batteryRealtime, int which);
+        public abstract Timer getForegroundActivityTimer();
         public abstract Timer getVibratorOnTimer();
 
         /**
@@ -1229,7 +1238,7 @@
         final int NU = uidStats.size();
         
         String category = STAT_NAMES[which];
-        
+
         // Dump "battery" stat
         dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, 
                 which == STATS_SINCE_CHARGED ? getStartCount() : "N/A",
@@ -1417,22 +1426,31 @@
                 }
             }
 
+            Timer fgTimer = u.getForegroundActivityTimer();
+            if (fgTimer != null) {
+                // Convert from microseconds to milliseconds with rounding
+                long totalTime = (fgTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000;
+                int count = fgTimer.getCountLocked(which);
+                if (totalTime != 0) {
+                    dumpLine(pw, uid, category, FOREGROUND_DATA, totalTime, count);
+                }
+            }
+
             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
             if (processStats.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
                         : processStats.entrySet()) {
                     Uid.Proc ps = ent.getValue();
-    
-                    long userTime = ps.getUserTime(which);
-                    long systemTime = ps.getSystemTime(which);
-                    int starts = ps.getStarts(which);
-    
-                    if (userTime != 0 || systemTime != 0 || starts != 0) {
-                        dumpLine(pw, uid, category, PROCESS_DATA, 
-                                ent.getKey(), // proc
-                                userTime * 10, // cpu time in ms
-                                systemTime * 10, // user time in ms
-                                starts); // process starts
+
+                    final long userMillis = ps.getUserTime(which) * 10;
+                    final long systemMillis = ps.getSystemTime(which) * 10;
+                    final long foregroundMillis = ps.getForegroundTime(which) * 10;
+                    final long starts = ps.getStarts(which);
+
+                    if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
+                            || starts != 0) {
+                        dumpLine(pw, uid, category, PROCESS_DATA, ent.getKey(), userMillis,
+                                systemMillis, foregroundMillis, starts);
                     }
                 }
             }
@@ -1961,6 +1979,24 @@
                 }
             }
 
+            Timer fgTimer = u.getForegroundActivityTimer();
+            if (fgTimer != null) {
+                // Convert from microseconds to milliseconds with rounding
+                long totalTime = (fgTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000;
+                int count = fgTimer.getCountLocked(which);
+                if (totalTime != 0) {
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    Foreground activities: ");
+                    formatTimeMs(sb, totalTime);
+                    sb.append("realtime (");
+                    sb.append(count);
+                    sb.append(" times)");
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
             if (processStats.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
@@ -1968,23 +2004,26 @@
                     Uid.Proc ps = ent.getValue();
                     long userTime;
                     long systemTime;
+                    long foregroundTime;
                     int starts;
                     int numExcessive;
 
                     userTime = ps.getUserTime(which);
                     systemTime = ps.getSystemTime(which);
+                    foregroundTime = ps.getForegroundTime(which);
                     starts = ps.getStarts(which);
                     numExcessive = which == STATS_SINCE_CHARGED
                             ? ps.countExcessivePowers() : 0;
 
-                    if (userTime != 0 || systemTime != 0 || starts != 0
+                    if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
                             || numExcessive != 0) {
                         sb.setLength(0);
                         sb.append(prefix); sb.append("    Proc ");
                                 sb.append(ent.getKey()); sb.append(":\n");
                         sb.append(prefix); sb.append("      CPU: ");
                                 formatTime(sb, userTime); sb.append("usr + ");
-                                formatTime(sb, systemTime); sb.append("krn");
+                                formatTime(sb, systemTime); sb.append("krn ; ");
+                                formatTime(sb, foregroundTime); sb.append("fg");
                         if (starts != 0) {
                             sb.append("\n"); sb.append(prefix); sb.append("      ");
                                     sb.append(starts); sb.append(" proc starts");
@@ -2042,7 +2081,7 @@
                                         sb.append(sent.getKey()); sb.append(":\n");
                                 sb.append(prefix); sb.append("        Created for: ");
                                         formatTimeMs(sb, startTime / 1000);
-                                        sb.append(" uptime\n");
+                                        sb.append("uptime\n");
                                 sb.append(prefix); sb.append("        Starts: ");
                                         sb.append(starts);
                                         sb.append(", launches: "); sb.append(launches);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 04b98841..85f7653 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -83,7 +83,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 64 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 65 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -663,7 +663,7 @@
         }
 
         public void logState(Printer pw, String prefix) {
-            pw.println(prefix + " mCount=" + mCount
+            pw.println(prefix + "mCount=" + mCount
                     + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
                     + " mUnpluggedCount=" + mUnpluggedCount);
             pw.println(prefix + "mTotalTime=" + mTotalTime
@@ -1048,7 +1048,7 @@
 
         public void logState(Printer pw, String prefix) {
             super.logState(pw, prefix);
-            pw.println(prefix + "mNesting=" + mNesting + "mUpdateTime=" + mUpdateTime
+            pw.println(prefix + "mNesting=" + mNesting + " mUpdateTime=" + mUpdateTime
                     + " mAcquireTime=" + mAcquireTime);
         }
 
@@ -2240,6 +2240,14 @@
         getUidStatsLocked(uid).noteVideoTurnedOffLocked();
     }
 
+    public void noteActivityResumedLocked(int uid) {
+        getUidStatsLocked(uid).noteActivityResumedLocked();
+    }
+
+    public void noteActivityPausedLocked(int uid) {
+        getUidStatsLocked(uid).noteActivityPausedLocked();
+    }
+
     public void noteVibratorOnLocked(int uid, long durationMillis) {
         getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis);
     }
@@ -2541,6 +2549,8 @@
         boolean mVideoTurnedOn;
         StopwatchTimer mVideoTurnedOnTimer;
 
+        StopwatchTimer mForegroundActivityTimer;
+
         BatchTimer mVibratorOnTimer;
 
         Counter[] mUserActivityCounters;
@@ -2776,6 +2786,27 @@
             }
         }
 
+        public StopwatchTimer createForegroundActivityTimerLocked() {
+            if (mForegroundActivityTimer == null) {
+                mForegroundActivityTimer = new StopwatchTimer(
+                        Uid.this, FOREGROUND_ACTIVITY, null, mUnpluggables);
+            }
+            return mForegroundActivityTimer;
+        }
+
+        @Override
+        public void noteActivityResumedLocked() {
+            // We always start, since we want multiple foreground PIDs to nest
+            createForegroundActivityTimerLocked().startRunningLocked(BatteryStatsImpl.this);
+        }
+
+        @Override
+        public void noteActivityPausedLocked() {
+            if (mForegroundActivityTimer != null) {
+                mForegroundActivityTimer.stopRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
         public BatchTimer createVibratorOnTimerLocked() {
             if (mVibratorOnTimer == null) {
                 mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
@@ -2844,6 +2875,11 @@
         }
 
         @Override
+        public Timer getForegroundActivityTimer() {
+            return mForegroundActivityTimer;
+        }
+
+        @Override
         public Timer getVibratorOnTimer() {
             return mVibratorOnTimer;
         }
@@ -2919,6 +2955,9 @@
                 active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
                 active |= mVideoTurnedOn;
             }
+            if (mForegroundActivityTimer != null) {
+                active |= !mForegroundActivityTimer.reset(BatteryStatsImpl.this, false);
+            }
             if (mVibratorOnTimer != null) {
                 if (mVibratorOnTimer.reset(BatteryStatsImpl.this, false)) {
                     mVibratorOnTimer.detach();
@@ -3018,6 +3057,10 @@
                     mVideoTurnedOnTimer.detach();
                     mVideoTurnedOnTimer = null;
                 }
+                if (mForegroundActivityTimer != null) {
+                    mForegroundActivityTimer.detach();
+                    mForegroundActivityTimer = null;
+                }
                 if (mUserActivityCounters != null) {
                     for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                         mUserActivityCounters[i].detach();
@@ -3099,6 +3142,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (mForegroundActivityTimer != null) {
+                out.writeInt(1);
+                mForegroundActivityTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
             if (mVibratorOnTimer != null) {
                 out.writeInt(1);
                 mVibratorOnTimer.writeToParcel(out, batteryRealtime);
@@ -3204,6 +3253,12 @@
                 mVideoTurnedOnTimer = null;
             }
             if (in.readInt() != 0) {
+                mForegroundActivityTimer = new StopwatchTimer(
+                        Uid.this, FOREGROUND_ACTIVITY, null, mUnpluggables, in);
+            } else {
+                mForegroundActivityTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
                         mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal, in);
             } else {
@@ -3372,16 +3427,16 @@
             long mSystemTime;
 
             /**
-             * Number of times the process has been started.
-             */
-            int mStarts;
-
-            /**
              * Amount of time the process was running in the foreground.
              */
             long mForegroundTime;
 
             /**
+             * Number of times the process has been started.
+             */
+            int mStarts;
+
+            /**
              * The amount of user time loaded from a previous save.
              */
             long mLoadedUserTime;
@@ -3392,16 +3447,16 @@
             long mLoadedSystemTime;
 
             /**
-             * The number of times the process has started from a previous save.
-             */
-            int mLoadedStarts;
-
-            /**
              * The amount of foreground time loaded from a previous save.
              */
             long mLoadedForegroundTime;
 
             /**
+             * The number of times the process has started from a previous save.
+             */
+            int mLoadedStarts;
+
+            /**
              * The amount of user time loaded from the previous run.
              */
             long mLastUserTime;
@@ -3412,16 +3467,16 @@
             long mLastSystemTime;
 
             /**
-             * The number of times the process has started from the previous run.
-             */
-            int mLastStarts;
-
-            /**
              * The amount of foreground time loaded from the previous run
              */
             long mLastForegroundTime;
 
             /**
+             * The number of times the process has started from the previous run.
+             */
+            int mLastStarts;
+
+            /**
              * The amount of user time when last unplugged.
              */
             long mUnpluggedUserTime;
@@ -3432,15 +3487,15 @@
             long mUnpluggedSystemTime;
 
             /**
-             * The number of times the process has started before unplugged.
-             */
-            int mUnpluggedStarts;
-
-            /**
              * The amount of foreground time since unplugged.
              */
             long mUnpluggedForegroundTime;
 
+            /**
+             * The number of times the process has started before unplugged.
+             */
+            int mUnpluggedStarts;
+
             SamplingCounter[] mSpeedBins;
 
             ArrayList<ExcessivePower> mExcessivePower;
@@ -3453,8 +3508,8 @@
             public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
                 mUnpluggedUserTime = mUserTime;
                 mUnpluggedSystemTime = mSystemTime;
-                mUnpluggedStarts = mStarts;
                 mUnpluggedForegroundTime = mForegroundTime;
+                mUnpluggedStarts = mStarts;
             }
 
             public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
@@ -5362,6 +5417,9 @@
                 u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
+                u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
                 u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
             }
 
@@ -5415,6 +5473,7 @@
                 Uid.Proc p = u.getProcessStatsLocked(procName);
                 p.mUserTime = p.mLoadedUserTime = in.readLong();
                 p.mSystemTime = p.mLoadedSystemTime = in.readLong();
+                p.mForegroundTime = p.mLoadedForegroundTime = in.readLong();
                 p.mStarts = p.mLoadedStarts = in.readInt();
                 int NSB = in.readInt();
                 if (NSB > 100) {
@@ -5564,6 +5623,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (u.mForegroundActivityTimer != null) {
+                out.writeInt(1);
+                u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
             if (u.mVibratorOnTimer != null) {
                 out.writeInt(1);
                 u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -5633,6 +5698,7 @@
                     Uid.Proc ps = ent.getValue();
                     out.writeLong(ps.mUserTime);
                     out.writeLong(ps.mSystemTime);
+                    out.writeLong(ps.mForegroundTime);
                     out.writeInt(ps.mStarts);
                     final int N = ps.mSpeedBins.length;
                     out.writeInt(N);
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
index b1bb8c1..bd0914d 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -518,6 +518,10 @@
         return pids;
     }
 
+    /**
+     * Returns the total time (in clock ticks, or 1/100 sec) spent executing in
+     * both user and system code.
+     */
     public long getCpuTimeForPid(int pid) {
         final String statFile = "/proc/" + pid + "/stat";
         final long[] statsData = mSinglePidStatsData;
@@ -531,9 +535,9 @@
     }
 
     /**
-     * Returns the times spent at each CPU speed, since the last call to this method. If this
-     * is the first time, it will return 1 for each value.
-     * @return relative times spent at different speed steps.
+     * Returns the delta time (in clock ticks, or 1/100 sec) spent at each CPU
+     * speed, since the last call to this method. If this is the first call, it
+     * will return 1 for each value.
      */
     public long[] getLastCpuSpeedTimes() {
         if (mCpuSpeedTimes == null) {