Merge "incidentd: dumping batterystats app data to proto"
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5995696..8682c01 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -447,8 +447,7 @@
 
         /**
          * Returns the max duration if it is being tracked.
-         * Not all Timer subclasses track the max, total, current durations.
-
+         * Not all Timer subclasses track the max, total, and current durations.
          */
         public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
             return -1;
@@ -456,14 +455,14 @@
 
         /**
          * Returns the current time the timer has been active, if it is being tracked.
-         * Not all Timer subclasses track the max, total, current durations.
+         * Not all Timer subclasses track the max, total, and current durations.
          */
         public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
             return -1;
         }
 
         /**
-         * Returns the current time the timer has been active, if it is being tracked.
+         * Returns the total time the timer has been active, if it is being tracked.
          *
          * Returns the total cumulative duration (i.e. sum of past durations) that this timer has
          * been on since reset.
@@ -471,7 +470,7 @@
          * depending on the Timer, getTotalTimeLocked may represent the total 'blamed' or 'pooled'
          * time, rather than the actual time. By contrast, getTotalDurationMsLocked always gives
          * the actual total time.
-         * Not all Timer subclasses track the max, total, current durations.
+         * Not all Timer subclasses track the max, total, and current durations.
          */
         public long getTotalDurationMsLocked(long elapsedRealtimeMs) {
             return -1;
@@ -600,9 +599,17 @@
         public abstract long getFullWifiLockTime(long elapsedRealtimeUs, int which);
         public abstract long getWifiScanTime(long elapsedRealtimeUs, int which);
         public abstract int getWifiScanCount(int which);
+        /**
+         * Returns the timer keeping track of wifi scans.
+         */
+        public abstract Timer getWifiScanTimer();
         public abstract int getWifiScanBackgroundCount(int which);
         public abstract long getWifiScanActualTime(long elapsedRealtimeUs);
         public abstract long getWifiScanBackgroundTime(long elapsedRealtimeUs);
+        /**
+         * Returns the timer keeping track of background wifi scans.
+         */
+        public abstract Timer getWifiScanBackgroundTimer();
         public abstract long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which);
         public abstract int getWifiBatchedScanCount(int csphBin, int which);
         public abstract long getWifiMulticastTime(long elapsedRealtimeUs, int which);
@@ -2824,6 +2831,10 @@
         }
     }
 
+    private static long roundUsToMs(long timeUs) {
+        return (timeUs + 500) / 1000;
+    }
+
     private static long computeWakeLock(Timer timer, long elapsedRealtimeUs, int which) {
         if (timer != null) {
             // Convert from microseconds to milliseconds with rounding
@@ -3028,8 +3039,7 @@
                                         Timer timer, long rawRealtime, int which) {
         if (timer != null) {
             // Convert from microseconds to milliseconds with rounding
-            final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
-                    / 1000;
+            final long totalTime = roundUsToMs(timer.getTotalTimeLocked(rawRealtime, which));
             final int count = timer.getCountLocked(which);
             if (totalTime != 0 || count != 0) {
                 dumpLine(pw, uid, category, type, totalTime, count);
@@ -3052,12 +3062,26 @@
             return;
         }
         // Convert from microseconds to milliseconds with rounding
-        final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000;
+        final long timeMs = roundUsToMs(timer.getTotalTimeLocked(rawRealtimeUs, which));
         final int count = timer.getCountLocked(which);
-        if (totalTimeMs != 0 || count != 0) {
+        final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs / 1000);
+        final long curDurationMs = timer.getCurrentDurationMsLocked(rawRealtimeUs / 1000);
+        final long totalDurationMs = timer.getTotalDurationMsLocked(rawRealtimeUs / 1000);
+        if (timeMs != 0 || count != 0 || maxDurationMs != -1 || curDurationMs != -1
+                || totalDurationMs != -1) {
             final long token = proto.start(fieldId);
-            proto.write(TimerProto.DURATION_MS, totalTimeMs);
+            proto.write(TimerProto.DURATION_MS, timeMs);
             proto.write(TimerProto.COUNT, count);
+            // These values will be -1 for timers that don't implement the functionality.
+            if (maxDurationMs != -1) {
+                proto.write(TimerProto.MAX_DURATION_MS, maxDurationMs);
+            }
+            if (curDurationMs != -1) {
+                proto.write(TimerProto.CURRENT_DURATION_MS, curDurationMs);
+            }
+            if (totalDurationMs != -1) {
+                proto.write(TimerProto.TOTAL_DURATION_MS, totalDurationMs);
+            }
             proto.end(token);
         }
     }
@@ -3770,7 +3794,7 @@
                 linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
                         rawRealtime, "w", which, linePrefix);
 
-                // Only log if we had at lease one wakelock...
+                // Only log if we had at least one wakelock...
                 if (sb.length() > 0) {
                     String name = wakelocks.keyAt(iw);
                     if (name.indexOf(',') >= 0) {
@@ -6126,9 +6150,8 @@
             return;
         }
         int count = steps.mNumStepDurations;
-        long token;
         for (int i = 0; i < count; ++i) {
-            token = proto.start(fieldId);
+            long token = proto.start(fieldId);
             proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
             proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
 
@@ -6621,15 +6644,443 @@
         }
 
         if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
-            // TODO: implement dumpProtoAppsLocked(proto, apps);
-            dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+            final BatteryStatsHelper helper = new BatteryStatsHelper(context, false,
+                    (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+            helper.create(this);
+            helper.refreshStats(STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+
+            dumpProtoAppsLocked(proto, helper, apps);
+            dumpProtoSystemLocked(proto, helper);
         }
 
         proto.end(bToken);
         proto.flush();
     }
 
-    private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) {
+    private void dumpProtoAppsLocked(ProtoOutputStream proto, BatteryStatsHelper helper,
+            List<ApplicationInfo> apps) {
+        final int which = STATS_SINCE_CHARGED;
+        final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtimeUs = rawRealtimeMs * 1000;
+        final long batteryUptimeUs = getBatteryUptime(rawUptimeUs);
+
+        SparseArray<ArrayList<String>> aidToPackages = new SparseArray<>();
+        if (apps != null) {
+            for (int i = 0; i < apps.size(); ++i) {
+                ApplicationInfo ai = apps.get(i);
+                int aid = UserHandle.getAppId(ai.uid);
+                ArrayList<String> pkgs = aidToPackages.get(aid);
+                if (pkgs == null) {
+                    pkgs = new ArrayList<String>();
+                    aidToPackages.put(aid, pkgs);
+                }
+                pkgs.add(ai.packageName);
+            }
+        }
+
+        SparseArray<BatterySipper> uidToSipper = new SparseArray<>();
+        final List<BatterySipper> sippers = helper.getUsageList();
+        if (sippers != null) {
+            for (int i = 0; i < sippers.size(); ++i) {
+                final BatterySipper bs = sippers.get(i);
+                if (bs.drainType != BatterySipper.DrainType.APP) {
+                    // Others are handled by dumpProtoSystemLocked()
+                    continue;
+                }
+                uidToSipper.put(bs.uidObj.getUid(), bs);
+            }
+        }
+
+        SparseArray<? extends Uid> uidStats = getUidStats();
+        final int n = uidStats.size();
+        for (int iu = 0; iu < n; ++iu) {
+            final long uTkn = proto.start(BatteryStatsProto.UIDS);
+            final Uid u = uidStats.valueAt(iu);
+
+            final int uid = uidStats.keyAt(iu);
+            proto.write(UidProto.UID, uid);
+
+            // Print packages and apk stats (UID_DATA & APK_DATA)
+            ArrayList<String> pkgs = aidToPackages.get(UserHandle.getAppId(uid));
+            if (pkgs == null) {
+                pkgs = new ArrayList<String>();
+            }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats =
+                    u.getPackageStats();
+            for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+                String pkg = packageStats.keyAt(ipkg);
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats =
+                        packageStats.valueAt(ipkg).getServiceStats();
+                if (serviceStats.size() == 0) {
+                    // Due to the way ActivityManagerService logs wakeup alarms, some packages (for
+                    // example, "android") may be included in the packageStats that aren't part of
+                    // the UID. If they don't have any services, then they shouldn't be listed here.
+                    // These packages won't be a part in the pkgs List.
+                    continue;
+                }
+
+                final long pToken = proto.start(UidProto.PACKAGES);
+                proto.write(UidProto.Package.NAME, pkg);
+                // Remove from the packages list since we're logging it here.
+                pkgs.remove(pkg);
+
+                for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
+                    final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+                    long sToken = proto.start(UidProto.Package.SERVICES);
+
+                    proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
+                    proto.write(UidProto.Package.Service.START_DURATION_MS,
+                            roundUsToMs(ss.getStartTime(batteryUptimeUs, which)));
+                    proto.write(UidProto.Package.Service.START_COUNT, ss.getStarts(which));
+                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, ss.getLaunches(which));
+
+                    proto.end(sToken);
+                }
+                proto.end(pToken);
+            }
+            // Print any remaining packages that weren't in the packageStats map. pkgs is pulled
+            // from PackageManager data. Packages are only included in packageStats if there was
+            // specific data tracked for them (services and wakeup alarms, etc.).
+            for (String p : pkgs) {
+                final long pToken = proto.start(UidProto.PACKAGES);
+                proto.write(UidProto.Package.NAME, p);
+                proto.end(pToken);
+            }
+
+            // Total wakelock data (AGGREGATED_WAKELOCK_DATA)
+            if (u.getAggregatedPartialWakelockTimer() != null) {
+                final Timer timer = u.getAggregatedPartialWakelockTimer();
+                // Times are since reset (regardless of 'which')
+                final long totTimeMs = timer.getTotalDurationMsLocked(rawRealtimeMs);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTimeMs = bgTimer != null
+                        ? bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                final long awToken = proto.start(UidProto.AGGREGATED_WAKELOCK);
+                proto.write(UidProto.AggregatedWakelock.PARTIAL_DURATION_MS, totTimeMs);
+                proto.write(UidProto.AggregatedWakelock.BACKGROUND_PARTIAL_DURATION_MS, bgTimeMs);
+                proto.end(awToken);
+            }
+
+            // Audio (AUDIO_DATA)
+            dumpTimer(proto, UidProto.AUDIO, u.getAudioTurnedOnTimer(), rawRealtimeUs, which);
+
+            // Bluetooth Controller (BLUETOOTH_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.BLUETOOTH_CONTROLLER,
+                    u.getBluetoothControllerActivity(), which);
+
+            // BLE scans (BLUETOOTH_MISC_DATA) (uses totalDurationMsLocked and MaxDurationMsLocked)
+            final Timer bleTimer = u.getBluetoothScanTimer();
+            if (bleTimer != null) {
+                final long bmToken = proto.start(UidProto.BLUETOOTH_MISC);
+
+                dumpTimer(proto, UidProto.BluetoothMisc.APPORTIONED_BLE_SCAN, bleTimer,
+                        rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN,
+                        u.getBluetoothScanBackgroundTimer(), rawRealtimeUs, which);
+                // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                dumpTimer(proto, UidProto.BluetoothMisc.UNOPTIMIZED_BLE_SCAN,
+                        u.getBluetoothUnoptimizedScanTimer(), rawRealtimeUs, which);
+                // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_UNOPTIMIZED_BLE_SCAN,
+                        u.getBluetoothUnoptimizedScanBackgroundTimer(), rawRealtimeUs, which);
+                // Result counters
+                proto.write(UidProto.BluetoothMisc.BLE_SCAN_RESULT_COUNT,
+                        u.getBluetoothScanResultCounter() != null
+                            ? u.getBluetoothScanResultCounter().getCountLocked(which) : 0);
+                proto.write(UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN_RESULT_COUNT,
+                        u.getBluetoothScanResultBgCounter() != null
+                            ? u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0);
+
+                proto.end(bmToken);
+            }
+
+            // Camera (CAMERA_DATA)
+            dumpTimer(proto, UidProto.CAMERA, u.getCameraTurnedOnTimer(), rawRealtimeUs, which);
+
+            // CPU stats (CPU_DATA & CPU_TIMES_AT_FREQ_DATA)
+            final long cpuToken = proto.start(UidProto.CPU);
+            proto.write(UidProto.Cpu.USER_DURATION_MS, roundUsToMs(u.getUserCpuTimeUs(which)));
+            proto.write(UidProto.Cpu.SYSTEM_DURATION_MS, roundUsToMs(u.getSystemCpuTimeUs(which)));
+
+            final long[] cpuFreqs = getCpuFreqs();
+            if (cpuFreqs != null) {
+                final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
+                // If total cpuFreqTimes is null, then we don't need to check for
+                // screenOffCpuFreqTimes.
+                if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+                    long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
+                    if (screenOffCpuFreqTimeMs == null) {
+                        screenOffCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
+                    }
+                    for (int ic = 0; ic < cpuFreqTimeMs.length; ++ic) {
+                        long cToken = proto.start(UidProto.Cpu.BY_FREQUENCY);
+                        proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
+                        proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
+                                cpuFreqTimeMs[ic]);
+                        proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
+                                screenOffCpuFreqTimeMs[ic]);
+                        proto.end(cToken);
+                    }
+                }
+            }
+            proto.end(cpuToken);
+
+            // Flashlight (FLASHLIGHT_DATA)
+            dumpTimer(proto, UidProto.FLASHLIGHT, u.getFlashlightTurnedOnTimer(),
+                    rawRealtimeUs, which);
+
+            // Foreground activity (FOREGROUND_ACTIVITY_DATA)
+            dumpTimer(proto, UidProto.FOREGROUND_ACTIVITY, u.getForegroundActivityTimer(),
+                    rawRealtimeUs, which);
+
+            // Foreground service (FOREGROUND_SERVICE_DATA)
+            dumpTimer(proto, UidProto.FOREGROUND_SERVICE, u.getForegroundServiceTimer(),
+                    rawRealtimeUs, which);
+
+            // Job completion (JOB_COMPLETION_DATA)
+            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
+            final int[] reasons = new int[]{
+                JobParameters.REASON_CANCELED,
+                JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
+                JobParameters.REASON_PREEMPT,
+                JobParameters.REASON_TIMEOUT,
+                JobParameters.REASON_DEVICE_IDLE,
+            };
+            for (int ic = 0; ic < completions.size(); ++ic) {
+                SparseIntArray types = completions.valueAt(ic);
+                if (types != null) {
+                    final long jcToken = proto.start(UidProto.JOB_COMPLETION);
+
+                    proto.write(UidProto.JobCompletion.NAME, completions.keyAt(ic));
+
+                    for (int r : reasons) {
+                        long rToken = proto.start(UidProto.JobCompletion.REASON_COUNT);
+                        proto.write(UidProto.JobCompletion.ReasonCount.NAME, r);
+                        proto.write(UidProto.JobCompletion.ReasonCount.COUNT, types.get(r, 0));
+                        proto.end(rToken);
+                    }
+
+                    proto.end(jcToken);
+                }
+            }
+
+            // Scheduled jobs (JOB_DATA)
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij = jobs.size() - 1; ij >= 0; --ij) {
+                final Timer timer = jobs.valueAt(ij);
+                final Timer bgTimer = timer.getSubTimer();
+                final long jToken = proto.start(UidProto.JOBS);
+
+                proto.write(UidProto.Job.NAME, jobs.keyAt(ij));
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Job.TOTAL, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Job.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(jToken);
+            }
+
+            // Modem Controller (MODEM_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.MODEM_CONTROLLER,
+                    u.getModemControllerActivity(), which);
+
+            // Network stats (NETWORK_DATA)
+            final long nToken = proto.start(UidProto.NETWORK);
+            proto.write(UidProto.Network.MOBILE_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+            proto.write(UidProto.Network.BT_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+            proto.write(UidProto.Network.BT_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_RX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_TX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_RX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_TX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_ACTIVE_DURATION_MS,
+                    roundUsToMs(u.getMobileRadioActiveTime(which)));
+            proto.write(UidProto.Network.MOBILE_ACTIVE_COUNT,
+                    u.getMobileRadioActiveCount(which));
+            proto.write(UidProto.Network.MOBILE_WAKEUP_COUNT,
+                    u.getMobileRadioApWakeupCount(which));
+            proto.write(UidProto.Network.WIFI_WAKEUP_COUNT,
+                    u.getWifiRadioApWakeupCount(which));
+            proto.write(UidProto.Network.MOBILE_BYTES_BG_RX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_BG_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_BYTES_BG_TX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_BG_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_BG_RX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_BG_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_BG_TX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_BG_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_BG_RX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_BG_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_BG_TX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_BG_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_BG_RX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_BG_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_BG_TX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_BG_TX_DATA, which));
+            proto.end(nToken);
+
+            // Power use item (POWER_USE_ITEM_DATA)
+            BatterySipper bs = uidToSipper.get(uid);
+            if (bs != null) {
+                final long bsToken = proto.start(UidProto.POWER_USE_ITEM);
+                proto.write(UidProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
+                proto.write(UidProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide);
+                proto.write(UidProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
+                proto.write(UidProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
+                        bs.proportionalSmearMah);
+                proto.end(bsToken);
+            }
+
+            // Processes (PROCESS_DATA)
+            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats =
+                    u.getProcessStats();
+            for (int ipr = processStats.size() - 1; ipr >= 0; --ipr) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
+                final long prToken = proto.start(UidProto.PROCESS);
+
+                proto.write(UidProto.Process.NAME, processStats.keyAt(ipr));
+                proto.write(UidProto.Process.USER_DURATION_MS, ps.getUserTime(which));
+                proto.write(UidProto.Process.SYSTEM_DURATION_MS, ps.getSystemTime(which));
+                proto.write(UidProto.Process.FOREGROUND_DURATION_MS, ps.getForegroundTime(which));
+                proto.write(UidProto.Process.START_COUNT, ps.getStarts(which));
+                proto.write(UidProto.Process.ANR_COUNT, ps.getNumAnrs(which));
+                proto.write(UidProto.Process.CRASH_COUNT, ps.getNumCrashes(which));
+
+                proto.end(prToken);
+            }
+
+            // Sensors (SENSOR_DATA)
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            for (int ise = 0; ise < sensors.size(); ++ise) {
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final Timer timer = se.getSensorTime();
+                if (timer == null) {
+                    continue;
+                }
+                final Timer bgTimer = se.getSensorBackgroundTime();
+                final int sensorNumber = sensors.keyAt(ise);
+                final long seToken = proto.start(UidProto.SENSORS);
+
+                proto.write(UidProto.Sensor.ID, sensorNumber);
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Sensor.APPORTIONED, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Sensor.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(seToken);
+            }
+
+            // State times (STATE_TIME_DATA)
+            for (int ips = 0; ips < Uid.NUM_PROCESS_STATE; ++ips) {
+                long durMs = roundUsToMs(u.getProcessStateTime(ips, rawRealtimeUs, which));
+                if (durMs == 0) {
+                    continue;
+                }
+                final long stToken = proto.start(UidProto.STATES);
+                proto.write(UidProto.StateTime.STATE, ips);
+                proto.write(UidProto.StateTime.DURATION_MS, durMs);
+                proto.end(stToken);
+            }
+
+            // Syncs (SYNC_DATA)
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy = syncs.size() - 1; isy >= 0; --isy) {
+                final Timer timer = syncs.valueAt(isy);
+                final Timer bgTimer = timer.getSubTimer();
+                final long syToken = proto.start(UidProto.SYNCS);
+
+                proto.write(UidProto.Sync.NAME, syncs.keyAt(isy));
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Sync.TOTAL, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Sync.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(syToken);
+            }
+
+            // User activity (USER_ACTIVITY_DATA)
+            if (u.hasUserActivity()) {
+                for (int i = 0; i < Uid.NUM_USER_ACTIVITY_TYPES; ++i) {
+                    int val = u.getUserActivityCount(i, which);
+                    if (val != 0) {
+                        final long uaToken = proto.start(UidProto.USER_ACTIVITY);
+                        proto.write(UidProto.UserActivity.NAME, i);
+                        proto.write(UidProto.UserActivity.COUNT, val);
+                        proto.end(uaToken);
+                    }
+                }
+            }
+
+            // Vibrator (VIBRATOR_DATA)
+            dumpTimer(proto, UidProto.VIBRATOR, u.getVibratorOnTimer(), rawRealtimeUs, which);
+
+            // Video (VIDEO_DATA)
+            dumpTimer(proto, UidProto.VIDEO, u.getVideoTurnedOnTimer(), rawRealtimeUs, which);
+
+            // Wakelocks (WAKELOCK_DATA)
+            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);
+                final long wToken = proto.start(UidProto.WAKELOCKS);
+                proto.write(UidProto.Wakelock.NAME, wakelocks.keyAt(iw));
+                dumpTimer(proto, UidProto.Wakelock.FULL, wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtimeUs, which);
+                final Timer pTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (pTimer != null) {
+                    dumpTimer(proto, UidProto.Wakelock.PARTIAL, pTimer, rawRealtimeUs, which);
+                    dumpTimer(proto, UidProto.Wakelock.BACKGROUND_PARTIAL, pTimer.getSubTimer(),
+                            rawRealtimeUs, which);
+                }
+                dumpTimer(proto, UidProto.Wakelock.WINDOW, wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtimeUs, which);
+                proto.end(wToken);
+            }
+
+            // Wakeup alarms (WAKEUP_ALARM_DATA)
+            for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa = alarms.size() - 1; iwa >= 0; --iwa) {
+                    final long waToken = proto.start(UidProto.WAKEUP_ALARM);
+                    proto.write(UidProto.WakeupAlarm.NAME, alarms.keyAt(iwa));
+                    proto.write(UidProto.WakeupAlarm.COUNT,
+                            alarms.valueAt(iwa).getCountLocked(which));
+                    proto.end(waToken);
+                }
+            }
+
+            // Wifi Controller (WIFI_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.WIFI_CONTROLLER,
+                    u.getWifiControllerActivity(), which);
+
+            // Wifi data (WIFI_DATA)
+            final long wToken = proto.start(UidProto.WIFI);
+            proto.write(UidProto.Wifi.FULL_WIFI_LOCK_DURATION_MS,
+                    roundUsToMs(u.getFullWifiLockTime(rawRealtimeUs, which)));
+            dumpTimer(proto, UidProto.Wifi.APPORTIONED_SCAN, u.getWifiScanTimer(),
+                    rawRealtimeUs, which);
+            proto.write(UidProto.Wifi.RUNNING_DURATION_MS,
+                    roundUsToMs(u.getWifiRunningTime(rawRealtimeUs, which)));
+            dumpTimer(proto, UidProto.Wifi.BACKGROUND_SCAN, u.getWifiScanBackgroundTimer(),
+                    rawRealtimeUs, which);
+            proto.end(wToken);
+
+            proto.end(uTkn);
+        }
+    }
+
+    private void dumpProtoSystemLocked(ProtoOutputStream proto, BatteryStatsHelper helper) {
         final long sToken = proto.start(BatteryStatsProto.SYSTEM);
         final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
         final long rawRealtimeMs = SystemClock.elapsedRealtime();
@@ -6637,7 +7088,7 @@
         final int which = STATS_SINCE_CHARGED;
 
         // Battery data (BATTERY_DATA)
-        long token = proto.start(SystemProto.BATTERY);
+        final long bToken = proto.start(SystemProto.BATTERY);
         proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
         proto.write(SystemProto.Battery.START_COUNT, getStartCount());
         proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
@@ -6660,10 +7111,10 @@
                 getMinLearnedBatteryCapacity());
         proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
                 getMaxLearnedBatteryCapacity());
