From 293b4b3ad799099773b7a288cf68288f971f0e94 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Wed, 19 Apr 2023 15:45:50 -0700 Subject: [PATCH] Add random noise to power monitor readings Bug: 273310268 Test: atest FrameworksServicesTests:PowerStatsServiceTest Change-Id: I12127999edcff0609fbccee4db91de86ff338838 --- .../server/powerstats/PowerStatsService.java | 54 ++++++++++++++++--- .../powerstats/PowerStatsServiceTest.java | 19 +++++-- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index 6d4c5a7891903..9832c490a491b 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -26,6 +26,7 @@ import android.hardware.power.stats.EnergyConsumerType; import android.hardware.power.stats.EnergyMeasurement; import android.hardware.power.stats.PowerEntity; import android.hardware.power.stats.StateResidencyResult; +import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -41,6 +42,7 @@ import android.os.UserHandle; import android.power.PowerStatsInternal; import android.provider.DeviceConfig; import android.provider.DeviceConfigInterface; +import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -58,6 +60,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -82,6 +85,11 @@ public class PowerStatsService extends SystemService { static final String KEY_POWER_MONITOR_API_ENABLED = "power_monitor_api_enabled"; + // The alpha parameter of the Beta distribution used by the random noise generator. + // The higher this value, the smaller the amount of added noise. + private static final double INTERVAL_RANDOM_NOISE_GENERATION_ALPHA = 50; + private static final long MAX_RANDOM_NOISE_UWS = 10_000_000; + private final Injector mInjector; private final Clock mClock; private final DeviceConfigInterface mDeviceConfig; @@ -189,6 +197,10 @@ public class PowerStatsService extends SystemService { DeviceConfigInterface getDeviceConfig() { return DeviceConfigInterface.REAL; } + + IntervalRandomNoiseGenerator createIntervalRandomNoiseGenerator() { + return new IntervalRandomNoiseGenerator(INTERVAL_RANDOM_NOISE_GENERATION_ALPHA); + } } private final IBinder mService = new IPowerStatsService.Stub() { @@ -200,7 +212,9 @@ public class PowerStatsService extends SystemService { @Override public void getPowerMonitorReadings(int[] powerMonitorIds, ResultReceiver resultReceiver) { - getHandler().post(() -> getPowerMonitorReadingsImpl(powerMonitorIds, resultReceiver)); + int callingUid = Binder.getCallingUid(); + getHandler().post(() -> + getPowerMonitorReadingsImpl(powerMonitorIds, resultReceiver, callingUid)); } @Override @@ -493,6 +507,7 @@ public class PowerStatsService extends SystemService { public final int id; public long timestampMs; public long energyUws = PowerMonitorReadings.ENERGY_UNAVAILABLE; + public long prevEnergyUws; private PowerMonitorState(PowerMonitor powerMonitor, int id) { this.powerMonitor = powerMonitor; @@ -502,7 +517,8 @@ public class PowerStatsService extends SystemService { private boolean mPowerMonitorApiEnabled = true; private volatile PowerMonitor[] mPowerMonitors; - private volatile PowerMonitorState[] mPowerMonitorStates; + private PowerMonitorState[] mPowerMonitorStates; + private IntervalRandomNoiseGenerator mIntervalRandomNoiseGenerator; private void setPowerMonitorApiEnabled(boolean powerMonitorApiEnabled) { if (powerMonitorApiEnabled != mPowerMonitorApiEnabled) { @@ -522,6 +538,10 @@ public class PowerStatsService extends SystemService { return; } + if (mIntervalRandomNoiseGenerator == null) { + mIntervalRandomNoiseGenerator = mInjector.createIntervalRandomNoiseGenerator(); + } + if (!mPowerMonitorApiEnabled) { mPowerMonitors = new PowerMonitor[0]; mPowerMonitorStates = new PowerMonitorState[0]; @@ -621,7 +641,7 @@ public class PowerStatsService extends SystemService { */ @VisibleForTesting public void getPowerMonitorReadingsImpl(@NonNull int[] powerMonitorIndices, - ResultReceiver resultReceiver) { + ResultReceiver resultReceiver, int callingUid) { ensurePowerMonitors(); long earliestTimestamp = Long.MAX_VALUE; @@ -644,15 +664,35 @@ public class PowerStatsService extends SystemService { || mClock.elapsedRealtime() - earliestTimestamp > MAX_POWER_MONITOR_AGE_MILLIS) { updateEnergyConsumers(powerMonitorStates); updateEnergyMeasurements(powerMonitorStates); + mIntervalRandomNoiseGenerator.refresh(); } long[] energy = new long[powerMonitorStates.length]; long[] timestamps = new long[powerMonitorStates.length]; for (int i = 0; i < powerMonitorStates.length; i++) { PowerMonitorState state = powerMonitorStates[i]; - - // TODO(273310268): add random noise - energy[i] = state.energyUws; + if (state.energyUws != PowerMonitorReadings.ENERGY_UNAVAILABLE + && state.prevEnergyUws != PowerMonitorReadings.ENERGY_UNAVAILABLE) { + energy[i] = mIntervalRandomNoiseGenerator.addNoise( + Math.max(state.prevEnergyUws, state.energyUws - MAX_RANDOM_NOISE_UWS), + state.energyUws, callingUid); + if (DEBUG) { + Log.d(TAG, String.format(Locale.ENGLISH, + "Monitor=%s timestamp=%d energy=%d" + + " uid=%d noise=%.1f%% returned=%d", + state.powerMonitor.name, + state.timestampMs, + state.energyUws, + callingUid, + state.energyUws != state.prevEnergyUws + ? (state.energyUws - energy[i]) * 100.0 + / (state.energyUws - state.prevEnergyUws) + : 0, + energy[i])); + } + } else { + energy[i] = state.energyUws; + } timestamps[i] = state.timestampMs; } @@ -678,6 +718,7 @@ public class PowerStatsService extends SystemService { == PowerMonitor.POWER_MONITOR_TYPE_CONSUMER) { for (EnergyConsumerResult energyConsumerResult : energyConsumerResults) { if (energyConsumerResult.id == powerMonitorState.id) { + powerMonitorState.prevEnergyUws = powerMonitorState.energyUws; powerMonitorState.energyUws = energyConsumerResult.energyUWs; powerMonitorState.timestampMs = energyConsumerResult.timestampMs; break; @@ -703,6 +744,7 @@ public class PowerStatsService extends SystemService { == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) { for (EnergyMeasurement energyMeasurement : energyMeasurements) { if (energyMeasurement.id == powerMonitorState.id) { + powerMonitorState.prevEnergyUws = powerMonitorState.energyUws; powerMonitorState.energyUws = energyMeasurement.energyUWs; powerMonitorState.timestampMs = energyMeasurement.timestampMs; break; diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 0034597a1d6aa..2ffe4aacda734 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -99,6 +99,7 @@ public class PowerStatsServiceTest { private static final int POWER_ENTITY_COUNT = 3; private static final int STATE_INFO_COUNT = 5; private static final int STATE_RESIDENCY_COUNT = 4; + private static final int APP_UID = 10042; private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); private PowerStatsService mService; @@ -109,6 +110,7 @@ public class PowerStatsServiceTest { private PowerStatsLogger mPowerStatsLogger; private MockClock mMockClock = new MockClock(); private DeviceConfigInterface mMockDeviceConfig = new FakeDeviceConfigInterface(); + private IntervalRandomNoiseGenerator mMockNoiseGenerator = new IntervalRandomNoiseGenerator(42); private class MockClock extends Clock { public long realtime; @@ -205,6 +207,11 @@ public class PowerStatsServiceTest { DeviceConfigInterface getDeviceConfig() { return mMockDeviceConfig; } + + @Override + IntervalRandomNoiseGenerator createIntervalRandomNoiseGenerator() { + return mMockNoiseGenerator; + } }; public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper { @@ -1102,6 +1109,7 @@ public class PowerStatsServiceTest { @Test public void getPowerMonitors() { mMockClock.realtime = 10 * 60_000; + mMockNoiseGenerator.reseed(314); mPowerStatsHALWrapper.buildEnergyConsumerResult(); EnergyConsumerResult[] energyConsumerResults = mPowerStatsHALWrapper.energyConsumerResults; @@ -1131,7 +1139,7 @@ public class PowerStatsServiceTest { GetPowerMonitorsResult result = new GetPowerMonitorsResult(); mService.getPowerMonitorReadingsImpl( new int[]{consumer1.index, consumer2.index, measurement1.index, - measurement2.index}, result); + measurement2.index}, result, APP_UID); assertThat(result.energyUws).isEqualTo(new long[]{42, 142, 314, 514}); assertThat(result.timestamps).isEqualTo(new long[]{600_000, 600_100, 600_000, 600_200}); @@ -1150,7 +1158,7 @@ public class PowerStatsServiceTest { } mService.getPowerMonitorReadingsImpl(new int[]{consumer1.index, measurement1.index}, - result); + result, APP_UID); assertThat(result.energyUws).isEqualTo(new long[]{42, 314}); assertThat(result.timestamps).isEqualTo(new long[]{600_000, 600_000}); @@ -1158,9 +1166,10 @@ public class PowerStatsServiceTest { mMockClock.realtime += 10 * 60000; mService.getPowerMonitorReadingsImpl(new int[]{consumer1.index, measurement1.index}, - result); + result, APP_UID); - assertThat(result.energyUws).isEqualTo(new long[]{300, 400}); + // This time, random noise is added + assertThat(result.energyUws).isEqualTo(new long[]{298, 399}); assertThat(result.timestamps).isEqualTo(new long[]{600_301, 600_401}); } @@ -1178,7 +1187,7 @@ public class PowerStatsServiceTest { assertThat(supportedPowerMonitorsResult.powerMonitors).isEmpty(); GetPowerMonitorsResult getPowerMonitorsResult = new GetPowerMonitorsResult(); - mService.getPowerMonitorReadingsImpl(new int[]{0}, getPowerMonitorsResult); + mService.getPowerMonitorReadingsImpl(new int[]{0}, getPowerMonitorsResult, APP_UID); assertThat(getPowerMonitorsResult.resultCode).isEqualTo( IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR);