diff options
9 files changed, 349 insertions, 7 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c1030cb0a246..0850bd997c3a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9775,6 +9775,22 @@ public final class Settings {          public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";          /** +         * BatteryStats specific settings. +         * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true" +         * +         * The following keys are supported: +         * <pre> +         * track_cpu_times_by_proc_state (boolean) +         * </pre> +         * +         * <p> +         * Type: string +         * @hide +         * see also com.android.internal.os.BatteryStatsImpl.Constants +         */ +        public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants"; + +        /**           * Whether or not App Standby feature is enabled. This controls throttling of apps           * based on usage patterns and predictions.           * Type: int (0 for false, 1 for true) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 9015cbbe2a19..b8ff9e4e3ebc 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -21,10 +21,13 @@ import android.annotation.Nullable;  import android.app.ActivityManager;  import android.bluetooth.BluetoothActivityEnergyInfo;  import android.bluetooth.UidTraffic; +import android.content.ContentResolver;  import android.content.Context;  import android.content.Intent; +import android.database.ContentObserver;  import android.net.ConnectivityManager;  import android.net.NetworkStats; +import android.net.Uri;  import android.net.wifi.WifiActivityEnergyInfo;  import android.net.wifi.WifiManager;  import android.os.BatteryManager; @@ -46,6 +49,7 @@ import android.os.SystemClock;  import android.os.UserHandle;  import android.os.WorkSource;  import android.os.WorkSource.WorkChain; +import android.provider.Settings;  import android.telephony.DataConnectionRealTimeInfo;  import android.telephony.ModemActivityInfo;  import android.telephony.ServiceState; @@ -54,6 +58,7 @@ import android.telephony.TelephonyManager;  import android.text.TextUtils;  import android.util.ArrayMap;  import android.util.IntArray; +import android.util.KeyValueListParser;  import android.util.Log;  import android.util.LogWriter;  import android.util.LongSparseArray; @@ -292,9 +297,18 @@ public class BatteryStatsImpl extends BatteryStats {      public void updateProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {          final SparseIntArray uidStates;          synchronized (BatteryStatsImpl.this) { +            if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) { +                return; +            }              if(!initKernelSingleUidTimeReaderLocked()) {                  return;              } +            // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to +            // compute deltas since it might result in mis-attributing cpu times to wrong states. +            if (mKernelSingleUidTimeReader.hasStaleData()) { +                mPendingUids.clear(); +                return; +            }              if (mPendingUids.size() == 0) {                  return; @@ -355,12 +369,23 @@ public class BatteryStatsImpl extends BatteryStats {       */      public void copyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {          synchronized (BatteryStatsImpl.this) { +            if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) { +                return; +            }              if(!initKernelSingleUidTimeReaderLocked()) {                  return;              }              final SparseArray<long[]> allUidCpuFreqTimesMs =                      mKernelUidCpuFreqTimeReader.getAllUidCpuFreqTimeMs(); +            // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to +            // compute deltas since it might result in mis-attributing cpu times to wrong states. +            if (mKernelSingleUidTimeReader.hasStaleData()) { +                mKernelSingleUidTimeReader.setAllUidsCpuTimesMs(allUidCpuFreqTimesMs); +                mKernelSingleUidTimeReader.markDataAsStale(false); +                mPendingUids.clear(); +                return; +            }              for (int i = allUidCpuFreqTimesMs.size() - 1; i >= 0; --i) {                  final int uid = allUidCpuFreqTimesMs.keyAt(i);                  final Uid u = getAvailableUidStatsLocked(mapUid(uid)); @@ -450,6 +475,7 @@ public class BatteryStatsImpl extends BatteryStats {          Future<?> scheduleCpuSyncDueToRemovedUid(int uid);          Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff);          Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff); +        Future<?> scheduleCpuSyncDueToSettingChange();      }      public Handler mHandler; @@ -812,6 +838,9 @@ public class BatteryStatsImpl extends BatteryStats {      @VisibleForTesting      protected PowerProfile mPowerProfile; +    @GuardedBy("this") +    private final Constants mConstants; +      /*       * Holds a SamplingTimer associated with each Resource Power Manager state and voter,       * recording their times when on-battery (regardless of screen state). @@ -900,6 +929,7 @@ public class BatteryStatsImpl extends BatteryStats {          mHandler = null;          mPlatformIdleStateCallback = null;          mUserInfoProvider = null; +        mConstants = new Constants(mHandler);          clearHistoryLocked();      } @@ -9411,7 +9441,7 @@ public class BatteryStatsImpl extends BatteryStats {                  if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {                      mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs); -                    if (mBsi.mPerProcStateCpuTimesAvailable) { +                    if (mBsi.trackPerProcStateCpuTimes()) {                          if (mBsi.mPendingUids.size() == 0) {                              mBsi.mExternalSync.scheduleReadProcStateCpuTimes(                                      mBsi.mOnBatteryTimeBase.isRunning(), @@ -9765,6 +9795,7 @@ public class BatteryStatsImpl extends BatteryStats {          mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));          mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));          mHandler = new MyHandler(handler.getLooper()); +        mConstants = new Constants(mHandler);          mStartCount++;          mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);          mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); @@ -9860,6 +9891,7 @@ public class BatteryStatsImpl extends BatteryStats {          mDailyFile = null;          mHandler = null;          mExternalSync = null; +        mConstants = new Constants(mHandler);          clearHistoryLocked();          readFromParcel(p);          mPlatformIdleStateCallback = null; @@ -12613,6 +12645,78 @@ public class BatteryStatsImpl extends BatteryStats {          mShuttingDown = true;      } +    public boolean trackPerProcStateCpuTimes() { +        return mConstants.TRACK_CPU_TIMES_BY_PROC_STATE && mPerProcStateCpuTimesAvailable; +    } + +    public void systemServicesReady(Context context) { +        mConstants.startObserving(context.getContentResolver()); +    } + +    @VisibleForTesting +    public final class Constants extends ContentObserver { +        public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE +                = "track_cpu_times_by_proc_state"; + +        private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; + +        public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; + +        private ContentResolver mResolver; +        private final KeyValueListParser mParser = new KeyValueListParser(','); + +        public Constants(Handler handler) { +            super(handler); +        } + +        public void startObserving(ContentResolver resolver) { +            mResolver = resolver; +            mResolver.registerContentObserver( +                    Settings.Global.getUriFor(Settings.Global.BATTERY_STATS_CONSTANTS), +                    false /* notifyForDescendants */, this); +            updateConstants(); +        } + +        @Override +        public void onChange(boolean selfChange, Uri uri) { +            updateConstants(); +        } + +        private void updateConstants() { +            synchronized (BatteryStatsImpl.this) { +                try { +                    mParser.setString(Settings.Global.getString(mResolver, +                            Settings.Global.BATTERY_STATS_CONSTANTS)); +                } catch (IllegalArgumentException e) { +                    // Failed to parse the settings string, log this and move on +                    // with defaults. +                    Slog.e(TAG, "Bad batterystats settings", e); +                } + +                updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE, +                        mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE, +                                DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE)); +            } +        } + +        private void updateTrackCpuTimesByProcStateLocked(boolean wasEnabled, boolean isEnabled) { +            TRACK_CPU_TIMES_BY_PROC_STATE = isEnabled; +            if (isEnabled && !wasEnabled) { +                mKernelSingleUidTimeReader.markDataAsStale(true); +                mExternalSync.scheduleCpuSyncDueToSettingChange(); +            } +        } + +        public void dumpLocked(PrintWriter pw) { +            pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("="); +            pw.println(TRACK_CPU_TIMES_BY_PROC_STATE); +        } +    } + +    public void dumpConstantsLocked(PrintWriter pw) { +        mConstants.dumpLocked(pw); +    } +      Parcel mPendingWrite = null;      final ReentrantLock mWriteLock = new ReentrantLock(); diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java index ca635a409a44..ebeb24c41479 100644 --- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java @@ -46,12 +46,14 @@ public class KernelSingleUidTimeReader {      private final int mCpuFreqsCount;      @GuardedBy("this") -    private final SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>(); +    private SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>();      @GuardedBy("this")      private int mReadErrorCounter;      @GuardedBy("this")      private boolean mSingleUidCpuTimesAvailable = true; +    @GuardedBy("this") +    private boolean mHasStaleData;      private final Injector mInjector; @@ -166,6 +168,30 @@ public class KernelSingleUidTimeReader {          return deltaTimesMs;      } +    public void markDataAsStale(boolean hasStaleData) { +        synchronized (this) { +            mHasStaleData = hasStaleData; +        } +    } + +    public boolean hasStaleData() { +        synchronized (this) { +            return mHasStaleData; +        } +    } + +    public void setAllUidsCpuTimesMs(SparseArray<long[]> allUidsCpuTimesMs) { +        synchronized (this) { +            mLastUidCpuTimeMs.clear(); +            for (int i = allUidsCpuTimesMs.size() - 1; i >= 0; --i) { +                final long[] cpuTimesMs = allUidsCpuTimesMs.valueAt(i); +                if (cpuTimesMs != null) { +                    mLastUidCpuTimeMs.put(allUidsCpuTimesMs.keyAt(i), cpuTimesMs.clone()); +                } +            } +        } +    } +      public void removeUid(int uid) {          synchronized (this) {              mLastUidCpuTimeMs.delete(uid); diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 9df4f516e1e7..dfefbfd79551 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -112,6 +112,7 @@ public class SettingsBackupTest {                      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 e54fe7ddbf19..4d34721b5aba 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;  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.IBinder;  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 @@ public class BstatsCpuTimesValidationTest {      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 @@ public class BstatsCpuTimesValidationTest {      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 @@ public class BstatsCpuTimesValidationTest {      @Test      public void testCpuFreqTimes() throws Exception {          if (!sCpuFreqTimesAvailable) { +            Log.w(TAG, "Skipping " + testName.getMethodName() +                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable +                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);              return;          } @@ -148,6 +162,9 @@ public class BstatsCpuTimesValidationTest {      @Test      public void testCpuFreqTimes_screenOff() throws Exception {          if (!sCpuFreqTimesAvailable) { +            Log.w(TAG, "Skipping " + testName.getMethodName() +                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable +                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);              return;          } @@ -179,6 +196,9 @@ public class BstatsCpuTimesValidationTest {      @Test      public void testCpuFreqTimes_isolatedProcess() throws Exception {          if (!sCpuFreqTimesAvailable) { +            Log.w(TAG, "Skipping " + testName.getMethodName() +                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable +                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);              return;          } @@ -204,6 +224,9 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {      @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 @@ public class BstatsCpuTimesValidationTest {          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 @@ public class BstatsCpuTimesValidationTest {      }      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 43b41a0d142b..6c5a2aac159b 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 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {          }          @Override +        public Future<?> scheduleCpuSyncDueToSettingChange() { +            return null; +        } + +        @Override          public Future<?> scheduleReadProcStateCpuTimes(                  boolean onBattery, boolean onBatteryScreenOff) {              return null; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 700562258ba6..624035d2c720 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2697,6 +2697,13 @@ public class ActivityManagerService extends IActivityManager.Stub          }          @Override +        public void onBootPhase(int phase) { +            if (phase == PHASE_SYSTEM_SERVICES_READY) { +                mService.mBatteryStatsService.systemServicesReady(); +            } +        } + +        @Override          public void onCleanupUser(int userId) {              mService.mBatteryStatsService.onCleanupUser(userId);          } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 28b8edf6fef1..1fcaeef72dba 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -118,9 +118,14 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {      }      @Override +    public synchronized Future<?> scheduleCpuSyncDueToSettingChange() { +        return scheduleSyncLocked("setting-change", UPDATE_CPU); +    } + +    @Override      public Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {          synchronized (mStats) { -            if (!mStats.mPerProcStateCpuTimesAvailable) { +            if (!mStats.trackPerProcStateCpuTimes()) {                  return null;              }          } @@ -138,7 +143,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {      public Future<?> scheduleCopyFromAllUidsCpuTimes(              boolean onBattery, boolean onBatteryScreenOff) {          synchronized (mStats) { -            if (!mStats.mPerProcStateCpuTimesAvailable) { +            if (!mStats.trackPerProcStateCpuTimes()) {                  return null;              }          } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 813e617cc2e0..c9aa9a2e2fa3 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -185,6 +185,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub          ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());      } +    public void systemServicesReady() { +        mStats.systemServicesReady(mContext); +    } +      private final class LocalService extends BatteryStatsInternal {          @Override          public String[] getWifiIfaces() { @@ -1185,6 +1189,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub          pw.println("  --write: force write current collected stats to disk.");          pw.println("  --new-daily: immediately create and write new daily stats record.");          pw.println("  --read-daily: read-load last written daily stats."); +        pw.println("  --settings: dump the settings key/values related to batterystats");          pw.println("  <package.name>: optional name of package to filter output by.");          pw.println("  -h: print this help text.");          pw.println("Battery stats (batterystats) commands:"); @@ -1197,6 +1202,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub          pw.println("      pretend-screen-off: pretend the screen is off, even if screen state changes");      } +    private void dumpSettings(PrintWriter pw) { +        synchronized (mStats) { +            mStats.dumpConstantsLocked(pw); +        } +    } +      private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {          i++;          if (i >= args.length) { @@ -1307,6 +1318,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub                  } else if ("-h".equals(arg)) {                      dumpHelp(pw);                      return; +                } else if ("--settings".equals(arg)) { +                    dumpSettings(pw); +                    return;                  } else if ("-a".equals(arg)) {                      flags |= BatteryStats.DUMP_VERBOSE;                  } else if (arg.length() > 0 && arg.charAt(0) == '-'){  |