-        proto.end(token);
+        proto.end(bToken);
 
         // Battery discharge (BATTERY_DISCHARGE_DATA)
-        token = proto.start(SystemProto.BATTERY_DISCHARGE);
+        final long bdToken = proto.start(SystemProto.BATTERY_DISCHARGE);
         proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
                 getLowDischargeAmountSinceCharge());
         proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
@@ -6680,10 +7131,11 @@
                 getUahDischargeScreenOff(which) / 1000);
         proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
                 getUahDischargeScreenDoze(which) / 1000);
-        proto.end(token);
+        proto.end(bdToken);
 
         // Time remaining
         long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
+        // These are part of a oneof, so we should only set one of them.
         if (timeRemainingUs >= 0) {
             // Charge time remaining (CHARGE_TIME_REMAIN_DATA)
             proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
@@ -6702,11 +7154,11 @@
 
         // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
         for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
-            token = proto.start(SystemProto.DATA_CONNECTION);
+            final long pdcToken = proto.start(SystemProto.DATA_CONNECTION);
             proto.write(SystemProto.DataConnection.NAME, i);
             dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(pdcToken);
         }
 
         // Discharge step (DISCHARGE_STEP_DATA)
@@ -6729,7 +7181,7 @@
                 getModemControllerActivity(), which);
 
         // Global network data (GLOBAL_NETWORK_DATA)
