From 75ee6042443bfe22dc779609697881fc83ed23db Mon Sep 17 00:00:00 2001 From: Bookatz Date: Fri, 9 Nov 2018 12:27:37 -0800 Subject: [PATCH] Statsd atom: Power Use BatteryStats calculates power usage of the device and various components (such as apps). This information is used, e.g., in the battery panel of Settings. We now log it to statsd. It can be used for validating how good the information displayed in Settings is. In the long-term, it is likely not ideal for off-device calculations, since that can be hopefully estimated using statsd's raw data. Three atoms: one for the total power use, one for the power use of each uid, and one for each non-uid component. Since they will all likely be pulled together, StatsCompanionService will provide stale data for BatteryStats pulls called within a second of a previous BatteryStats pull. Also in this cl: Remove StatsLogEventWrapper.writeDouble. Statsd doesn't support actually writing doubles into its proto reports, so having this function is misleading (the data will get to statsd and then be completely ignored). It's less confusing if we don't pretend it does something. Change-Id: If80bab8ea938afa4632535bb88ff59879fbe8099 Fixes: 119111972 Test: cts-tradefed run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.atom.UidAtomTests#testDeviceCalculatedPowerUse Test: cts-tradefed run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.atom.UidAtomTests#testDeviceCalculatedPowerBlameUid Test: BatteryStatsHelperTest#testDrainTypesSyncedWithProto --- cmds/statsd/src/atoms.proto | 63 ++++++++++++++ .../src/external/StatsPullerManager.cpp | 14 +++ .../java/android/os/StatsLogEventWrapper.java | 8 -- .../android/internal/os/BatterySipper.java | 4 + .../internal/os/BatteryStatsHelperTest.java | 31 +++++++ data/etc/platform.xml | 1 + libs/services/src/os/StatsLogEventWrapper.cpp | 3 - .../server/stats/StatsCompanionService.java | 86 +++++++++++++++++++ 8 files changed, 199 insertions(+), 11 deletions(-) diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 53d967363765c..244974d137efd 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -192,6 +192,9 @@ message Atom { NativeProcessMemoryState native_process_memory_state = 10036; CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037; OnDevicePowerMeasurement on_device_power_measurement = 10038; + DeviceCalculatedPowerUse device_calculated_power_use = 10039; + DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040; + DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -3198,3 +3201,63 @@ message CpuTimePerThreadFreq { // Time spent in frequency in milliseconds, since thread start. optional uint32 time_millis = 7; } + +/** + * Pulls on-device BatteryStats power use calculations for the overall device. + */ +message DeviceCalculatedPowerUse { + // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset + // (i.e. roughly since device was last significantly charged). + // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()). + optional float computed_power_milli_amp_hours = 1; +} + +/** + * Pulls on-device BatteryStats power use calculations broken down by uid. + * This atom should be complemented by DeviceCalculatedPowerBlameOther, which contains the power use + * that is attributed to non-uid items. They must all be included to get the total power use. + */ +message DeviceCalculatedPowerBlameUid { + // Uid being blamed. Note: isolated uids have already been mapped to host uid. + optional int32 uid = 1 [(is_uid) = true]; + + // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset + // (i.e. roughly since device was last significantly charged). + optional float power_milli_amp_hours = 2; +} + +/** + * Pulls on-device BatteryStats power use calculations that are not due to a uid, broken down by + * drain type. + * This atom should be complemented by DeviceCalculatedPowerBlameUid, which contains the blame that + * is attributed uids. They must all be included to get the total power use. + */ +message DeviceCalculatedPowerBlameOther { + // The type of item whose power use is being reported. + enum DrainType { + AMBIENT_DISPLAY = 0; + // reserved 1; reserved "APP"; // Logged instead in DeviceCalculatedPowerBlameUid. + BLUETOOTH = 2; + CAMERA = 3; + // Cell-standby + CELL = 4; + FLASHLIGHT = 5; + IDLE = 6; + MEMORY = 7; + // Amount that total computed drain exceeded the drain estimated using the + // battery level changes and capacity. + OVERCOUNTED = 8; + PHONE = 9; + SCREEN = 10; + // Amount that total computed drain was below the drain estimated using the + // battery level changes and capacity. + UNACCOUNTED = 11; + // reserved 12; reserved "USER"; // Entire drain for a user. This is NOT supported. + WIFI = 13; + } + optional DrainType drain_type = 1; + + // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset + // (i.e. roughly since device was last significantly charged). + optional float power_milli_amp_hours = 2; +} \ No newline at end of file diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 8378ae15c1ef3..0e131cb6c8c98 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -243,6 +243,20 @@ const std::map StatsPullerManager::kAllPullAtomInfo = { {2, 3, 4, 5, 6}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}}, + // DeviceCalculatedPowerUse. + {android::util::DEVICE_CALCULATED_POWER_USE, + {{}, {}, 1 * NS_PER_SEC, + new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}}, + // DeviceCalculatedPowerBlameUid. + {android::util::DEVICE_CALCULATED_POWER_BLAME_UID, + {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here. + 1 * NS_PER_SEC, + new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}}, + // DeviceCalculatedPowerBlameOther. + {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER, + {{}, {}, + 1 * NS_PER_SEC, + new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java index 72e1ab9728469..866bd9a17f41c 100644 --- a/core/java/android/os/StatsLogEventWrapper.java +++ b/core/java/android/os/StatsLogEventWrapper.java @@ -103,14 +103,6 @@ public final class StatsLogEventWrapper implements Parcelable { mValues.add(val); } - /** - * Write a double value. - */ - public void writeDouble(double val) { - mTypes.add(EVENT_TYPE_DOUBLE); - mValues.add(val); - } - /** * Write a storage value. */ diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index 0baf73cc024aa..02c9542fa40d8 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -130,6 +130,10 @@ public class BatterySipper implements Comparable { public double wakeLockPowerMah; public double wifiPowerMah; + // **************** + // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto) + // so the ordinal values (and therefore the order) must never change. + // **************** public enum DrainType { AMBIENT_DISPLAY, @UnsupportedAppUsage diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index e81f6789185e3..7467114a75966 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -41,6 +41,7 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.format.DateUtils; +import android.util.StatsLog; import junit.framework.TestCase; @@ -258,6 +259,36 @@ public class BatteryStatsHelperTest extends TestCase { assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); } + @Test + public void testDrainTypesSyncedWithProto() { + assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY); + // AtomsProto has no "APP" + assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH); + assertEquals(BatterySipper.DrainType.CAMERA.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA); + assertEquals(BatterySipper.DrainType.CELL.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL); + assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT); + assertEquals(BatterySipper.DrainType.IDLE.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE); + assertEquals(BatterySipper.DrainType.MEMORY.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY); + assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED); + assertEquals(BatterySipper.DrainType.PHONE.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE); + assertEquals(BatterySipper.DrainType.SCREEN.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN); + assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED); + // AtomsProto has no "USER" + assertEquals(BatterySipper.DrainType.WIFI.ordinal(), + StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI); + } + private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah, int uidCode, boolean isUidNull) { final BatterySipper sipper = mock(BatterySipper.class); diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 68f24fb7b6610..a4c5ed2ee30f1 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -173,6 +173,7 @@ + diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp index a1a6d9fe0e22b..04c4629b54327 100644 --- a/libs/services/src/os/StatsLogEventWrapper.cpp +++ b/libs/services/src/os/StatsLogEventWrapper.cpp @@ -85,9 +85,6 @@ status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) { case StatsLogValue::FLOAT: mElements.push_back(StatsLogValue(in->readFloat())); break; - case StatsLogValue::DOUBLE: - mElements.push_back(StatsLogValue(in->readDouble())); - break; case StatsLogValue::STORAGE: mElements.push_back(StatsLogValue()); mElements.back().setType(StatsLogValue::STORAGE); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 01d02d61cc839..d111702e5bbbb 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -47,6 +47,7 @@ import android.net.NetworkRequest; import android.net.NetworkStats; import android.net.wifi.IWifiManager; import android.net.wifi.WifiActivityEnergyInfo; +import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Bundle; @@ -87,6 +88,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.IProcessStats; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.net.NetworkStatsFactory; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BinderCallsStats.ExportedCallStat; import com.android.internal.os.KernelCpuSpeedReader; import com.android.internal.os.KernelCpuThreadReader; @@ -205,6 +208,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Nullable private final KernelCpuThreadReader mKernelCpuThreadReader; + private BatteryStatsHelper mBatteryStatsHelper = null; + private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000; + private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS; + private static IThermalService sThermalService; private File mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_companion"); @@ -1430,6 +1437,73 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pulledData.add(e); } + private BatteryStatsHelper getBatteryStatsHelper() { + if (mBatteryStatsHelper == null) { + final long callingToken = Binder.clearCallingIdentity(); + try { + // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly(). + mBatteryStatsHelper = new BatteryStatsHelper(mContext, false); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + mBatteryStatsHelper.create((Bundle) null); + } + long currentTime = SystemClock.elapsedRealtime(); + if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) { + // Load BatteryStats and do all the calculations. + mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL); + // Calculations are done so we don't need to save the raw BatteryStats data in RAM. + mBatteryStatsHelper.clearStats(); + mBatteryStatsHelperTimestampMs = currentTime; + } + return mBatteryStatsHelper; + } + + private void pullDeviceCalculatedPowerUse(int tagId, + long elapsedNanos, final long wallClockNanos, List pulledData) { + BatteryStatsHelper bsHelper = getBatteryStatsHelper(); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeFloat((float) bsHelper.getComputedPower()); + pulledData.add(e); + } + + private void pullDeviceCalculatedPowerBlameUid(int tagId, + long elapsedNanos, final long wallClockNanos, List pulledData) { + final List sippers = getBatteryStatsHelper().getUsageList(); + if (sippers == null) { + return; + } + for (BatterySipper bs : sippers) { + if (bs.drainType != bs.drainType.APP) { + continue; + } + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(bs.uidObj.getUid()); + e.writeFloat((float) bs.totalPowerMah); + pulledData.add(e); + } + } + + private void pullDeviceCalculatedPowerBlameOther(int tagId, + long elapsedNanos, final long wallClockNanos, List pulledData) { + final List sippers = getBatteryStatsHelper().getUsageList(); + if (sippers == null) { + return; + } + for (BatterySipper bs : sippers) { + if (bs.drainType == bs.drainType.APP) { + continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid(). + } + if (bs.drainType == bs.drainType.USER) { + continue; // This is not supported. We purposefully calculate over USER_ALL. + } + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(bs.drainType.ordinal()); + e.writeFloat((float) bs.totalPowerMah); + pulledData.add(e); + } + } + private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos, List pulledData) { mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead, @@ -1655,6 +1729,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.DEVICE_CALCULATED_POWER_USE: { + pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret); + break; + } + case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: { + pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret); + break; + } + case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: { + pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null;