PowerStats: adjust time since boot to epoch before writing to disk

Data stored to disk are extracted into an incident report for timeseries
visualization which shows walltime and may span multiple reboot sessions
so storing walltime is better suited than relative time since device boot.

Bug: 179712445
Tests: atest FrameworksServicesTests:PowerStatsServiceTest
Signed-off-by: Thierry Strudel <tstrudel@google.com>
Change-Id: I5ae8e2fe22f1ab9ea960e5be8fef4e9fb1cc10f0
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index 4b1ee02..f64e146 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -144,7 +144,7 @@
      */
     optional int64 total_state_entry_count = 3;
     /**
-     * Last time this state was entered. Time in milliseconds since boot
+     * Last time this state was entered. Walltime in milliseconds since Unix epoch.
      */
     optional int64 last_entry_timestamp_ms = 4;
 }
@@ -208,7 +208,7 @@
     /** Unique index identifying the energy consumer. */
     optional int32 id = 1;
 
-    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+    /** Walltime in milliseconds since Unix epoch */
     optional int64 timestamp_ms = 2;
 
     /** Accumulated energy since device boot in microwatt-seconds (uWs) */
@@ -247,7 +247,7 @@
      */
     optional int32 id = 1;
 
-    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+    /** Walltime in milliseconds since Unix epoch */
     optional int64 timestamp_ms = 2;
 
     /** Accumulated energy since device boot in microwatt-seconds (uWs) */
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index c4f29ea..ef0079e 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -16,6 +16,8 @@
 
 package com.android.server.powerstats;
 
+import static java.lang.System.currentTimeMillis;
+
 import android.content.Context;
 import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
@@ -26,11 +28,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
 import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
 import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -61,6 +66,8 @@
     protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1;
     protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2;
 
+    // TODO(b/181240441): Add a listener to update the Wall clock baseline when changed
+    private final long mStartWallTime;
     private final PowerStatsDataStorage mPowerStatsMeterStorage;
     private final PowerStatsDataStorage mPowerStatsModelStorage;
     private final PowerStatsDataStorage mPowerStatsResidencyStorage;
@@ -79,6 +86,8 @@
                 // Log power meter data.
                 EnergyMeasurement[] energyMeasurements =
                     mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
+                EnergyMeasurementUtils.adjustTimeSinceBootToEpoch(energyMeasurements,
+                        mStartWallTime);
                 mPowerStatsMeterStorage.write(
                         EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
                 if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
@@ -86,6 +95,8 @@
                 // Log power model data without attribution data.
                 EnergyConsumerResult[] ecrNoAttribution =
                     mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+                EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrNoAttribution,
+                        mStartWallTime);
                 mPowerStatsModelStorage.write(
                         EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false));
                 if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution);
@@ -97,6 +108,8 @@
                 // Log power model data with attribution data.
                 EnergyConsumerResult[] ecrAttribution =
                     mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+                EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrAttribution,
+                        mStartWallTime);
                 mPowerStatsModelStorage.write(
                         EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true));
                 if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution);
@@ -108,6 +121,8 @@
                 // Log state residency data.
                 StateResidencyResult[] stateResidencyResults =
                     mPowerStatsHALWrapper.getStateResidency(new int[0]);
+                StateResidencyResultUtils.adjustTimeSinceBootToEpoch(stateResidencyResults,
+                        mStartWallTime);
                 mPowerStatsResidencyStorage.write(
                         StateResidencyResultUtils.getProtoBytes(stateResidencyResults));
                 if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults);
@@ -293,12 +308,19 @@
         return mDeleteResidencyDataOnBoot;
     }
 
+    @VisibleForTesting
+    public long getStartWallTime() {
+        return mStartWallTime;
+    }
+
     public PowerStatsLogger(Context context, File dataStoragePath,
             String meterFilename, String meterCacheFilename,
             String modelFilename, String modelCacheFilename,
             String residencyFilename, String residencyCacheFilename,
             IPowerStatsHALWrapper powerStatsHALWrapper) {
         super(Looper.getMainLooper());
+        mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
+        if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
         mPowerStatsHALWrapper = powerStatsHALWrapper;
         mDataStoragePath = dataStoragePath;
 
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 11b22a5..746a098 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -109,6 +109,18 @@
     }
 
     static class StateResidencyResultUtils {
+        public static void adjustTimeSinceBootToEpoch(StateResidencyResult[] stateResidencyResult,
+                long startWallTime) {
+            for (int i = 0; i < stateResidencyResult.length; i++) {
+                final int stateLength = stateResidencyResult[i].stateResidencyData.length;
+                for (int j = 0; j < stateLength; j++) {
+                    final StateResidency stateResidencyData =
+                            stateResidencyResult[i].stateResidencyData[j];
+                    stateResidencyData.lastEntryTimestampMs += startWallTime;
+                }
+            }
+        }
+
         public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) {
             ProtoOutputStream pos = new ProtoOutputStream();
             packProtoMessage(stateResidencyResult, pos);
@@ -306,6 +318,13 @@
     }
 
     static class EnergyMeasurementUtils {
+        public static void adjustTimeSinceBootToEpoch(EnergyMeasurement[] energyMeasurement,
+                long startWallTime) {
+            for (int i = 0; i < energyMeasurement.length; i++) {
+                energyMeasurement[i].timestampMs += startWallTime;
+            }
+        }
+
         public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
             ProtoOutputStream pos = new ProtoOutputStream();
             packProtoMessage(energyMeasurement, pos);
@@ -518,6 +537,13 @@
     }
 
     static class EnergyConsumerResultUtils {
+        public static void adjustTimeSinceBootToEpoch(EnergyConsumerResult[] energyConsumerResult,
+                long startWallTime) {
+            for (int i = 0; i < energyConsumerResult.length; i++) {
+                energyConsumerResult[i].timestampMs += startWallTime;
+            }
+        }
+
         public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult,
                 boolean includeAttribution) {
             ProtoOutputStream pos = new ProtoOutputStream();
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index ddbe81c..26b34fd 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -318,7 +318,8 @@
         assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
         for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
             assertTrue(pssProto.energyMeasurement[i].id == i);
-            assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+            assertTrue(pssProto.energyMeasurement[i].timestampMs ==
+                    i + mPowerStatsLogger.getStartWallTime());
             assertTrue(pssProto.energyMeasurement[i].durationMs == i);
             assertTrue(pssProto.energyMeasurement[i].energyUws == i);
         }
@@ -359,7 +360,8 @@
         assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
         for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
             assertTrue(pssProto.energyConsumerResult[i].id == i);
-            assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+            assertTrue(pssProto.energyConsumerResult[i].timestampMs ==
+                    i + mPowerStatsLogger.getStartWallTime());
             assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
             assertTrue(pssProto.energyConsumerResult[i].attribution.length
                     == ENERGY_CONSUMER_ATTRIBUTION_COUNT);
@@ -420,7 +422,8 @@
                 assertTrue(stateResidency.id == j);
                 assertTrue(stateResidency.totalTimeInStateMs == j);
                 assertTrue(stateResidency.totalStateEntryCount == j);
-                assertTrue(stateResidency.lastEntryTimestampMs == j);
+                assertTrue(stateResidency.lastEntryTimestampMs ==
+                        j + mPowerStatsLogger.getStartWallTime());
             }
         }
     }