-        token = proto.start(SystemProto.GLOBAL_NETWORK);
+        final long gnToken = proto.start(SystemProto.GLOBAL_NETWORK);
         proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
                 getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
         proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
@@ -6750,7 +7202,7 @@
                 getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
         proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
                 getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
-        proto.end(token);
+        proto.end(gnToken);
 
         // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
         dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
@@ -6758,21 +7210,21 @@
 
 
         // Global wifi (GLOBAL_WIFI_DATA)
-        token = proto.start(SystemProto.GLOBAL_WIFI);
+        final long gwToken = proto.start(SystemProto.GLOBAL_WIFI);
         proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
                 getWifiOnTime(rawRealtimeUs, which) / 1000);
         proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
                 getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
-        proto.end(token);
+        proto.end(gwToken);
 
         // Kernel wakelock (KERNEL_WAKELOCK_DATA)
         final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
         for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
-            token = proto.start(SystemProto.KERNEL_WAKELOCK);
+            final long kwToken = proto.start(SystemProto.KERNEL_WAKELOCK);
             proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
             dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(kwToken);
         }
 
         // Misc (MISC_DATA)
@@ -6802,7 +7254,7 @@
                 }
             }
         }
-        token = proto.start(SystemProto.MISC);
+        final long mToken = proto.start(SystemProto.MISC);
         proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
                 getScreenOnTime(rawRealtimeUs, which) / 1000);
         proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
