Merge "Introduce BatteryUsageStats API"

This commit is contained in:
Dmitri Plotnikov
2020-12-04 00:00:46 +00:00
committed by Android (Google) Code Review
10 changed files with 745 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Interface for objects containing battery attribution data.
*
* @hide
*/
public abstract class BatteryConsumer {
/**
* Power usage component, describing the particular part of the system
* responsible for power drain.
*
* @hide
*/
@IntDef(prefix = {"POWER_COMPONENT_"}, value = {
POWER_COMPONENT_CPU,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface PowerComponent {
}
public static final int POWER_COMPONENT_CPU = 0;
public static final int POWER_COMPONENT_COUNT = 1;
public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
private final PowerComponents mPowerComponents;
protected BatteryConsumer(@NonNull PowerComponents powerComponents) {
mPowerComponents = powerComponents;
}
/**
* Total power consumed by this consumer, in mAh.
*/
public double getConsumedPower() {
return mPowerComponents.getTotalPowerConsumed();
}
/**
* Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @return Amount of consumed power in mAh.
*/
public double getConsumedPower(@PowerComponent int componentId) {
return mPowerComponents.getConsumedPower(componentId);
}
/**
* Returns the amount of drain attributed to the specified custom drain type.
*
* @param componentId The ID of the custom power component.
* @return Amount of consumed power in mAh.
*/
public double getConsumedPowerForCustomComponent(int componentId) {
return mPowerComponents.getConsumedPowerForCustomComponent(componentId);
}
protected void writeToParcel(Parcel dest, int flags) {
mPowerComponents.writeToParcel(dest, flags);
}
}

View File

@@ -160,6 +160,22 @@ public final class BatteryStatsManager {
mBatteryStats = batteryStats;
}
/**
* Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
* and per-UID basis.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.BATTERY_STATS)
@NonNull
public BatteryUsageStats getBatteryUsageStats() {
try {
return mBatteryStats.getBatteryUsageStats();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Indicates that the wifi connection RSSI has changed.
*

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
parcelable BatteryUsageStats;

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import java.util.ArrayList;
import java.util.List;
/**
* Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis.
*
* @hide
*/
public final class BatteryUsageStats implements Parcelable {
private final double mConsumedPower;
private final int mDischargePercentage;
private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
private BatteryUsageStats(@NonNull Builder builder) {
mConsumedPower = builder.mConsumedPower;
mDischargePercentage = builder.mDischargePercentage;
mUidBatteryConsumers = builder.mUidBatteryConsumers;
}
/**
* Portion of battery charge drained since BatteryStats reset (e.g. due to being fully
* charged), as percentage of the full charge in the range [0:100]
*/
public int getDischargePercentage() {
return mDischargePercentage;
}
/**
* Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
* charged), in mAh
*/
public double getConsumedPower() {
return mConsumedPower;
}
@NonNull
public List<UidBatteryConsumer> getUidBatteryConsumers() {
return mUidBatteryConsumers;
}
@Override
public int describeContents() {
return 0;
}
private BatteryUsageStats(@NonNull Parcel source) {
mUidBatteryConsumers = new ArrayList<>();
source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
mConsumedPower = source.readDouble();
mDischargePercentage = source.readInt();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelableList(mUidBatteryConsumers, flags);
dest.writeDouble(mConsumedPower);
dest.writeInt(mDischargePercentage);
}
@NonNull
public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() {
public BatteryUsageStats createFromParcel(@NonNull Parcel source) {
return new BatteryUsageStats(source);
}
public BatteryUsageStats[] newArray(int size) {
return new BatteryUsageStats[size];
}
};
/**
* Builder for BatteryUsageStats.
*/
public static final class Builder {
private double mConsumedPower;
private int mDischargePercentage;
private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers = new ArrayList<>();
/**
* Constructs a read-only object using the Builder values.
*/
@NonNull
public BatteryUsageStats build() {
return new BatteryUsageStats(this);
}
/**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
*/
@SuppressLint("PercentageInt") // See b/174188159
@NonNull
public Builder setDischargePercentage(int dischargePercentage) {
mDischargePercentage = dischargePercentage;
return this;
}
/**
* Sets the battery discharge amount since BatteryStats reset, in mAh.
*/
@NonNull
public Builder setConsumedPower(double consumedPower) {
mConsumedPower = consumedPower;
return this;
}
/**
* Adds a UidBatteryConsumer, which represents battery attribution data for an
* individual UID.
*/
@NonNull
public Builder addUidBatteryConsumer(@NonNull UidBatteryConsumer uidBatteryConsumer) {
mUidBatteryConsumers.add(uidBatteryConsumer);
return this;
}
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
/**
* Contains details of battery attribution data broken down to individual power drain types
* such as CPU, RAM, GPU etc.
*
* @hide
*/
class PowerComponents {
private final double mTotalPowerConsumed;
private final double[] mPowerComponents;
PowerComponents(@NonNull Builder builder) {
mTotalPowerConsumed = builder.mTotalPowerConsumed;
mPowerComponents = builder.mPowerComponents;
}
PowerComponents(@NonNull Parcel source) {
mTotalPowerConsumed = source.readDouble();
mPowerComponents = source.createDoubleArray();
}
/** Writes contents to Parcel */
void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeDouble(mTotalPowerConsumed);
dest.writeDoubleArray(mPowerComponents);
}
/**
* Total power consumed by this consumer, in mAh.
*/
public double getTotalPowerConsumed() {
return mTotalPowerConsumed;
}
/**
* Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @return Amount of consumed power in mAh.
*/
public double getConsumedPower(@UidBatteryConsumer.PowerComponent int componentId) {
if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) {
throw new IllegalArgumentException(
"Unsupported power component ID: " + componentId);
}
try {
return mPowerComponents[componentId];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Unsupported power component ID: " + componentId);
}
}
/**
* Returns the amount of drain attributed to the specified custom drain type.
*
* @param componentId The ID of the custom power component.
* @return Amount of consumed power in mAh.
*/
public double getConsumedPowerForCustomComponent(int componentId) {
if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
}
try {
return mPowerComponents[
BatteryConsumer.POWER_COMPONENT_COUNT + componentId
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
}
}
/**
* Builder for PowerComponents.
*/
static final class Builder {
private double mTotalPowerConsumed;
private final double[] mPowerComponents;
Builder(int customPowerComponentCount) {
mPowerComponents = new double[BatteryConsumer.POWER_COMPONENT_COUNT
+ customPowerComponentCount];
}
/**
* Sets the sum amount of power consumed since BatteryStats reset.
*/
@NonNull
public Builder setTotalPowerConsumed(double totalPowerConsumed) {
mTotalPowerConsumed = totalPowerConsumed;
return this;
}
/**
* Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentPower Amount of consumed power in mAh.
*/
@NonNull
public Builder setConsumedPower(@UidBatteryConsumer.PowerComponent int componentId,
double componentPower) {
if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) {
throw new IllegalArgumentException(
"Unsupported power component ID: " + componentId);
}
try {
mPowerComponents[componentId] = componentPower;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported power component ID: " + componentId);
}
return this;
}
/**
* Sets the amount of drain attributed to the specified custom drain type.
*
* @param componentId The ID of the custom power component.
* @param componentPower Amount of consumed power in mAh.
*/
@NonNull
public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
}
try {
mPowerComponents[BatteryConsumer.POWER_COMPONENT_COUNT + componentId
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID] = componentPower;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
}
return this;
}
/**
* Creates a read-only object out of the Builder values.
*/
@NonNull
public PowerComponents build() {
return new PowerComponents(this);
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
/**
* Contains power consumption data attributed to a specific UID.
*
* @hide
*/
public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable {
private final int mUid;
@Nullable
private final String mPackageWithHighestDrain;
public int getUid() {
return mUid;
}
@Nullable
public String getPackageWithHighestDrain() {
return mPackageWithHighestDrain;
}
private UidBatteryConsumer(@NonNull Builder builder) {
super(builder.mPowerComponentsBuilder.build());
mUid = builder.mUid;
mPackageWithHighestDrain = builder.mPackageWithHighestDrain;
}
private UidBatteryConsumer(@NonNull Parcel source) {
super(new PowerComponents(source));
mUid = source.readInt();
mPackageWithHighestDrain = source.readString();
}
/**
* Writes the contents into a Parcel.
*/
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mUid);
dest.writeString(mPackageWithHighestDrain);
}
@NonNull
public static final Creator<UidBatteryConsumer> CREATOR = new Creator<UidBatteryConsumer>() {
public UidBatteryConsumer createFromParcel(@NonNull Parcel source) {
return new UidBatteryConsumer(source);
}
public UidBatteryConsumer[] newArray(int size) {
return new UidBatteryConsumer[size];
}
};
@Override
public int describeContents() {
return 0;
}
/**
* Builder for UidBatteryConsumer.
*/
public static final class Builder {
private final PowerComponents.Builder mPowerComponentsBuilder;
private final int mUid;
private String mPackageWithHighestDrain;
public Builder(int customPowerComponentCount, int uid) {
mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount);
mUid = uid;
}
/**
* Creates a read-only object out of the Builder values.
*/
@NonNull
public UidBatteryConsumer build() {
return new UidBatteryConsumer(this);
}
/**
* Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentPower Amount of consumed power in mAh.
*/
@NonNull
public Builder setConsumedPower(@PowerComponent int componentId, double componentPower) {
mPowerComponentsBuilder.setConsumedPower(componentId, componentPower);
return this;
}
/**
* Sets the amount of drain attributed to the specified custom drain type.
*
* @param componentId The ID of the custom power component.
* @param componentPower Amount of consumed power in mAh.
*/
@NonNull
public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower);
return this;
}
/**
* Sets the amount of power consumed since BatteryStats reset, mAh.
*/
@NonNull
public Builder setConsumedPower(double consumedPower) {
mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
return this;
}
/**
* Sets the name of the package owned by this UID that consumed the highest amount
* of power since BatteryStats reset.
*/
@NonNull
public Builder setPackageWithHighestDrain(@Nullable String packageName) {
mPackageWithHighestDrain = packageName;
return this;
}
}
}

