Add a global setting to turn on/off the proc state cpu times tracking.

Bug: 66953194
Test: atest core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
Test: atest hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
Test: atest core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
Change-Id: Id26476ad77c95994f358d8bd59b6c2e6513c4c54
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index fc86500..91782e9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -112,6 +112,7 @@
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
+                    Settings.Global.BATTERY_STATS_CONSTANTS,
                     Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index e54fe7d..4d34721 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -25,6 +25,8 @@
 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
 import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES;
 
+import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE;
+
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -48,15 +50,19 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
 import android.util.DebugUtils;
 import android.util.Log;
 
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
@@ -86,6 +92,9 @@
     private static final int START_SERVICE_TIMEOUT_MS = 2000;
     private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000;
 
+    private static final int SETTING_UPDATE_TIMEOUT_MS = 2000;
+    private static final int SETTING_UPDATE_CHECK_INTERVAL_MS = 200;
+
     private static final int GENERAL_TIMEOUT_MS = 4000;
     private static final int GENERAL_INTERVAL_MS = 200;
 
@@ -97,6 +106,8 @@
     private static boolean sCpuFreqTimesAvailable;
     private static boolean sPerProcStateTimesAvailable;
 
+    @Rule public TestName testName = new TestName();
+
     @BeforeClass
     public static void setupOnce() throws Exception {
         sContext = InstrumentationRegistry.getContext();
@@ -123,6 +134,9 @@
     @Test
     public void testCpuFreqTimes() throws Exception {
         if (!sCpuFreqTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -148,6 +162,9 @@
     @Test
     public void testCpuFreqTimes_screenOff() throws Exception {
         if (!sCpuFreqTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -179,6 +196,9 @@
     @Test
     public void testCpuFreqTimes_isolatedProcess() throws Exception {
         if (!sCpuFreqTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -204,6 +224,9 @@
     @Test
     public void testCpuFreqTimes_stateTop() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -234,6 +257,9 @@
     @Test
     public void testIsolatedCpuFreqTimes_stateService() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -272,6 +298,9 @@
     @Test
     public void testCpuFreqTimes_stateTopSleeping() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -302,6 +331,9 @@
     @Test
     public void testCpuFreqTimes_stateFgService() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -332,6 +364,9 @@
     @Test
     public void testCpuFreqTimes_stateFg() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -362,6 +397,9 @@
     @Test
     public void testCpuFreqTimes_stateBg() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -392,6 +430,9 @@
     @Test
     public void testCpuFreqTimes_stateCached() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -419,6 +460,124 @@
         batteryOffScreenOn();
     }
 
+    @Test
+    public void testCpuFreqTimes_trackingDisabled() throws Exception {
+        if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
+            return;
+        }
+
+        final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(),
+                Settings.Global.BATTERY_STATS_CONSTANTS);
+        try {
+            batteryOnScreenOn();
+            forceStop();
+            resetBatteryStats();
+            final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
+            assertNull("Initial snapshot should be null, initial="
+                    + Arrays.toString(initialSnapshot), initialSnapshot);
+            assertNull("Initial top state snapshot should be null",
+                    getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));
+
+            doSomeWork(PROCESS_STATE_TOP);
+            forceStop();
+
+            final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            final String msgCpuTimes = getAllCpuTimesMsg();
+            assertCpuTimesValid(cpuTimesMs);
+            long actualCpuTimeMs = 0;
+            for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+                actualCpuTimeMs += cpuTimesMs[i];
+            }
+            assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
+                    WORK_DURATION_MS, actualCpuTimeMs);
+
+            updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false);
+
+            doSomeWork(PROCESS_STATE_TOP);
+            forceStop();
+
+            final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            assertCpuTimesValid(cpuTimesMs2);
+            assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20,
+                    "Unexpected cpu times with tracking off");
+
+            updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true);
+
+            final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            assertCpuTimesValid(cpuTimesMs3);
+            assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 20,
+                    "Unexpected cpu times after turning on tracking");
+
+            doSomeWork(PROCESS_STATE_TOP);
+            forceStop();
+
+            final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            assertCpuTimesValid(cpuTimesMs4);
+            actualCpuTimeMs = 0;
+            for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+                actualCpuTimeMs += cpuTimesMs[i];
+            }
+            assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
+                    2 * WORK_DURATION_MS, actualCpuTimeMs);
+
+            batteryOffScreenOn();
+        } finally {
+            Settings.Global.putString(sContext.getContentResolver(),
+                    Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants);
+        }
+    }
+
+    private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) {
+        for (int i = actual.length - 1; i >= 0; --i) {
+            if (actual[i] > expected[i] + delta || actual[i] < expected[i]) {
+                fail(errMsg + ", actual=" + actual + ", expected=" + expected + ", delta=" + delta);
+            }
+        }
+    }
+
+    private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled)
+            throws Exception {
+        final String newConstants;
+        final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled;
+        if (originalConstants == null || "null".equals(originalConstants)) {
+            newConstants = setting;
+        } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) {
+            newConstants = originalConstants.replaceAll(
+                    KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting);
+        } else {
+            newConstants = originalConstants + "," + setting;
+        }
+        Settings.Global.putString(sContext.getContentResolver(),
+                Settings.Global.BATTERY_STATS_CONSTANTS, newConstants);
+        assertTrackPerProcStateCpuTimesSetting(enabled);
+    }
+
+    private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception {
+        final String expectedValue = Boolean.toString(enabled);
+        assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> {
+            final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE);
+            return expectedValue.equals(actualValue)
+                    ? null : "expected=" + expectedValue + ", actual=" + actualValue;
+        }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS);
+    }
+
+    private String getSettingValueFromDump(String key) throws Exception {
+        final String settingsDump = executeCmdSilent("dumpsys batterystats --settings");
+        final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
+        splitter.setString(settingsDump);
+        String next;
+        while (splitter.hasNext()) {
+            next = splitter.next();
+            if (next.startsWith(key)) {
+                return next.split("=")[1];
+            }
+        }
+        return null;
+    }
+
     private void assertCpuTimesValid(long[] cpuTimes) {
         assertNotNull(cpuTimes);
         for (int i = 0; i < cpuTimes.length; ++i) {
@@ -750,13 +909,18 @@
     }
 
     private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition)
-            throws Exception {
-        final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS;
+        throws Exception {
+        assertDelayedCondition(errMsgPrefix, condition, GENERAL_TIMEOUT_MS, GENERAL_INTERVAL_MS);
+    }
+
+    private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition,
+            long timeoutMs, long checkIntervalMs) throws Exception {
+        final long endTime = SystemClock.uptimeMillis() + timeoutMs;
         while (SystemClock.uptimeMillis() <= endTime) {
             if (condition.getErrIfNotTrue() == null) {
                 return;
             }
-            SystemClock.sleep(GENERAL_INTERVAL_MS);
+            SystemClock.sleep(checkIntervalMs);
         }
         final String errMsg = condition.getErrIfNotTrue();
         if (errMsg != null) {
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 43b41a0..6c5a2aa 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -139,6 +139,11 @@
         }
 
         @Override
+        public Future<?> scheduleCpuSyncDueToSettingChange() {
+            return null;
+        }
+
+        @Override
         public Future<?> scheduleReadProcStateCpuTimes(
                 boolean onBattery, boolean onBatteryScreenOff) {
             return null;