@@ -6845,11 +7297,7 @@
                 getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
         proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
                 getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
-        proto.end(token);
-
-        final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
-        helper.create(this);
-        helper.refreshStats(which, UserHandle.USER_ALL);
+        proto.end(mToken);
 
         // Power use item (POWER_USE_ITEM_DATA)
         final List<BatterySipper> sippers = helper.getUsageList();
@@ -6881,7 +7329,7 @@
                         n = SystemProto.PowerUseItem.FLASHLIGHT;
                         break;
                     case APP:
-                        // dumpProtoAppLocked will handle this.
+                        // dumpProtoAppsLocked will handle this.
                         continue;
                     case USER:
                         n = SystemProto.PowerUseItem.USER;
@@ -6900,7 +7348,7 @@
                         n = SystemProto.PowerUseItem.MEMORY;
                         break;
                 }
-                token = proto.start(SystemProto.POWER_USE_ITEM);
+                final long puiToken = proto.start(SystemProto.POWER_USE_ITEM);
                 proto.write(SystemProto.PowerUseItem.NAME, n);
                 proto.write(SystemProto.PowerUseItem.UID, uid);
                 proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
@@ -6908,39 +7356,39 @@
                 proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
                 proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
                         bs.proportionalSmearMah);
-                proto.end(token);
+                proto.end(puiToken);
             }
         }
 
         // Power use summary (POWER_USE_SUMMARY_DATA)
-        token = proto.start(SystemProto.POWER_USE_SUMMARY);
+        final long pusToken = proto.start(SystemProto.POWER_USE_SUMMARY);
         proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
                 helper.getPowerProfile().getBatteryCapacity());
         proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower());
         proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower());
         proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower());
-        proto.end(token);
+        proto.end(pusToken);
 
         // RPM stats (RESOURCE_POWER_MANAGER_DATA)
         final Map<String, ? extends Timer> rpmStats = getRpmStats();
         final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
         for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
-            token = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
+            final long rpmToken = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
             proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
             dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
                     ent.getValue(), rawRealtimeUs, which);
             dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
                     screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(rpmToken);
         }
 
         // Screen brightness (SCREEN_BRIGHTNESS_DATA)
         for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
-            token = proto.start(SystemProto.SCREEN_BRIGHTNESS);
+            final long sbToken = proto.start(SystemProto.SCREEN_BRIGHTNESS);
             proto.write(SystemProto.ScreenBrightness.NAME, i);
             dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(sbToken);
         }
 
         // Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