View File

@@ -19,6 +19,7 @@ package com.android.internal.app;
import com.android.internal.os.BatteryStatsImpl;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.os.BatteryUsageStats;
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
@@ -49,6 +50,9 @@ interface IBatteryStats {
void noteResetFlashlight();
// Remaining methods are only used in Java.
BatteryUsageStats getBatteryUsageStats();
@UnsupportedAppUsage
byte[] getStatistics();

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import android.content.Context;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.Bundle;
import android.os.UidBatteryConsumer;
import android.os.UserManager;
import java.util.List;
/**
* Uses accumulated battery stats data and PowerCalculators to produce power
* usage data attributed to subsystems and UIDs.
*/
public class BatteryUsageStatsProvider {
private final Context mContext;
private final BatteryStatsImpl mStats;
public BatteryUsageStatsProvider(Context context, BatteryStatsImpl stats) {
mContext = context;
mStats = stats;
}
/**
* Returns a snapshot of battery attribution data.
*/
public BatteryUsageStats getBatteryUsageStats() {
// TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly.
final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
false /* collectBatteryBroadcast */);
batteryStatsHelper.create((Bundle) null);
final UserManager userManager = mContext.getSystemService(UserManager.class);
batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
userManager.getUserProfiles());
// TODO(b/174186358): read extra power component number from configuration
final int customPowerComponentCount = 0;
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder()
.setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
.setConsumedPower(batteryStatsHelper.getTotalPower());
final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
for (int i = 0; i < usageList.size(); i++) {
final BatterySipper sipper = usageList.get(i);
if (sipper.drainType == BatterySipper.DrainType.APP) {
batteryUsageStatsBuilder.addUidBatteryConsumer(
new UidBatteryConsumer.Builder(customPowerComponentCount, sipper.getUid())
.setPackageWithHighestDrain(sipper.packageWithHighestDrain)
.setConsumedPower(sipper.sumPower())
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
sipper.cpuPowerMah)
.build());
}
}
return batteryUsageStatsBuilder.build();
}
}

