Loading core/java/android/provider/Settings.java +16 −0 Original line number Diff line number Diff line Loading @@ -9774,6 +9774,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. Loading core/java/com/android/internal/os/BatteryStatsImpl.java +105 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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)); Loading Loading @@ -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; Loading Loading @@ -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). Loading Loading @@ -900,6 +929,7 @@ public class BatteryStatsImpl extends BatteryStats { mHandler = null; mPlatformIdleStateCallback = null; mUserInfoProvider = null; mConstants = new Constants(mHandler); clearHistoryLocked(); } Loading Loading @@ -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(), Loading Loading @@ -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); Loading Loading @@ -9860,6 +9891,7 @@ public class BatteryStatsImpl extends BatteryStats { mDailyFile = null; mHandler = null; mExternalSync = null; mConstants = new Constants(mHandler); clearHistoryLocked(); readFromParcel(p); mPlatformIdleStateCallback = null; Loading Loading @@ -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(); Loading core/java/com/android/internal/os/KernelSingleUidTimeReader.java +27 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading core/tests/coretests/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +167 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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(); Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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) { Loading Loading @@ -751,12 +910,17 @@ public class BstatsCpuTimesValidationTest { private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition) throws Exception { final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS; 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) { Loading Loading
core/java/android/provider/Settings.java +16 −0 Original line number Diff line number Diff line Loading @@ -9774,6 +9774,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. Loading
core/java/com/android/internal/os/BatteryStatsImpl.java +105 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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)); Loading Loading @@ -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; Loading Loading @@ -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). Loading Loading @@ -900,6 +929,7 @@ public class BatteryStatsImpl extends BatteryStats { mHandler = null; mPlatformIdleStateCallback = null; mUserInfoProvider = null; mConstants = new Constants(mHandler); clearHistoryLocked(); } Loading Loading @@ -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(), Loading Loading @@ -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); Loading Loading @@ -9860,6 +9891,7 @@ public class BatteryStatsImpl extends BatteryStats { mDailyFile = null; mHandler = null; mExternalSync = null; mConstants = new Constants(mHandler); clearHistoryLocked(); readFromParcel(p); mPlatformIdleStateCallback = null; Loading Loading @@ -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(); Loading
core/java/com/android/internal/os/KernelSingleUidTimeReader.java +27 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
core/tests/coretests/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +167 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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(); Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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) { Loading Loading @@ -751,12 +910,17 @@ public class BstatsCpuTimesValidationTest { private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition) throws Exception { final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS; 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) { Loading