@@ -6949,47 +7397,47 @@
 
         // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
         for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
-            token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
+            final long pssToken = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
             proto.write(SystemProto.PhoneSignalStrength.NAME, i);
             dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(pssToken);
         }
 
         // Wakeup reasons (WAKEUP_REASON_DATA)
         final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
         for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
-            token = proto.start(SystemProto.WAKEUP_REASON);
+            final long wrToken = proto.start(SystemProto.WAKEUP_REASON);
             proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
             dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wrToken);
         }
 
         // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
         for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
-            token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
+            final long wssToken = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
             proto.write(SystemProto.WifiSignalStrength.NAME, i);
             dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wssToken);
         }
 
         // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
         for (int i = 0; i < NUM_WIFI_STATES; ++i) {
-            token = proto.start(SystemProto.WIFI_STATE);
+            final long wsToken = proto.start(SystemProto.WIFI_STATE);
             proto.write(SystemProto.WifiState.NAME, i);
             dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wsToken);
         }
 
         // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
         for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
-            token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
+            final long wssToken = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
             proto.write(SystemProto.WifiSupplicantState.NAME, i);
             dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
                     rawRealtimeUs, which);
-            proto.end(token);
+            proto.end(wssToken);
         }
 
         proto.end(sToken);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5c310b1..239d709 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6428,6 +6428,11 @@
         }
 
         @Override
+        public Timer getWifiScanTimer() {
+            return mWifiScanTimer;
+        }
+
+        @Override
         public int getWifiScanBackgroundCount(int which) {
             if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
                 return 0;
@@ -6454,6 +6459,14 @@
         }
 
         @Override