View File

@@ -25,6 +25,7 @@ import android.hardware.power.stats.EnergyConsumerId;
import android.hardware.power.stats.EnergyConsumerResult;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -57,6 +58,7 @@ import android.util.Slog;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BatteryUsageStatsProvider;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
@@ -105,6 +107,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private native void getLowPowerStats(RpmStats rpmStats);
private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
@@ -258,6 +261,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
mStats.setPowerProfileLocked(new PowerProfile(context));
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
}
public void publish() {
@@ -550,6 +554,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// Public interface...
/**
* Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
* and per-UID basis.
*/
public BatteryUsageStats getBatteryUsageStats() {
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);
return mBatteryUsageStatsProvider.getBatteryUsageStats();
}
public byte[] getStatistics() {
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.UidBatteryConsumer;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class BatteryUsageStatsPerfTest {
@Rule
public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
/**
* Measures the performance of {@link BatteryStatsManager#getBatteryUsageStats()},
* which triggers a battery stats sync on every iteration.
*/
@Test
public void testGetBatteryUsageStats() {
final Context context = InstrumentationRegistry.getContext();
final BatteryStatsManager batteryStatsManager =
context.getSystemService(BatteryStatsManager.class);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
state.pauseTiming();
List<UidBatteryConsumer> uidBatteryConsumers =
batteryUsageStats.getUidBatteryConsumers();
double power = 0;
for (int i = 0; i < uidBatteryConsumers.size(); i++) {
UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(i);
power += uidBatteryConsumer.getConsumedPower();
}
assertThat(power).isGreaterThan(0.0);
state.resumeTiming();
}
}
}