diff options
author | Dmitri Plotnikov <dplotnikov@google.com> | 2020-11-13 09:46:45 -0800 |
---|---|---|
committer | Dmitri Plotnikov <dplotnikov@google.com> | 2020-12-03 10:42:41 -0800 |
commit | bfe822007b3f70b58eaf0f755d32ce6b59684a31 (patch) | |
tree | 6aa13d4220d2aa70dce187c0238fb7465324daf4 | |
parent | 7c976c677409827bf26864e7a0d5a0d02e03d64a (diff) |
Introduce BatteryUsageStats API
Bug: 158137862
Test: Start Settings app, navigate to Apps, pick an app, navigate to Battery
Change-Id: I8da8cc9501e01282b855f1bee6032911d91aaf77
-rw-r--r-- | core/java/android/os/BatteryConsumer.java | 89 | ||||
-rw-r--r-- | core/java/android/os/BatteryStatsManager.java | 16 | ||||
-rw-r--r-- | core/java/android/os/BatteryUsageStats.aidl | 19 | ||||
-rw-r--r-- | core/java/android/os/BatteryUsageStats.java | 138 | ||||
-rw-r--r-- | core/java/android/os/PowerComponents.java | 170 | ||||
-rw-r--r-- | core/java/android/os/UidBatteryConsumer.java | 145 | ||||
-rw-r--r-- | core/java/com/android/internal/app/IBatteryStats.aidl | 4 | ||||
-rw-r--r-- | core/java/com/android/internal/os/BatteryUsageStatsProvider.java | 76 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/BatteryStatsService.java | 14 | ||||
-rw-r--r-- | tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java | 74 |
10 files changed, 745 insertions, 0 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java new file mode 100644 index 000000000000..d00c3c361722 --- /dev/null +++ b/core/java/android/os/BatteryConsumer.java @@ -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); + } +} diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java index a9585c62866b..d9e01cd4fc7d 100644 --- a/core/java/android/os/BatteryStatsManager.java +++ b/core/java/android/os/BatteryStatsManager.java @@ -161,6 +161,22 @@ public final class BatteryStatsManager { } /** + * 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. * * @param newRssi The new RSSI value. diff --git a/core/java/android/os/BatteryUsageStats.aidl b/core/java/android/os/BatteryUsageStats.aidl new file mode 100644 index 000000000000..0400f19974f1 --- /dev/null +++ b/core/java/android/os/BatteryUsageStats.aidl @@ -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; diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java new file mode 100644 index 000000000000..3f036cdcfa72 --- /dev/null +++ b/core/java/android/os/BatteryUsageStats.java @@ -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; + } + } +} diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java new file mode 100644 index 000000000000..42ba1ff60e5a --- /dev/null +++ b/core/java/android/os/PowerComponents.java @@ -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); + } + } +} diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java new file mode 100644 index 000000000000..7dcbf7d4cef3 --- /dev/null +++ b/core/java/android/os/UidBatteryConsumer.java @@ -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; + } + } +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 6d98a5982c4f..0b047a2a3d47 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -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(); diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java new file mode 100644 index 000000000000..62e9f98181ab --- /dev/null +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -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(); + } +} diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 46e16bcbb1b6..3b6f0ac42ed2 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -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); diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java new file mode 100644 index 000000000000..54d70478f762 --- /dev/null +++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java @@ -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(); + } + } +} |