+        public Timer getWifiScanBackgroundTimer() {
+            if (mWifiScanTimer == null) {
+                return null;
+            }
+            return mWifiScanTimer.getSubTimer();
+        }
+
+        @Override
         public long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which) {
             if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
             if (mWifiBatchedScanTimer[csphBin] == null) {
diff --git a/core/proto/android/app/jobparameters.proto b/core/proto/android/app/jobparameters.proto
new file mode 100644
index 0000000..4f6a2a2
--- /dev/null
+++ b/core/proto/android/app/jobparameters.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.JobParameters object.
+ */
+message JobParametersProto {
+    enum CancelReason {
+        REASON_CANCELLED = 0;
+        REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+        REASON_PREEMPT = 2;
+        REASON_TIMEOUT = 3;
+        REASON_DEVICE_IDLE = 4;
+    }
+}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 38879c0..c33c0a0 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -19,6 +19,8 @@
 
 package android.os;
 
+import "frameworks/base/core/proto/android/app/jobparameters.proto";
+import "frameworks/base/core/proto/android/os/powermanager.proto";
 import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
 
 message BatteryStatsProto {
@@ -356,11 +358,12 @@
   optional PowerUseSummary power_use_summary = 18;
 
   message ResourcePowerManager {
+    // Either StateName or StateName.VoterName.
     optional string name = 1;
     optional TimerProto total = 2;
     optional TimerProto screen_off = 3;
   }
-  optional ResourcePowerManager resource_power_manager = 19;
+  repeated ResourcePowerManager resource_power_manager = 19;
 
   message ScreenBrightness {
     enum Name {
@@ -436,12 +439,332 @@
 }
 
 message TimerProto {
+  // This may be an apportioned time.
   optional int64 duration_ms = 1;
   optional int64 count = 2;
+  // The max duration if it is being tracked. Not all Timer subclasses
+  // track the max duration.
+  optional int64 max_duration_ms = 3;
+  // The current time the timer has been active, if it is being tracked.
+  // Not all Timer subclasses track the current duration.
+  optional int64 current_duration_ms = 4;
+  // The total cumulative duration (i.e. sum of past durations) that this timer
+  // has been on since reset. This may differ from duration_ms since, depending
+  // on the Timer, getTotalTimeLocked may represent the total 'blamed' or
+  // 'pooled' time, rather than the actual time. By contrast, total_duration_ms
+  // always gives the actual total time. Not all Timer subclasses track the
+  // total duration.
+  optional int64 total_duration_ms = 5;
 }
 
 message UidProto {
   // Combination of app ID and user ID.
   optional int32 uid = 1;
-  repeated string package_names = 2;
+
+  // The statistics associated with a particular package.
+  message Package {
+    optional string name = 1;
+
+    message Service {
+      optional string name = 1;
+      // Time spent started.
+      optional int64 start_duration_ms = 2;
+      optional int32 start_count = 3;
+      optional int32 launch_count = 4;
+    }
+    repeated Service services = 2;
+  }
+  repeated Package packages = 2;
+
+  optional ControllerActivityProto bluetooth_controller = 3;
+  optional ControllerActivityProto modem_controller = 4;
+  optional ControllerActivityProto wifi_controller = 5;
+
+  // Bluetooth misc data.
+  message BluetoothMisc {
+    // Duration spent BLE scanning blamed on this App (i.e. apportioned to this
+    // app amongst all apps doing BLE scanning; see explanation of 'apportioned'
+    // in App's comment).
+    optional TimerProto apportioned_ble_scan = 1;
+    // Background times aren't apportioned.
+    optional TimerProto background_ble_scan = 2;
+    // Running unoptimized BLE scanning, as defined by Bluetooth's
+    // AppScanStats.recordScanStart. As of May 2017, these are unfiltered,
+    // non-opportunistic, non-first-match scans. Durations are not
+    // pooled/apportioned.
+    optional TimerProto unoptimized_ble_scan = 3;
+    // Running unoptimized BLE scanning when app is in background. Durations are
+    // not pooled/apportioned.
+    optional TimerProto background_unoptimized_ble_scan = 4;
+    // Count of results returned by BLE scanning.
+    optional int32 ble_scan_result_count = 5;
+    // Count of results returned by BLE scans when app is in background.
+    // (Included in ble_scan_result_count.)
+    optional int32 background_ble_scan_result_count = 6;
+  }
+  optional BluetoothMisc bluetooth_misc = 6;
+
+  message Cpu {
+    // Total CPU time with processes executing in userspace. Summed up across
+    // multiple cores.
+    optional int64 user_duration_ms = 1;
+    // Total CPU time with processes executing kernel syscalls. Summed up across
+    // multiple cores.
+    optional int64 system_duration_ms = 2;
+
+    // CPU time broken down by CPU frequency (go/cpu-battery-metrics).
+    //
+    // These are real CPU time measurement from the kernel, so their sum can
+    // be different from the sum of user_duration_millis and
+    // system_duration_millis, which are just approximations. Data is not
+    // tracked when device is charging.
+    message ByFrequency {
+      // Index of the frequency in system.cpu_frequency. It starts from 1, to
+      // make it easier to analyze.
+      optional int32 frequency_index = 1;
+      // CPU time in milliseconds.
+      optional int64 total_duration_ms = 2;
+      // Screen-off CPU time in milliseconds.
+      optional int64 screen_off_duration_ms = 3;
+    }
+    repeated ByFrequency by_frequency = 3;
+  }
+  optional Cpu cpu = 7;
+
+  // Duration is pooled/apportioned.
+  optional TimerProto audio = 8;
+  // Duration is pooled/apportioned.
+  optional TimerProto camera = 9;
+  // Duration is pooled/apportioned.
+  optional TimerProto flashlight = 10;
+  // Duration is not pooled/apportioned.
+  optional TimerProto foreground_activity = 11;
+  // Duration is not pooled/apportioned.
+  optional TimerProto foreground_service = 12;
+  // Duration is not pooled/apportioned.
+  optional TimerProto vibrator = 13;
+  // Duration is pooled/apportioned.
+  optional TimerProto video = 14;
+
+  message Job {
+    optional string name = 1;
+    // Job times aren't apportioned.
+    optional TimerProto total = 2;
+    optional TimerProto background = 3;
+  }
+  repeated Job jobs = 15;
+
+  message JobCompletion {
+    // Job name.
+    optional string name = 1;
+
+    message ReasonCount {
+      optional android.app.JobParametersProto.CancelReason name = 1;
+      optional int32 count = 2;
+    }
+    repeated ReasonCount reason_count = 2;
+  };
+  repeated JobCompletion job_completion = 16;
+
+  message Network {
+    // Mobile data traffic (total, background + foreground).
+    optional int64 mobile_bytes_rx = 1;
+    optional int64 mobile_bytes_tx = 2;
+    // Wifi data traffic (total, background + foreground).
+    optional int64 wifi_bytes_rx = 3;
+    optional int64 wifi_bytes_tx = 4;
+    // Bluetooth data traffic (total, background + foreground).
+    optional int64 bt_bytes_rx = 5;
+    optional int64 bt_bytes_tx = 6;
+    // In packets (total, background + foreground).
+    optional int64 mobile_packets_rx = 7;
+    optional int64 mobile_packets_tx = 8;
+    optional int64 wifi_packets_rx = 9;
+    optional int64 wifi_packets_tx = 10;
+    // Radio active duration.
+    optional int64 mobile_active_duration_ms = 11;
+    optional int32 mobile_active_count = 12;
+    // Number of times the app woke up the mobile radio.
+    optional int32 mobile_wakeup_count = 13;
+    // Number of times the app woke up the wifi radio.
+    optional int32 wifi_wakeup_count = 14;
+    // Mobile data traffic in the background only, included in total above.
+    optional int64 mobile_bytes_bg_rx = 15;
+    optional int64 mobile_bytes_bg_tx = 16;
+    // Wifi data traffic in the background only, included in total above.
+    optional int64 wifi_bytes_bg_rx = 17;
+    optional int64 wifi_bytes_bg_tx = 18;
+    // In packets (background only, included in total packets above).
+    optional int64 mobile_packets_bg_rx = 19;
+    optional int64 mobile_packets_bg_tx = 20;
+    optional int64 wifi_packets_bg_rx = 21;
+    optional int64 wifi_packets_bg_tx = 22;
+  };
+  optional Network network = 17;
+
+  // TODO: combine System and App messages?
+  message PowerUseItem {
+    // Estimated power use in mAh.
+    optional double computed_power_mah = 1;
+    // Starting in Oreo, Battery Settings has two modes to display the battery
+    // info. The first is "app usage list". In this mode, items with should_hide
+    // enabled are hidden.
+    optional bool should_hide = 2;
+    // Smeared power from screen usage. Screen usage power is split and smeared
+    // among apps, based on activity time.
+    optional double screen_power_mah = 3;
+    // Smeared power using proportional method. Power usage from hidden sippers
+    // is smeared to all apps proportionally (except for screen usage).
+    optional double proportional_smear_mah = 4;
+  };
+  optional PowerUseItem power_use_item = 18;
+
+  // Durations are not pooled/apportioned.
+  message Process {
+    optional string name = 1;
+    // Time spent executing in user code.
+    optional int64 user_duration_ms = 2;
+    // Time spent executing in kernel code.
+    optional int64 system_duration_ms = 3;
+    // Time the process was running in the foreground.
+    optional int64 foreground_duration_ms = 4;
+    // Number of times the process has been started.
+    optional int32 start_count = 5;
+    // Number of times the process has had an ANR.
+    optional int32 anr_count = 6;
+    // Number of times the process has crashed.
+    optional int32 crash_count = 7;
+  };
+  repeated Process process = 19;
+
+  message StateTime {
+    // All of these (non-deprecated) states are mutually exclusive and can be
+    // added together to find the total time a uid has had any processes running
+    // at all.
+
+    // In approximate order or priority (top being what the framework considers
+    // most important and is thus least likely to kill when resources are
+    // needed:
+    // top > foreground service > top sleeping > foreground > background > cache
+    enum State {
+      // Time this uid has any processes in the top state (or above such as
+      // persistent).
+      PROCESS_STATE_TOP = 0;
+      // Time this uid has any process with a started out bound foreground
+      // service, but none in the "top" state.
+      PROCESS_STATE_FOREGROUND_SERVICE = 1;
+      // Time this uid has any process that is top while the device is sleeping,
+      // but none in the "foreground service" or better state. Sleeping is
+      // mostly screen off, but also includes the time when the screen is on but
+      // the device has not yet been unlocked.
+      PROCESS_STATE_TOP_SLEEPING = 2;
+      // Time this uid has any process in an active foreground state, but none
+      // in the "top sleeping" or better state.
+      PROCESS_STATE_FOREGROUND = 3;
+      // Time this uid has any process in an active background state, but none
+      // in the "foreground" or better state.
+      PROCESS_STATE_BACKGROUND = 4;
+      // Time this uid has any processes that are sitting around cached, not in
+      // one of the other active states.
+      PROCESS_STATE_CACHED = 5;
+    }
+    optional State state = 1;
+    optional int64 duration_ms = 2;
+  }
+  repeated StateTime states = 20;
+
+  message Sensor {
+    optional int32 id = 1;
+    optional TimerProto apportioned = 2;
+    // Background times aren't apportioned.
+    optional TimerProto background = 3;
+  }
+  repeated Sensor sensors = 21;
+
+  message Sync {
+    optional string name = 1;
+    // Sync times aren't apportioned.
+    optional TimerProto total = 2;
+    optional TimerProto background = 3;
+  }
+  repeated Sync syncs = 22;
+
+  message UserActivity {
+    optional android.os.PowerManagerProto.UserActivityEvent name = 1;
+    optional int32 count = 2;
+  };
+  repeated UserActivity user_activity = 23;
+
+  // Aggregated wakelock data for an app overall, across all of its wakelocks.
+  // The Wakelock message holds data about each *individual* wakelock, but it
+  // cannot be used to ascertain the aggregated time the app spent holding
+  // wakelocks, since merely summing Wakelock data will either underestimate (in
+  // the case of wakelock.partial.duration_ms) or overestimate (in the case of
+  // wakelock.partial.total_duration_ms) the total time, due to overlapping
+  // wakelocks. AggregatedWakelock, on the other hand, holds overall per-app
+  // wakelock data.
+  message AggregatedWakelock {
+    // The total duration that the app spent holding partial wakelocks.
+    // It includes both foreground + background use.
+    optional int64 partial_duration_ms = 1;
+    // The total duration that the app spent holding partial wakelocks while the
+    // app was in the background. Subtracting from partial_duration_ms will
+    // yield foreground usage.
+    optional int64 background_partial_duration_ms = 2;
+  };
+  optional AggregatedWakelock aggregated_wakelock = 24;
+
+  message Wakelock {
+    optional string name = 1;
+
+    // Full wakelocks keep the screen on. Based on
+    // PowerManager.SCREEN_BRIGHT_WAKE_LOCK (deprecated in API 13) and
+    // PowerManager.SCREEN_DIM_WAKE_LOCK (deprecated in API 17). Current, max,
+    // and total durations are not tracked for full wakelocks.
+    optional TimerProto full = 2;
+
+    // Partial wakelocks ensure the CPU is running while allowing the screen
+    // to turn off. Based on PowerManager.PARTIAL_WAKE_LOCK.
+    // Partial wakelock metrics are only recorded when the device is unplugged
+    // *and* the screen is off. Current, max, and total durations are tracked
+    // for partial wakelocks.
+    optional TimerProto partial = 3;
+
+    // These fields are for tracking partial wakelocks (see above), but only
+    // the time the wakelock was held while the app was in a background state.
+    // Since all background tracking is 'actual', not 'apportioned',
+    // background_partial.duration_ms is identical to
+    // background_partial.total_duration_ms.
+    optional TimerProto background_partial = 4;
+
+    // Window wakelocks keep the screen on. Current, max, and total durations
+    // are not tracked for window wakelocks.
+    optional TimerProto window = 5;
+  };
+  repeated Wakelock wakelocks = 25;
+
+  message WakeupAlarm {
+    // Wakeup alarm name.
+    optional string name = 1;
+    // Only includes counts when screen-off (& on battery).
+    optional int32 count = 2;
+  }
+  repeated WakeupAlarm wakeup_alarm = 26;
+
+  message Wifi {
+    // Duration holding Wifi-lock. This time is apportioned.
+    optional int64 full_wifi_lock_duration_ms = 1;
+    // Duration running Wifi. This time is apportioned.
+    optional int64 running_duration_ms = 2;
+    // Duration performing Wifi-scan blamed on this App (i.e. apportioned to
+    // this app amongst all apps doing Wifi-scanning; see explanation of
+    // 'apportioned' in App's comment).
+    optional TimerProto apportioned_scan = 3;
+    // Scans performed when app is in background. (Included in
+    // apportioned_scan). This value is not apportioned. Subtracting
+    // background_scan.total_duration_ms from apportioned_scan.total_duration_ms
+    // will yield foreground usage.
+    optional TimerProto background_scan = 4;
+  };
+  optional Wifi wifi = 27;
 }
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
new file mode 100644
index 0000000..3bfe5d6
--- /dev/null
+++ b/core/proto/android/os/powermanager.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+message PowerManagerProto {
+    /* User activity events in PowerManager.java. */
+    enum UserActivityEvent {
+        // Unspecified event type.
+        USER_ACTIVITY_EVENT_OTHER = 0;
+        // Button or key pressed or released.
+        USER_ACTIVITY_EVENT_BUTTON = 1;
+        // Touch down, move or up.
+        USER_ACTIVITY_EVENT_TOUCH = 2;
+        // Accessibility taking action on behalf of user.
+        USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
+    }
+}