Add random noise to power monitor readings

Bug: 273310268
Test: atest FrameworksServicesTests:PowerStatsServiceTest
Change-Id: I12127999edcff0609fbccee4db91de86ff338838
This commit is contained in:
Dmitri Plotnikov
2023-04-19 15:45:50 -07:00
parent 1ea5f812f7
commit 293b4b3ad7
2 changed files with 62 additions and 11 deletions

View File

@@ -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;

View File

@@ -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);