Cumulative wakelock time per uid

Currently, the time for each partial wakelock was tracked. If one
wants the total time that a uid held partial wakelocks (over all of its
wakelocks), they could sum over all the uid's partial wakelock
totalTimes, but this would underestimate the value because totalTimes
are pooled amongst all uids. Alternatively, they could sum over all the
uid's partial wakelock totalDurations, but this would overestimate the
value because totalDurations are not pooled within the uid (so there
will be double-counting if two wakelocks are held simultaneously). This
cl adds a new timer that specifically tracks the actual total time that
the uid spent holding wakelocks, and also provides the ability to see
how much time the uid was in the background when doing so.

Bug: 38198272
Test: runtest -x frameworks/base/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java

Change-Id: I20ea3546338c44ffdf06859bc840d9059fb18321
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 3eea72d..499d6bb 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -157,6 +157,11 @@
     public static final int BLUETOOTH_SCAN_ON = 19;
 
     /**
+     * A constant indicating an aggregated partial wake lock timer.
+     */
+    public static final int AGGREGATED_WAKE_TYPE_PARTIAL = 20;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -185,6 +190,7 @@
      *   - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs, Syncs.
      * New in version 21:
      *   - Actual (not just apportioned) Wakelock time is also recorded.
+     *   - Aggregated partial wakelock time (per uid, instead of per wakelock) is recorded.
      */
     static final String CHECKIN_VERSION = "21";
 
@@ -216,6 +222,10 @@
     // window totalTime,  'w', count, current duration, max duration, total duration
     // [Currently, full and window wakelocks have durations current = max = total = -1]
     private static final String WAKELOCK_DATA = "wl";
+    // awl line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, which, "awl",
+    // cumulative partial wakelock duration, cumulative background partial wakelock duration
+    private static final String AGGREGATED_WAKELOCK_DATA = "awl";
     private static final String SYNC_DATA = "sy";
     private static final String JOB_DATA = "jb";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
@@ -485,6 +495,13 @@
         }
 
         /**
+         * The cumulative time the uid spent holding any partial wakelocks. This will generally
+         * differ from summing over the Wakelocks in getWakelockStats since the latter may have
+         * wakelocks that overlap in time (and therefore over-counts).
+         */
+        public abstract Timer getAggregatedPartialWakelockTimer();
+
+        /**
          * Returns a mapping containing sensor statistics.
          *
          * @return a Map from Integer sensor ids to Uid.Sensor objects.
@@ -495,7 +512,7 @@
          * Returns a mapping containing active process data.
          */
         public abstract SparseArray<? extends Pid> getPidStats();
-        
+
         /**
          * Returns a mapping containing process statistics.
          *
@@ -609,7 +626,7 @@
         static final String[] USER_ACTIVITY_TYPES = {
             "other", "button", "touch", "accessibility"
         };
-        
+
         public static final int NUM_USER_ACTIVITY_TYPES = 4;
 
         public abstract void noteUserActivityLocked(int type);
@@ -1259,7 +1276,7 @@
         public static final byte CMD_SHUTDOWN = 8;
 
         public byte cmd = CMD_NULL;
-        
+
         /**
          * Return whether the command code is a delta data update.
          */
@@ -1271,13 +1288,13 @@
         public byte batteryStatus;
         public byte batteryHealth;
         public byte batteryPlugType;
-        
+
         public short batteryTemperature;
         public char batteryVoltage;
 
         // The charge of the battery in micro-Ampere-hours.
         public int batteryChargeUAh;
-        
+
         // Constants from SCREEN_BRIGHTNESS_*
         public static final int STATE_BRIGHTNESS_SHIFT = 0;
         public static final int STATE_BRIGHTNESS_MASK = 0x7;
@@ -1458,13 +1475,13 @@
 
         public HistoryItem() {
         }
-        
+
         public HistoryItem(long time, Parcel src) {
             this.time = time;
             numReadInts = 2;
             readFromParcel(src);
         }
-        
+
         public int describeContents() {
             return 0;
         }
@@ -1560,7 +1577,7 @@
             eventCode = EVENT_NONE;
             eventTag = null;
         }
-        
+
         public void setTo(HistoryItem o) {
             time = o.time;
             cmd = o.cmd;
@@ -1714,7 +1731,7 @@
         public final String shortName;
         public final String[] values;
         public final String[] shortValues;
-        
+
         public BitDescription(int mask, String name, String shortName) {
             this.mask = mask;
             this.shift = -1;
@@ -1723,7 +1740,7 @@
             this.values = null;
             this.shortValues = null;
         }
-        
+
         public BitDescription(int mask, int shift, String name, String shortName,
                 String[] values, String[] shortValues) {
             this.mask = mask;
@@ -1771,20 +1788,20 @@
      * Return the base time offset for the battery history.
      */
     public abstract long getHistoryBaseTime();
-    
+
     /**
      * Returns the number of times the device has been started.
      */
     public abstract int getStartCount();
-    
+
     /**
      * Returns the time in microseconds that the screen has been on while the device was
      * running on battery.
-     * 
+     *
      * {@hide}
      */
     public abstract long getScreenOnTime(long elapsedRealtimeUs, int which);
-    
+
     /**
      * Returns the number of times the screen was turned on.
      *
@@ -1799,11 +1816,11 @@
     public static final int SCREEN_BRIGHTNESS_MEDIUM = 2;
     public static final int SCREEN_BRIGHTNESS_LIGHT = 3;
     public static final int SCREEN_BRIGHTNESS_BRIGHT = 4;
-    
+
     static final String[] SCREEN_BRIGHTNESS_NAMES = {
         "dark", "dim", "medium", "light", "bright"
     };
-    
+
     static final String[] SCREEN_BRIGHTNESS_SHORT_NAMES = {
         "0", "1", "2", "3", "4"
     };
@@ -1813,7 +1830,7 @@
     /**
      * Returns the time in microseconds that the screen has been on with
      * the given brightness
-     * 
+     *
      * {@hide}
      */
     public abstract long getScreenBrightnessTime(int brightnessBin,
@@ -1897,11 +1914,11 @@
     /**
      * Returns the time in microseconds that the phone has been on while the device was
      * running on battery.
-     * 
+     *
      * {@hide}
      */
     public abstract long getPhoneOnTime(long elapsedRealtimeUs, int which);
-    
+
     /**
      * Returns the number of times a phone call was activated.
      *
@@ -1912,7 +1929,7 @@
     /**
      * Returns the time in microseconds that the phone has been running with
      * the given signal strength.
-     * 
+     *
      * {@hide}
      */
     public abstract long getPhoneSignalStrengthTime(int strengthBin,
@@ -1929,7 +1946,7 @@
 
     /**
      * Returns the number of times the phone has entered the given signal strength.
-     * 
+     *
      * {@hide}
      */
     public abstract int getPhoneSignalStrengthCount(int strengthBin, int which);
@@ -1997,13 +2014,13 @@
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
         "ehrpd", "hspap", "other"
     };
-    
+
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
-    
+
     /**
      * Returns the time in microseconds that the phone has been running with
      * the given data connection.
-     * 
+     *
      * {@hide}
      */
     public abstract long getPhoneDataConnectionTime(int dataType,
@@ -2012,7 +2029,7 @@
     /**
      * Returns the number of times the phone has entered the given data
      * connection type.
-     * 
+     *
      * {@hide}
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
@@ -2131,7 +2148,7 @@
     /**
      * Returns the time in microseconds that wifi has been on while the device was
      * running on battery.
-     * 
+     *
      * {@hide}
      */
     public abstract long getWifiOnTime(long elapsedRealtimeUs, int which);
@@ -2322,7 +2339,7 @@
      * Return whether we are currently running on battery.
      */
     public abstract boolean getIsOnBattery();
-    
+
     /**
      * Returns a SparseArray containing the statistics for each uid.
      */
@@ -2341,13 +2358,13 @@
      * @param curTime the amount of elapsed realtime in microseconds.
      */
     public abstract long getBatteryRealtime(long curTime);
-    
+
     /**
      * Returns the battery percentage level at the last time the device was unplugged from power, or
-     * the last time it booted on battery power. 
+     * the last time it booted on battery power.
      */
     public abstract int getDischargeStartLevel();
-    
+
     /**
      * Returns the current battery percentage level if we are in a discharge cycle, otherwise
      * returns the level at the last plug event.
@@ -2643,7 +2660,7 @@
 
     final String formatBytesLocked(long bytes) {
         mFormatBuilder.setLength(0);
-        
+
         if (bytes < BYTES_PER_KB) {
             return bytes + "B";
         } else if (bytes < BYTES_PER_MB) {
@@ -2773,10 +2790,10 @@
         }
         return false;
     }
-    
+
     /**
      * Checkin version of wakelock printer. Prints simple comma-separated list.
-     * 
+     *
      * @param sb a StringBuilder object.
      * @param timer a Timer object contining the wakelock times.
      * @param elapsedRealtimeUs the current time in microseconds.
@@ -2831,13 +2848,13 @@
 
     /**
      * Dump a comma-separated line of values for terse checkin mode.
-     * 
+     *
      * @param pw the PageWriter to dump log to
      * @param category category of data (e.g. "total", "last", "unplugged", "current" )
      * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" ,  "process", "network")
      * @param args type-dependent data arguments
      */
-    private static final void dumpLine(PrintWriter pw, int uid, String category, String type, 
+    private static final void dumpLine(PrintWriter pw, int uid, String category, String type,
            Object... args ) {
         dumpLineHeader(pw, uid, category, type);
         for (Object arg : args) {
@@ -3014,7 +3031,7 @@
 
     /**
      * Checkin server version of dump to produce more compact, computer-readable log.
-     * 
+     *
      * NOTE: all times are expressed in 'ms'.
      */
     public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
@@ -3047,10 +3064,10 @@
                 .getCountLocked(which);
 
         final StringBuilder sb = new StringBuilder(128);
-        
+
         final SparseArray<? extends Uid> uidStats = getUidStats();
         final int NU = uidStats.size();
-        
+
         final String category = STAT_NAMES[which];
 
         // Dump "battery" stat
@@ -3063,11 +3080,11 @@
                 getEstimatedBatteryCapacity(),
                 getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity());
 
-        
+
         // Calculate wakelock times across all uids.
         long fullWakeLockTimeTotal = 0;
         long partialWakeLockTimeTotal = 0;
-        
+
         for (int iu = 0; iu < NU; iu++) {
             final Uid u = uidStats.valueAt(iu);
 
@@ -3138,14 +3155,14 @@
                 getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which),
                 getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT),
                 getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
-        
+
         // Dump screen brightness stats
         Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             args[i] = getScreenBrightnessTime(i, rawRealtime, which) / 1000;
         }
         dumpLine(pw, 0 /* uid */, category, SCREEN_BRIGHTNESS_DATA, args);
-        
+
         // Dump signal strength stats
         args = new Object[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -3207,7 +3224,7 @@
             dumpLine(pw, 0 /* uid */, category, BATTERY_LEVEL_DATA, getDischargeStartLevel(),
                     getDischargeCurrentLevel());
         }
-        
+
         if (which == STATS_SINCE_UNPLUGGED) {
             dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
                     getDischargeStartLevel()-getDischargeCurrentLevel(),
@@ -3221,7 +3238,7 @@
                     getDischargeAmountScreenOffSinceCharge(),
                     dischargeCount / 1000, dischargeScreenOffCount / 1000);
         }
-        
+
         if (reqUid < 0) {
             final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
@@ -3244,7 +3261,7 @@
                 }
             }
         }
-        
+
         final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
         helper.create(this);
         helper.refreshStats(which, UserHandle.USER_ALL);
@@ -3434,7 +3451,17 @@
                     dumpLine(pw, uid /* uid */, category, USER_ACTIVITY_DATA, args);
                 }
             }
-            
+
+            if (u.getAggregatedPartialWakelockTimer() != null) {
+                final Timer timer = u.getAggregatedPartialWakelockTimer();
+                // Convert from microseconds to milliseconds with rounding
+                final long totTimeMs = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTimeMs = bgTimer != null ?
+                        (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : 0;
+                dumpLine(pw, uid, category, AGGREGATED_WAKELOCK_DATA, totTimeMs, bgTimeMs);
+            }
+
             final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
             for (int iw=wakelocks.size()-1; iw>=0; iw--) {
                 final Uid.Wakelock wl = wakelocks.valueAt(iw);
@@ -3666,7 +3693,7 @@
         final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
 
         final StringBuilder sb = new StringBuilder(128);
-        
+
         final SparseArray<? extends Uid> uidStats = getUidStats();
         final int NU = uidStats.size();
 
@@ -3932,7 +3959,7 @@
                 }
             }
         }
-        
+
         final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
         final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
         final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
@@ -4161,15 +4188,15 @@
         if (which == STATS_SINCE_UNPLUGGED) {
             if (getIsOnBattery()) {
                 pw.print(prefix); pw.println("  Device is currently unplugged");
-                pw.print(prefix); pw.print("    Discharge cycle start level: "); 
+                pw.print(prefix); pw.print("    Discharge cycle start level: ");
                         pw.println(getDischargeStartLevel());
                 pw.print(prefix); pw.print("    Discharge cycle current level: ");
                         pw.println(getDischargeCurrentLevel());
             } else {
                 pw.print(prefix); pw.println("  Device is currently plugged into power");
-                pw.print(prefix); pw.print("    Last discharge cycle start level: "); 
+                pw.print(prefix); pw.print("    Last discharge cycle start level: ");
                         pw.println(getDischargeStartLevel());
-                pw.print(prefix); pw.print("    Last discharge cycle end level: "); 
+                pw.print(prefix); pw.print("    Last discharge cycle end level: ");
                         pw.println(getDischargeCurrentLevel());
             }
             pw.print(prefix); pw.print("    Amount discharged while screen on: ");
@@ -4450,7 +4477,7 @@
             if (reqUid >= 0 && uid != reqUid && uid != Process.SYSTEM_UID) {
                 continue;
             }
-            
+
             final Uid u = uidStats.valueAt(iu);
 
             pw.print(prefix);
@@ -4538,7 +4565,7 @@
                         formatTimeMs(sb, uidWifiRunningTime / 1000);
                         sb.append("("); sb.append(formatRatioLocked(uidWifiRunningTime,
                                 whichBatteryRealtime)); sb.append(")\n");
-                sb.append(prefix); sb.append("    Full Wifi Lock: "); 
+                sb.append(prefix); sb.append("    Full Wifi Lock: ");
                         formatTimeMs(sb, fullWifiLockOnTime / 1000);
                         sb.append("("); sb.append(formatRatioLocked(fullWifiLockOnTime,
                                 whichBatteryRealtime)); sb.append(")\n");
@@ -4690,8 +4717,23 @@
                         rawRealtime, which);
             }
             if (countWakelock > 1) {
-                if (totalFullWakelock != 0 || totalPartialWakelock != 0
-                        || totalWindowWakelock != 0) {
+                // get unpooled partial wakelock quantities (unlike totalPartialWakelock, which is
+                // pooled and therefore just a lower bound)
+                long actualTotalPartialWakelock = 0;
+                long actualBgPartialWakelock = 0;
+                if (u.getAggregatedPartialWakelockTimer() != null) {
+                    final Timer aggTimer = u.getAggregatedPartialWakelockTimer();
+                    // Convert from microseconds to milliseconds with rounding
+                    actualTotalPartialWakelock =
+                            (aggTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    final Timer bgAggTimer = aggTimer.getSubTimer();
+                    actualBgPartialWakelock = bgAggTimer != null ?
+                            (bgAggTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : 0;
+                }
+
+                if (actualTotalPartialWakelock != 0 || actualBgPartialWakelock != 0 ||
+                        totalFullWakelock != 0 || totalPartialWakelock != 0 ||
+                        totalWindowWakelock != 0) {
                     sb.setLength(0);
                     sb.append(prefix);
                     sb.append("    TOTAL wake: ");
@@ -4707,7 +4749,23 @@
                         }
                         needComma = true;
                         formatTimeMs(sb, totalPartialWakelock);
-                        sb.append("partial");
+                        sb.append("blamed partial");
+                    }
+                    if (actualTotalPartialWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, actualTotalPartialWakelock);
+                        sb.append("actual partial");
+                    }
+                    if (actualBgPartialWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, actualBgPartialWakelock);
+                        sb.append("actual background partial");
                     }
                     if (totalWindowWakelock != 0) {
                         if (needComma) {