diff options
| author | 2024-04-10 17:07:05 +0000 | |
|---|---|---|
| committer | 2024-04-10 17:07:05 +0000 | |
| commit | f880e7c89a9c3649e027b5dd9e209a548f9867e3 (patch) | |
| tree | be72055f96a242d2d3910a98bdd68789afd40276 | |
| parent | cb9e08754691d4a90786462103d4b010ab0d8ade (diff) | |
| parent | 99aacb2926839829311d105cc220c83ec9898565 (diff) | |
Merge changes from topic "mobile-radio-power-stats-collector" into main
* changes:
Simplify naming of power stats processors
Include the entire PowerStatsTests source into the Ravenwood version
Export mobile radio and phone call stats to BatteryUsageStats
Introduce PowerStatsProcessor for phone calls
Introduce PowerStatsProcessor for mobile radio
Enable PowerProfile tests in Ravenwood
Introduce PowerStatsCollector for mobile networking
Add support for power component states to PowerStats
64 files changed, 4613 insertions, 1081 deletions
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index eef6ce7619e8..07fa679a428a 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -229,6 +229,17 @@ public final class LongArrayMultiStateCounter implements Parcelable { } /** + * Copies time-in-state and timestamps from the supplied counter. + */ + public void copyStatesFrom(LongArrayMultiStateCounter counter) { + if (mStateCount != counter.mStateCount) { + throw new IllegalArgumentException( + "State count is not the same: " + mStateCount + " vs. " + counter.mStateCount); + } + native_copyStatesFrom(mNativeObject, counter.mNativeObject); + } + + /** * Sets the new values for the given state. */ public void setValues(int state, long[] values) { @@ -376,6 +387,10 @@ public final class LongArrayMultiStateCounter implements Parcelable { private static native void native_setState(long nativeObject, int state, long timestampMs); @CriticalNative + private static native void native_copyStatesFrom(long nativeObjectTarget, + long nativeObjectSource); + + @CriticalNative private static native void native_setValues(long nativeObject, int state, long longArrayContainerNativeObject); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index ab982f5b67cf..9646ae957720 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -18,6 +18,7 @@ package com.android.internal.os; import android.annotation.LongDef; +import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.XmlRes; import android.compat.annotation.UnsupportedAppUsage; @@ -352,19 +353,39 @@ public class PowerProfile { * WARNING: use only for testing! */ @VisibleForTesting - public void forceInitForTesting(Context context, @XmlRes int xmlId) { + public void initForTesting(XmlPullParser parser) { + initForTesting(parser, null); + } + + /** + * Reinitialize the PowerProfile with the provided XML, using optional Resources for fallback + * configuration settings. + * WARNING: use only for testing! + */ + @VisibleForTesting + public void initForTesting(XmlPullParser parser, @Nullable Resources resources) { synchronized (sLock) { sPowerItemMap.clear(); sPowerArrayMap.clear(); sModemPowerProfile.clear(); - initLocked(context, xmlId); + + try { + readPowerValuesFromXml(parser, resources); + } finally { + if (parser instanceof XmlResourceParser) { + ((XmlResourceParser) parser).close(); + } + } + initLocked(); } } @GuardedBy("sLock") private void initLocked(Context context, @XmlRes int xmlId) { if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { - readPowerValuesFromXml(context, xmlId); + final Resources resources = context.getResources(); + XmlResourceParser parser = resources.getXml(xmlId); + readPowerValuesFromXml(parser, resources); } initLocked(); } @@ -377,9 +398,8 @@ public class PowerProfile { initModem(); } - private void readPowerValuesFromXml(Context context, @XmlRes int xmlId) { - final Resources resources = context.getResources(); - XmlResourceParser parser = resources.getXml(xmlId); + private static void readPowerValuesFromXml(XmlPullParser parser, + @Nullable Resources resources) { boolean parsingArray = false; ArrayList<Double> array = new ArrayList<>(); String arrayName = null; @@ -430,9 +450,17 @@ public class PowerProfile { } catch (IOException e) { throw new RuntimeException(e); } finally { - parser.close(); + if (parser instanceof XmlResourceParser) { + ((XmlResourceParser) parser).close(); + } } + if (resources != null) { + getDefaultValuesFromConfig(resources); + } + } + + private static void getDefaultValuesFromConfig(Resources resources) { // Now collect other config variables. int[] configResIds = new int[]{ com.android.internal.R.integer.config_bluetooth_idle_cur_ma, diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java index 56263fb924ea..7c7c7b8fa51d 100644 --- a/core/java/com/android/internal/os/PowerStats.java +++ b/core/java/com/android/internal/os/PowerStats.java @@ -47,7 +47,7 @@ public final class PowerStats { private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER = new BatteryStatsHistory.VarintParceler(); - private static final byte PARCEL_FORMAT_VERSION = 1; + private static final byte PARCEL_FORMAT_VERSION = 2; private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF; private static final int PARCEL_FORMAT_VERSION_SHIFT = @@ -57,7 +57,12 @@ public final class PowerStats { Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK); public static final int MAX_STATS_ARRAY_LENGTH = (1 << Integer.bitCount(STATS_ARRAY_LENGTH_MASK)) - 1; - private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000; + private static final int STATE_STATS_ARRAY_LENGTH_MASK = 0x00FF0000; + private static final int STATE_STATS_ARRAY_LENGTH_SHIFT = + Integer.numberOfTrailingZeros(STATE_STATS_ARRAY_LENGTH_MASK); + public static final int MAX_STATE_STATS_ARRAY_LENGTH = + (1 << Integer.bitCount(STATE_STATS_ARRAY_LENGTH_MASK)) - 1; + private static final int UID_STATS_ARRAY_LENGTH_MASK = 0xFF000000; private static final int UID_STATS_ARRAY_LENGTH_SHIFT = Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK); public static final int MAX_UID_STATS_ARRAY_LENGTH = @@ -74,6 +79,10 @@ public final class PowerStats { private static final String XML_ATTR_ID = "id"; private static final String XML_ATTR_NAME = "name"; private static final String XML_ATTR_STATS_ARRAY_LENGTH = "stats-array-length"; + private static final String XML_TAG_STATE = "state"; + private static final String XML_ATTR_STATE_KEY = "key"; + private static final String XML_ATTR_STATE_LABEL = "label"; + private static final String XML_ATTR_STATE_STATS_ARRAY_LENGTH = "state-stats-array-length"; private static final String XML_ATTR_UID_STATS_ARRAY_LENGTH = "uid-stats-array-length"; private static final String XML_TAG_EXTRAS = "extras"; @@ -85,7 +94,24 @@ public final class PowerStats { public final int powerComponentId; public final String name; + /** + * Stats for the power component, such as the total usage time. + */ public final int statsArrayLength; + + /** + * Map of device state codes to their corresponding human-readable labels. + */ + public final SparseArray<String> stateLabels; + + /** + * Stats for a specific state of the power component, e.g. "mobile radio in the 5G mode" + */ + public final int stateStatsArrayLength; + + /** + * Stats for the usage of this power component by a specific UID (app) + */ public final int uidStatsArrayLength; /** @@ -95,17 +121,25 @@ public final class PowerStats { public final PersistableBundle extras; public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId, - int statsArrayLength, int uidStatsArrayLength, @NonNull PersistableBundle extras) { + int statsArrayLength, @Nullable SparseArray<String> stateLabels, + int stateStatsArrayLength, int uidStatsArrayLength, + @NonNull PersistableBundle extras) { this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId), - statsArrayLength, uidStatsArrayLength, extras); + statsArrayLength, stateLabels, stateStatsArrayLength, uidStatsArrayLength, + extras); } public Descriptor(int customPowerComponentId, String name, int statsArrayLength, + @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength, int uidStatsArrayLength, PersistableBundle extras) { if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH); } + if (stateStatsArrayLength > MAX_STATE_STATS_ARRAY_LENGTH) { + throw new IllegalArgumentException( + "stateStatsArrayLength is too high. Max = " + MAX_STATE_STATS_ARRAY_LENGTH); + } if (uidStatsArrayLength > MAX_UID_STATS_ARRAY_LENGTH) { throw new IllegalArgumentException( "uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH); @@ -113,11 +147,25 @@ public final class PowerStats { this.powerComponentId = customPowerComponentId; this.name = name; this.statsArrayLength = statsArrayLength; + this.stateLabels = stateLabels != null ? stateLabels : new SparseArray<>(); + this.stateStatsArrayLength = stateStatsArrayLength; this.uidStatsArrayLength = uidStatsArrayLength; this.extras = extras; } /** + * Returns the label associated with the give state key, e.g. "5G-high" for the + * state of Mobile Radio representing the 5G mode and high signal power. + */ + public String getStateLabel(int key) { + String label = stateLabels.get(key); + if (label != null) { + return label; + } + return name + "-" + Integer.toHexString(key); + } + + /** * Writes the Descriptor into the parcel. */ public void writeSummaryToParcel(Parcel parcel) { @@ -125,11 +173,18 @@ public final class PowerStats { & PARCEL_FORMAT_VERSION_MASK) | ((statsArrayLength << STATS_ARRAY_LENGTH_SHIFT) & STATS_ARRAY_LENGTH_MASK) + | ((stateStatsArrayLength << STATE_STATS_ARRAY_LENGTH_SHIFT) + & STATE_STATS_ARRAY_LENGTH_MASK) | ((uidStatsArrayLength << UID_STATS_ARRAY_LENGTH_SHIFT) & UID_STATS_ARRAY_LENGTH_MASK); parcel.writeInt(firstWord); parcel.writeInt(powerComponentId); parcel.writeString(name); + parcel.writeInt(stateLabels.size()); + for (int i = 0, size = stateLabels.size(); i < size; i++) { + parcel.writeInt(stateLabels.keyAt(i)); + parcel.writeString(stateLabels.valueAt(i)); + } extras.writeToParcel(parcel, 0); } @@ -148,13 +203,22 @@ public final class PowerStats { } int statsArrayLength = (firstWord & STATS_ARRAY_LENGTH_MASK) >>> STATS_ARRAY_LENGTH_SHIFT; + int stateStatsArrayLength = + (firstWord & STATE_STATS_ARRAY_LENGTH_MASK) >>> STATE_STATS_ARRAY_LENGTH_SHIFT; int uidStatsArrayLength = (firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT; int powerComponentId = parcel.readInt(); String name = parcel.readString(); + int stateLabelCount = parcel.readInt(); + SparseArray<String> stateLabels = new SparseArray<>(stateLabelCount); + for (int i = stateLabelCount; i > 0; i--) { + int key = parcel.readInt(); + String label = parcel.readString(); + stateLabels.put(key, label); + } PersistableBundle extras = parcel.readPersistableBundle(); - return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength, - extras); + return new Descriptor(powerComponentId, name, statsArrayLength, stateLabels, + stateStatsArrayLength, uidStatsArrayLength, extras); } @Override @@ -163,11 +227,13 @@ public final class PowerStats { if (!(o instanceof Descriptor)) return false; Descriptor that = (Descriptor) o; return powerComponentId == that.powerComponentId - && statsArrayLength == that.statsArrayLength - && uidStatsArrayLength == that.uidStatsArrayLength - && Objects.equals(name, that.name) - && extras.size() == that.extras.size() // Unparcel the Parcel if not yet - && Bundle.kindofEquals(extras, + && statsArrayLength == that.statsArrayLength + && stateLabels.contentEquals(that.stateLabels) + && stateStatsArrayLength == that.stateStatsArrayLength + && uidStatsArrayLength == that.uidStatsArrayLength + && Objects.equals(name, that.name) + && extras.size() == that.extras.size() // Unparcel the Parcel if not yet + && Bundle.kindofEquals(extras, that.extras); // Since the Parcel is now unparceled, do a deep comparison } @@ -179,7 +245,14 @@ public final class PowerStats { serializer.attributeInt(null, XML_ATTR_ID, powerComponentId); serializer.attribute(null, XML_ATTR_NAME, name); serializer.attributeInt(null, XML_ATTR_STATS_ARRAY_LENGTH, statsArrayLength); + serializer.attributeInt(null, XML_ATTR_STATE_STATS_ARRAY_LENGTH, stateStatsArrayLength); serializer.attributeInt(null, XML_ATTR_UID_STATS_ARRAY_LENGTH, uidStatsArrayLength); + for (int i = stateLabels.size() - 1; i >= 0; i--) { + serializer.startTag(null, XML_TAG_STATE); + serializer.attributeInt(null, XML_ATTR_STATE_KEY, stateLabels.keyAt(i)); + serializer.attribute(null, XML_ATTR_STATE_LABEL, stateLabels.valueAt(i)); + serializer.endTag(null, XML_TAG_STATE); + } try { serializer.startTag(null, XML_TAG_EXTRAS); extras.saveToXml(serializer); @@ -199,6 +272,8 @@ public final class PowerStats { int powerComponentId = -1; String name = null; int statsArrayLength = 0; + SparseArray<String> stateLabels = new SparseArray<>(); + int stateStatsArrayLength = 0; int uidStatsArrayLength = 0; PersistableBundle extras = null; int eventType = parser.getEventType(); @@ -212,9 +287,16 @@ public final class PowerStats { name = parser.getAttributeValue(null, XML_ATTR_NAME); statsArrayLength = parser.getAttributeInt(null, XML_ATTR_STATS_ARRAY_LENGTH); + stateStatsArrayLength = parser.getAttributeInt(null, + XML_ATTR_STATE_STATS_ARRAY_LENGTH); uidStatsArrayLength = parser.getAttributeInt(null, XML_ATTR_UID_STATS_ARRAY_LENGTH); break; + case XML_TAG_STATE: + int value = parser.getAttributeInt(null, XML_ATTR_STATE_KEY); + String label = parser.getAttributeValue(null, XML_ATTR_STATE_LABEL); + stateLabels.put(value, label); + break; case XML_TAG_EXTRAS: extras = PersistableBundle.restoreFromXml(parser); break; @@ -225,11 +307,11 @@ public final class PowerStats { if (powerComponentId == -1) { return null; } else if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { - return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength, - extras); + return new Descriptor(powerComponentId, name, statsArrayLength, + stateLabels, stateStatsArrayLength, uidStatsArrayLength, extras); } else if (powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT) { - return new Descriptor(powerComponentId, statsArrayLength, uidStatsArrayLength, - extras); + return new Descriptor(powerComponentId, statsArrayLength, stateLabels, + stateStatsArrayLength, uidStatsArrayLength, extras); } else { Slog.e(TAG, "Unrecognized power component: " + powerComponentId); return null; @@ -247,12 +329,14 @@ public final class PowerStats { extras.size(); // Unparcel } return "PowerStats.Descriptor{" - + "powerComponentId=" + powerComponentId - + ", name='" + name + '\'' - + ", statsArrayLength=" + statsArrayLength - + ", uidStatsArrayLength=" + uidStatsArrayLength - + ", extras=" + extras - + '}'; + + "powerComponentId=" + powerComponentId + + ", name='" + name + '\'' + + ", statsArrayLength=" + statsArrayLength + + ", stateStatsArrayLength=" + stateStatsArrayLength + + ", stateLabels=" + stateLabels + + ", uidStatsArrayLength=" + uidStatsArrayLength + + ", extras=" + extras + + '}'; } } @@ -293,6 +377,12 @@ public final class PowerStats { public long[] stats; /** + * Device-wide mode stats, used when the power component can operate in different modes, + * e.g. RATs such as LTE and 5G. + */ + public final SparseArray<long[]> stateStats = new SparseArray<>(); + + /** * Per-UID CPU stats. */ public final SparseArray<long[]> uidStats = new SparseArray<>(); @@ -313,6 +403,15 @@ public final class PowerStats { parcel.writeInt(descriptor.powerComponentId); parcel.writeLong(durationMs); VARINT_PARCELER.writeLongArray(parcel, stats); + + if (descriptor.stateStatsArrayLength != 0) { + parcel.writeInt(stateStats.size()); + for (int i = 0; i < stateStats.size(); i++) { + parcel.writeInt(stateStats.keyAt(i)); + VARINT_PARCELER.writeLongArray(parcel, stateStats.valueAt(i)); + } + } + parcel.writeInt(uidStats.size()); for (int i = 0; i < uidStats.size(); i++) { parcel.writeInt(uidStats.keyAt(i)); @@ -347,6 +446,17 @@ public final class PowerStats { stats.durationMs = parcel.readLong(); stats.stats = new long[descriptor.statsArrayLength]; VARINT_PARCELER.readLongArray(parcel, stats.stats); + + if (descriptor.stateStatsArrayLength != 0) { + int count = parcel.readInt(); + for (int i = 0; i < count; i++) { + int state = parcel.readInt(); + long[] stateStats = new long[descriptor.stateStatsArrayLength]; + VARINT_PARCELER.readLongArray(parcel, stateStats); + stats.stateStats.put(state, stateStats); + } + } + int uidCount = parcel.readInt(); for (int i = 0; i < uidCount; i++) { int uid = parcel.readInt(); @@ -376,6 +486,14 @@ public final class PowerStats { if (stats.length > 0) { sb.append("=").append(Arrays.toString(stats)); } + if (descriptor.stateStatsArrayLength != 0) { + for (int i = 0; i < stateStats.size(); i++) { + sb.append(" ["); + sb.append(descriptor.getStateLabel(stateStats.keyAt(i))); + sb.append("]="); + sb.append(Arrays.toString(stateStats.valueAt(i))); + } + } for (int i = 0; i < uidStats.size(); i++) { sb.append(uidPrefix) .append(UserHandle.formatUid(uidStats.keyAt(i))) @@ -391,6 +509,18 @@ public final class PowerStats { pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')'); pw.increaseIndent(); pw.print("duration", durationMs).println(); + if (descriptor.statsArrayLength != 0) { + pw.print("stats", Arrays.toString(stats)).println(); + } + if (descriptor.stateStatsArrayLength != 0) { + for (int i = 0; i < stateStats.size(); i++) { + pw.print("state "); + pw.print(descriptor.getStateLabel(stateStats.keyAt(i))); + pw.print(": "); + pw.print(Arrays.toString(stateStats.valueAt(i))); + pw.println(); + } + } for (int i = 0; i < uidStats.size(); i++) { pw.print("UID "); pw.print(uidStats.keyAt(i)); diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java index b15c10e6ba20..64d3139561b6 100644 --- a/core/java/com/android/internal/power/ModemPowerProfile.java +++ b/core/java/com/android/internal/power/ModemPowerProfile.java @@ -17,14 +17,16 @@ package com.android.internal.power; import android.annotation.IntDef; -import android.content.res.XmlResourceParser; +import android.os.BatteryStats; import android.telephony.ModemActivityInfo; import android.telephony.ServiceState; import android.telephony.TelephonyManager; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseDoubleArray; +import com.android.internal.os.PowerProfile; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -95,6 +97,8 @@ public class ModemPowerProfile { */ public static final int MODEM_DRAIN_TYPE_TX = 0x3000_0000; + private static final int IGNORE = -1; + @IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = { MODEM_DRAIN_TYPE_SLEEP, MODEM_DRAIN_TYPE_IDLE, @@ -256,7 +260,7 @@ public class ModemPowerProfile { /** * Generates a ModemPowerProfile object from the <modem /> element of a power_profile.xml */ - public void parseFromXml(XmlResourceParser parser) throws IOException, + public void parseFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { @@ -286,7 +290,7 @@ public class ModemPowerProfile { } /** Parse the <active /> XML element */ - private void parseActivePowerConstantsFromXml(XmlResourceParser parser) + private void parseActivePowerConstantsFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { // Parse attributes to get the type of active modem usage the power constants are for. final int ratType; @@ -339,7 +343,7 @@ public class ModemPowerProfile { } } - private static int getTypeFromAttribute(XmlResourceParser parser, String attr, + private static int getTypeFromAttribute(XmlPullParser parser, String attr, SparseArray<String> names) { final String value = XmlUtils.readStringAttribute(parser, attr); if (value == null) { @@ -382,6 +386,84 @@ public class ModemPowerProfile { } } + public static long getAverageBatteryDrainKey(@ModemDrainType int drainType, + @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, + int txLevel) { + long key = PowerProfile.SUBSYSTEM_MODEM; + + // Attach Modem drain type to the key if specified. + if (drainType != IGNORE) { + key |= drainType; + } + + // Attach RadioAccessTechnology to the key if specified. + switch (rat) { + case IGNORE: + // do nothing + break; + case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER: + key |= MODEM_RAT_TYPE_DEFAULT; + break; + case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE: + key |= MODEM_RAT_TYPE_LTE; + break; + case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR: + key |= MODEM_RAT_TYPE_NR; + break; + default: + Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat); + } + + // Attach NR Frequency Range to the key if specified. + switch (freqRange) { + case IGNORE: + // do nothing + break; + case ServiceState.FREQUENCY_RANGE_UNKNOWN: + key |= MODEM_NR_FREQUENCY_RANGE_DEFAULT; + break; + case ServiceState.FREQUENCY_RANGE_LOW: + key |= MODEM_NR_FREQUENCY_RANGE_LOW; + break; + case ServiceState.FREQUENCY_RANGE_MID: + key |= MODEM_NR_FREQUENCY_RANGE_MID; + break; + case ServiceState.FREQUENCY_RANGE_HIGH: + key |= MODEM_NR_FREQUENCY_RANGE_HIGH; + break; + case ServiceState.FREQUENCY_RANGE_MMWAVE: + key |= MODEM_NR_FREQUENCY_RANGE_MMWAVE; + break; + default: + Log.w(TAG, "Unexpected NR frequency range : " + freqRange); + } + + // Attach transmission level to the key if specified. + switch (txLevel) { + case IGNORE: + // do nothing + break; + case 0: + key |= MODEM_TX_LEVEL_0; + break; + case 1: + key |= MODEM_TX_LEVEL_1; + break; + case 2: + key |= MODEM_TX_LEVEL_2; + break; + case 3: + key |= MODEM_TX_LEVEL_3; + break; + case 4: + key |= MODEM_TX_LEVEL_4; + break; + default: + Log.w(TAG, "Unexpected transmission level : " + txLevel); + } + return key; + } + /** * Returns the average battery drain in milli-amps of the modem for a given drain type. * Returns {@link Double.NaN} if a suitable value is not found for the given key. @@ -444,6 +526,7 @@ public class ModemPowerProfile { } return sb.toString(); } + private static void appendFieldToString(StringBuilder sb, String fieldName, SparseArray<String> names, int key) { sb.append(fieldName); diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp index 76b05eac82af..b3c41dfe81a1 100644 --- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp +++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp @@ -55,6 +55,14 @@ static void native_setState(jlong nativePtr, jint state, jlong timestamp) { counter->setState(state, timestamp); } +static void native_copyStatesFrom(jlong nativePtrTarget, jlong nativePtrSource) { + battery::LongArrayMultiStateCounter *counterTarget = + reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrTarget); + battery::LongArrayMultiStateCounter *counterSource = + reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrSource); + counterTarget->copyStatesFrom(*counterSource); +} + static void native_setValues(jlong nativePtr, jint state, jlong longArrayContainerNativePtr) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); @@ -219,6 +227,8 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = { // @CriticalNative {"native_setState", "(JIJ)V", (void *)native_setState}, // @CriticalNative + {"native_copyStatesFrom", "(JJ)V", (void *)native_copyStatesFrom}, + // @CriticalNative {"native_setValues", "(JIJ)V", (void *)native_setValues}, // @CriticalNative {"native_updateValues", "(JJJ)V", (void *)native_updateValues}, diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml index e42962ce9195..ae4789937832 100644 --- a/core/res/res/values/config_battery_stats.xml +++ b/core/res/res/values/config_battery_stats.xml @@ -32,6 +32,9 @@ devices--> <integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer> + <!-- Mobile Radio power stats collection throttle period in milliseconds. --> + <integer name="config_defaultPowerStatsThrottlePeriodMobileRadio">3600000</integer> + <!-- PowerStats aggregation period in milliseconds. This is the interval at which the power stats aggregation procedure is performed and the results stored in PowerStatsStore. --> <integer name="config_powerStatsAggregationPeriod">14400000</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 509a0dac7abb..9e0954093eb2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5216,6 +5216,7 @@ <java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" /> <java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" /> <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" /> + <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodMobileRadio" /> <java-symbol type="integer" name="config_powerStatsAggregationPeriod" /> <java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" /> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 48082048691c..404e8731d5c5 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -267,5 +267,10 @@ android_ravenwood_test { generate_get_transaction_name: true, local_include_dirs: ["aidl"], }, + java_resources: [ + "res/xml/power_profile_test.xml", + "res/xml/power_profile_test_cpu_legacy.xml", + "res/xml/power_profile_test_modem.xml", + ], auto_gen_config: true, } diff --git a/core/tests/coretests/res/xml/power_profile_test.xml b/core/tests/coretests/res/xml/power_profile_test.xml index 322ae05bc63e..7356c9e38012 100644 --- a/core/tests/coretests/res/xml/power_profile_test.xml +++ b/core/tests/coretests/res/xml/power_profile_test.xml @@ -98,4 +98,16 @@ <value>40</value> <value>50</value> </array> -</device>
\ No newline at end of file + + <!-- Idle current for bluetooth in mA.--> + <item name="bluetooth.controller.idle">0.02</item> + + <!-- Rx current for bluetooth in mA.--> + <item name="bluetooth.controller.rx">3</item> + + <!-- Tx current for bluetooth in mA--> + <item name="bluetooth.controller.tx">5</item> + + <!-- Operating voltage for bluetooth in mV.--> + <item name="bluetooth.controller.voltage">3300</item> +</device> diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java index 533b799341c1..fa5d72a04b88 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java @@ -55,6 +55,21 @@ public class LongArrayMultiStateCounterTest { } @Test + public void copyStatesFrom() { + LongArrayMultiStateCounter source = new LongArrayMultiStateCounter(2, 1); + updateValue(source, new long[]{0}, 1000); + source.setState(0, 1000); + source.setState(1, 2000); + + LongArrayMultiStateCounter target = new LongArrayMultiStateCounter(2, 1); + target.copyStatesFrom(source); + updateValue(target, new long[]{1000}, 5000); + + assertCounts(target, 0, new long[]{250}); + assertCounts(target, 1, new long[]{750}); + } + + @Test public void setValue() { LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4); diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java index c0f0714e52cc..951fa98caf27 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -21,20 +21,21 @@ import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT; import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL; import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import android.annotation.XmlRes; import android.content.Context; import android.content.res.Resources; -import android.content.res.XmlResourceParser; -import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.ravenwood.RavenwoodRule; +import android.util.Xml; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.frameworks.coretests.R; import com.android.internal.power.ModemPowerProfile; import com.android.internal.util.XmlUtils; @@ -43,6 +44,11 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.StringReader; /* * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml and @@ -53,7 +59,6 @@ import org.junit.runner.RunWith; */ @SmallTest @RunWith(AndroidJUnit4.class) -@IgnoreUnderRavenwood(blockedBy = PowerProfile.class) public class PowerProfileTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); @@ -62,17 +67,15 @@ public class PowerProfileTest { static final String ATTR_NAME = "name"; private PowerProfile mProfile; - private Context mContext; @Before public void setUp() { - mContext = InstrumentationRegistry.getContext(); - mProfile = new PowerProfile(mContext); + mProfile = new PowerProfile(); } @Test public void testPowerProfile() { - mProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mProfile.initForTesting(resolveParser("power_profile_test")); assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND)); assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)); @@ -127,11 +130,36 @@ public class PowerProfileTest { PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(0.02, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE)); + assertEquals(3, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX)); + assertEquals(5, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX)); + assertEquals(3300, mProfile.getAveragePower( + PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)); + } + + @DisabledOnRavenwood + @Test + public void configDefaults() throws XmlPullParserException { + Resources mockResources = mock(Resources.class); + when(mockResources.getInteger(com.android.internal.R.integer.config_bluetooth_rx_cur_ma)) + .thenReturn(123); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new StringReader( + "<device name='Android'>" + + "<item name='bluetooth.controller.idle'>10</item>" + + "</device>")); + mProfile.initForTesting(parser, mockResources); + assertThat(mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE)) + .isEqualTo(10); + assertThat(mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX)) + .isEqualTo(123); } + @Test public void testPowerProfile_legacyCpuConfig() { // This power profile has per-cluster data, rather than per-policy - mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_cpu_legacy); + mProfile.initForTesting(resolveParser("power_profile_test_cpu_legacy")); assertEquals(2.11, mProfile.getAveragePowerForCpuScalingPolicy(0)); assertEquals(2.22, mProfile.getAveragePowerForCpuScalingPolicy(4)); @@ -148,7 +176,7 @@ public class PowerProfileTest { @Test public void testModemPowerProfile_defaultRat() throws Exception { - final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem, + final XmlPullParser parser = getTestModemElement("power_profile_test_modem", "testModemPowerProfile_defaultRat"); ModemPowerProfile mpp = new ModemPowerProfile(); mpp.parseFromXml(parser); @@ -216,7 +244,7 @@ public class PowerProfileTest { @Test public void testModemPowerProfile_partiallyDefined() throws Exception { - final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem, + final XmlPullParser parser = getTestModemElement("power_profile_test_modem", "testModemPowerProfile_partiallyDefined"); ModemPowerProfile mpp = new ModemPowerProfile(); mpp.parseFromXml(parser); @@ -369,7 +397,7 @@ public class PowerProfileTest { @Test public void testModemPowerProfile_fullyDefined() throws Exception { - final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem, + final XmlPullParser parser = getTestModemElement("power_profile_test_modem", "testModemPowerProfile_fullyDefined"); ModemPowerProfile mpp = new ModemPowerProfile(); mpp.parseFromXml(parser); @@ -519,11 +547,10 @@ public class PowerProfileTest { | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); } - private XmlResourceParser getTestModemElement(@XmlRes int xmlId, String elementName) + private XmlPullParser getTestModemElement(String resourceName, String elementName) throws Exception { + XmlPullParser parser = resolveParser(resourceName); final String element = TAG_TEST_MODEM; - final Resources resources = mContext.getResources(); - XmlResourceParser parser = resources.getXml(xmlId); while (true) { XmlUtils.nextElement(parser); final String e = parser.getName(); @@ -535,10 +562,26 @@ public class PowerProfileTest { return parser; } - fail("Unanable to find element " + element + " with name " + elementName); + fail("Unable to find element " + element + " with name " + elementName); return null; } + private XmlPullParser resolveParser(String resourceName) { + if (RavenwoodRule.isOnRavenwood()) { + try { + return Xml.resolvePullParser(getClass().getClassLoader() + .getResourceAsStream("res/xml/" + resourceName + ".xml")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + Context context = androidx.test.InstrumentationRegistry.getContext(); + Resources resources = context.getResources(); + int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName()); + return resources.getXml(resId); + } + } + private void assertEquals(double expected, double actual) { Assert.assertEquals(expected, actual, 0.1); } diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java index b99e2026ef26..6402206410b5 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java @@ -21,20 +21,27 @@ import static com.google.common.truth.Truth.assertThat; import android.os.BatteryConsumer; import android.os.Parcel; import android.os.PersistableBundle; -import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; +import android.util.SparseArray; +import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + @RunWith(AndroidJUnit4.class) @SmallTest -@IgnoreUnderRavenwood(reason = "Needs kernel support") public class PowerStatsTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); @@ -47,7 +54,10 @@ public class PowerStatsTest { mRegistry = new PowerStats.DescriptorRegistry(); PersistableBundle extras = new PersistableBundle(); extras.putBoolean("hasPowerMonitor", true); - mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras); + SparseArray<String> stateLabels = new SparseArray<>(); + stateLabels.put(0x0F, "idle"); + mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels, + 1, 2, extras); mRegistry.register(mDescriptor); } @@ -58,6 +68,8 @@ public class PowerStatsTest { stats.stats[0] = 10; stats.stats[1] = 20; stats.stats[2] = 30; + stats.stateStats.put(0x0F, new long[]{16}); + stats.stateStats.put(0xF0, new long[]{17}); stats.uidStats.put(42, new long[]{40, 50}); stats.uidStats.put(99, new long[]{60, 70}); @@ -73,6 +85,7 @@ public class PowerStatsTest { assertThat(newDescriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU); assertThat(newDescriptor.name).isEqualTo("cpu"); assertThat(newDescriptor.statsArrayLength).isEqualTo(3); + assertThat(newDescriptor.stateStatsArrayLength).isEqualTo(1); assertThat(newDescriptor.uidStatsArrayLength).isEqualTo(2); assertThat(newDescriptor.extras.getBoolean("hasPowerMonitor")).isTrue(); @@ -81,6 +94,11 @@ public class PowerStatsTest { PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry); assertThat(newStats.durationMs).isEqualTo(1234); assertThat(newStats.stats).isEqualTo(new long[]{10, 20, 30}); + assertThat(newStats.stateStats.size()).isEqualTo(2); + assertThat(newStats.stateStats.get(0x0F)).isEqualTo(new long[]{16}); + assertThat(newStats.descriptor.getStateLabel(0x0F)).isEqualTo("idle"); + assertThat(newStats.stateStats.get(0xF0)).isEqualTo(new long[]{17}); + assertThat(newStats.descriptor.getStateLabel(0xF0)).isEqualTo("cpu-f0"); assertThat(newStats.uidStats.size()).isEqualTo(2); assertThat(newStats.uidStats.get(42)).isEqualTo(new long[]{40, 50}); assertThat(newStats.uidStats.get(99)).isEqualTo(new long[]{60, 70}); @@ -90,9 +108,33 @@ public class PowerStatsTest { } @Test + public void xmlFormat() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TypedXmlSerializer serializer = Xml.newBinarySerializer(); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); + mDescriptor.writeXml(serializer); + serializer.flush(); + + byte[] bytes = out.toByteArray(); + + TypedXmlPullParser parser = Xml.newBinaryPullParser(); + parser.setInput(new ByteArrayInputStream(bytes), StandardCharsets.UTF_8.name()); + PowerStats.Descriptor actual = PowerStats.Descriptor.createFromXml(parser); + + assertThat(actual.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU); + assertThat(actual.name).isEqualTo("cpu"); + assertThat(actual.statsArrayLength).isEqualTo(3); + assertThat(actual.stateStatsArrayLength).isEqualTo(1); + assertThat(actual.getStateLabel(0x0F)).isEqualTo("idle"); + assertThat(actual.getStateLabel(0xF0)).isEqualTo("cpu-f0"); + assertThat(actual.uidStatsArrayLength).isEqualTo(2); + assertThat(actual.extras.getBoolean("hasPowerMonitor")).isEqualTo(true); + } + + @Test public void parceling_unrecognizedPowerComponent() { PowerStats stats = new PowerStats( - new PowerStats.Descriptor(777, "luck", 3, 2, new PersistableBundle())); + new PowerStats.Descriptor(777, "luck", 3, null, 1, 2, new PersistableBundle())); stats.durationMs = 1234; Parcel parcel = Parcel.obtain(); diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java index 741411095f53..0f65544f8b66 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java @@ -100,6 +100,16 @@ public class LongArrayMultiStateCounter_host { mLastStateChangeTimestampMs = timestampMs; } + public void copyStatesFrom(LongArrayMultiStateCounterRavenwood source) { + for (int i = 0; i < mStateCount; i++) { + mStates[i].mTimeInStateSinceUpdate = source.mStates[i].mTimeInStateSinceUpdate; + Arrays.fill(mStates[i].mCounter, 0); + } + mCurrentState = source.mCurrentState; + mLastStateChangeTimestampMs = source.mLastStateChangeTimestampMs; + mLastUpdateTimestampMs = source.mLastUpdateTimestampMs; + } + public void setValue(int state, long[] values) { System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength); } @@ -335,6 +345,10 @@ public class LongArrayMultiStateCounter_host { getInstance(instanceId).setState(state, timestampMs); } + public static void native_copyStatesFrom(long targetInstanceId, long sourceInstanceId) { + getInstance(targetInstanceId).copyStatesFrom(getInstance(sourceInstanceId)); + } + public static void native_incrementValues(long instanceId, long containerInstanceId, long timestampMs) { getInstance(instanceId).incrementValues( diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 4f46ecdb9228..f98799dd3723 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -124,7 +124,9 @@ import com.android.server.power.stats.BatteryExternalStatsWorker; import com.android.server.power.stats.BatteryStatsDumpHelperImpl; import com.android.server.power.stats.BatteryStatsImpl; import com.android.server.power.stats.BatteryUsageStatsProvider; -import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor; +import com.android.server.power.stats.CpuPowerStatsProcessor; +import com.android.server.power.stats.MobileRadioPowerStatsProcessor; +import com.android.server.power.stats.PhoneCallPowerStatsProcessor; import com.android.server.power.stats.PowerStatsAggregator; import com.android.server.power.stats.PowerStatsExporter; import com.android.server.power.stats.PowerStatsScheduler; @@ -408,11 +410,18 @@ public final class BatteryStatsService extends IBatteryStats.Stub com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge); final long powerStatsThrottlePeriodCpu = context.getResources().getInteger( com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu); + final long powerStatsThrottlePeriodMobileRadio = context.getResources().getInteger( + com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodMobileRadio); mBatteryStatsConfig = new BatteryStatsImpl.BatteryStatsConfig.Builder() .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel) .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge) - .setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu) + .setPowerStatsThrottlePeriodMillis( + BatteryConsumer.POWER_COMPONENT_CPU, + powerStatsThrottlePeriodCpu) + .setPowerStatsThrottlePeriodMillis( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, + powerStatsThrottlePeriodMobileRadio) .build(); mPowerStatsUidResolver = new PowerStatsUidResolver(); mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock, @@ -470,7 +479,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessor( - new CpuAggregatedPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies)); + new CpuPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies)); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new MobileRadioPowerStatsProcessor(mPowerProfile)); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE, + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) + .setProcessor(new PhoneCallPowerStatsProcessor()); return config; } @@ -494,8 +516,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub } public void systemServicesReady() { - mStats.setPowerStatsCollectorEnabled(Flags.streamlinedBatteryStats()); - mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats()); + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CPU, + Flags.streamlinedBatteryStats()); + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, + Flags.streamlinedConnectivityBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_CPU, + Flags.streamlinedBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, + Flags.streamlinedConnectivityBatteryStats()); mWorker.systemServicesReady(); mStats.systemServicesReady(mContext); mCpuWakeupStats.systemServicesReady(); @@ -536,7 +566,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub * Notifies BatteryStatsService that the system server is ready. */ public void onSystemReady() { - mStats.onSystemReady(); + mStats.onSystemReady(mContext); mPowerStatsScheduler.start(Flags.streamlinedBatteryStats()); } @@ -1591,19 +1621,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { - final boolean update; synchronized (mStats) { // Ignore if no power state change. if (mLastPowerStateFromRadio == powerState) return; mLastPowerStateFromRadio = powerState; - update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid, + mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid, elapsedRealtime, uptime); } - - if (update) { - mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO); - } }); } FrameworkStatsLog.write_non_chained( diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java index 894226cf32c9..e1b4b88ed1df 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java @@ -34,11 +34,14 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.StringWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.TimeZone; /** * This class represents aggregated power stats for a variety of power components (CPU, WiFi, @@ -66,7 +69,7 @@ class AggregatedPowerStats { aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs(); mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()]; for (int i = 0; i < configs.size(); i++) { - mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(configs.get(i)); + mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(this, configs.get(i)); } } @@ -223,7 +226,7 @@ class AggregatedPowerStats { if (i == 0) { baseTime = clockUpdate.monotonicTime; sb.append("Start time: ") - .append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime)) + .append(formatDateTime(clockUpdate.currentTime)) .append(" (") .append(baseTime) .append(") duration: ") @@ -235,8 +238,7 @@ class AggregatedPowerStats { TimeUtils.formatDuration( clockUpdate.monotonicTime - baseTime, sb, TimeUtils.HUNDRED_DAY_FIELD_LEN + 3); - sb.append(" ").append( - DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime)); + sb.append(" ").append(formatDateTime(clockUpdate.currentTime)); ipw.increaseIndent(); ipw.println(sb); ipw.decreaseIndent(); @@ -267,6 +269,12 @@ class AggregatedPowerStats { } } + private static String formatDateTime(long timeInMillis) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT")); + return format.format(new Date(timeInMillis)); + } + @Override public String toString() { StringWriter sw = new StringWriter(); diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java index 6fbbc0f072e8..5aad570ffd41 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java @@ -75,7 +75,7 @@ public class AggregatedPowerStatsConfig { private final int mPowerComponentId; private @TrackedState int[] mTrackedDeviceStates; private @TrackedState int[] mTrackedUidStates; - private AggregatedPowerStatsProcessor mProcessor = NO_OP_PROCESSOR; + private PowerStatsProcessor mProcessor = NO_OP_PROCESSOR; PowerComponent(int powerComponentId) { this.mPowerComponentId = powerComponentId; @@ -85,6 +85,9 @@ public class AggregatedPowerStatsConfig { * Configures which states should be tracked as separate dimensions for the entire device. */ public PowerComponent trackDeviceStates(@TrackedState int... states) { + if (mTrackedDeviceStates != null) { + throw new IllegalStateException("Component is already configured"); + } mTrackedDeviceStates = states; return this; } @@ -93,6 +96,9 @@ public class AggregatedPowerStatsConfig { * Configures which states should be tracked as separate dimensions on a per-UID basis. */ public PowerComponent trackUidStates(@TrackedState int... states) { + if (mTrackedUidStates != null) { + throw new IllegalStateException("Component is already configured"); + } mTrackedUidStates = states; return this; } @@ -102,7 +108,7 @@ public class AggregatedPowerStatsConfig { * before giving the aggregates stats to consumers. The processor can complete the * aggregation process, for example by computing estimated power usage. */ - public PowerComponent setProcessor(@NonNull AggregatedPowerStatsProcessor processor) { + public PowerComponent setProcessor(@NonNull PowerStatsProcessor processor) { mProcessor = processor; return this; } @@ -137,7 +143,7 @@ public class AggregatedPowerStatsConfig { } @NonNull - public AggregatedPowerStatsProcessor getProcessor() { + public PowerStatsProcessor getProcessor() { return mProcessor; } @@ -153,6 +159,7 @@ public class AggregatedPowerStatsConfig { } return false; } + } private final List<PowerComponent> mPowerComponents = new ArrayList<>(); @@ -168,23 +175,55 @@ public class AggregatedPowerStatsConfig { return builder; } + /** + * Creates a configuration for the specified power component, which is a subcomponent + * of a different power component. The tracked states will be the same as the parent + * component's. + */ + public PowerComponent trackPowerComponent(int powerComponentId, + int parentPowerComponentId) { + PowerComponent parent = null; + for (int i = 0; i < mPowerComponents.size(); i++) { + PowerComponent powerComponent = mPowerComponents.get(i); + if (powerComponent.getPowerComponentId() == parentPowerComponentId) { + parent = powerComponent; + break; + } + } + + if (parent == null) { + throw new IllegalArgumentException( + "Parent component " + parentPowerComponentId + " is not configured"); + } + + PowerComponent powerComponent = trackPowerComponent(powerComponentId); + powerComponent.mTrackedDeviceStates = parent.mTrackedDeviceStates; + powerComponent.mTrackedUidStates = parent.mTrackedUidStates; + return powerComponent; + } + public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() { return mPowerComponents; } - private static final AggregatedPowerStatsProcessor NO_OP_PROCESSOR = - new AggregatedPowerStatsProcessor() { + private static final PowerStatsProcessor NO_OP_PROCESSOR = + new PowerStatsProcessor() { @Override - public void finish(PowerComponentAggregatedPowerStats stats) { + void finish(PowerComponentAggregatedPowerStats stats) { } @Override - public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { + String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { return Arrays.toString(stats); } @Override - public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { + String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { + return descriptor.getStateLabel(key) + " " + Arrays.toString(stats); + } + + @Override + String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { return Arrays.toString(stats); } }; diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java index a8eda3ca6a47..cb10da9787df 100644 --- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java @@ -24,6 +24,7 @@ import android.hardware.power.stats.EnergyConsumer; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyConsumerType; import android.net.wifi.WifiManager; +import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.Bundle; import android.os.OutcomeReceiver; @@ -603,24 +604,31 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat } if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { - // We were asked to fetch Telephony data. - if (mTelephony != null) { - CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>(); - mTelephony.requestModemActivityInfo(Runnable::run, - new OutcomeReceiver<ModemActivityInfo, - TelephonyManager.ModemActivityInfoException>() { - @Override - public void onResult(ModemActivityInfo result) { - temp.complete(result); - } - - @Override - public void onError(TelephonyManager.ModemActivityInfoException e) { - Slog.w(TAG, "error reading modem stats:" + e); - temp.complete(null); - } - }); - modemFuture = temp; + @SuppressWarnings("GuardedBy") + PowerStatsCollector collector = mStats.getPowerStatsCollector( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); + if (collector.isEnabled()) { + collector.schedule(); + } else { + // We were asked to fetch Telephony data. + if (mTelephony != null) { + CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>(); + mTelephony.requestModemActivityInfo(Runnable::run, + new OutcomeReceiver<ModemActivityInfo, + TelephonyManager.ModemActivityInfoException>() { + @Override + public void onResult(ModemActivityInfo result) { + temp.complete(result); + } + + @Override + public void onError(TelephonyManager.ModemActivityInfoException e) { + Slog.w(TAG, "error reading modem stats:" + e); + temp.complete(null); + } + }); + modemFuture = temp; + } } if (!railUpdated) { synchronized (mStats) { diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 3a84897839a1..fc2df578c6e3 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -22,6 +22,8 @@ import static android.os.BatteryStats.Uid.NUM_PROCESS_STATE; import static android.os.BatteryStatsManager.NUM_WIFI_STATES; import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES; +import static com.android.server.power.stats.MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -35,6 +37,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.location.GnssSignalQuality; @@ -71,6 +74,7 @@ import android.os.connectivity.CellularBatteryStats; import android.os.connectivity.GpsBatteryStats; import android.os.connectivity.WifiActivityEnergyInfo; import android.os.connectivity.WifiBatteryStats; +import android.power.PowerStatsInternal; import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.Annotation.NetworkType; @@ -99,6 +103,7 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.SparseDoubleArray; import android.util.SparseIntArray; import android.util.SparseLongArray; @@ -137,6 +142,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; +import com.android.server.LocalServices; import com.android.server.power.optimization.Flags; import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; @@ -166,8 +172,12 @@ import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; /** * All information we are collecting about things that can happen that impact @@ -280,8 +290,9 @@ public class BatteryStatsImpl extends BatteryStats { private KernelMemoryBandwidthStats mKernelMemoryBandwidthStats; private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>(); private int[] mCpuPowerBracketMap; - private final CpuPowerStatsCollector mCpuPowerStatsCollector; - private boolean mPowerStatsCollectorEnabled; + private CpuPowerStatsCollector mCpuPowerStatsCollector; + private MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector; + private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray(); public LongSparseArray<SamplingTimer> getKernelMemoryStats() { return mKernelMemoryStats; @@ -433,9 +444,11 @@ public class BatteryStatsImpl extends BatteryStats { public static class BatteryStatsConfig { static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0; static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1; + static final long DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD = + TimeUnit.HOURS.toMillis(1); private final int mFlags; - private final long mPowerStatsThrottlePeriodCpu; + private SparseLongArray mPowerStatsThrottlePeriods; private BatteryStatsConfig(Builder builder) { int flags = 0; @@ -446,7 +459,7 @@ public class BatteryStatsImpl extends BatteryStats { flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG; } mFlags = flags; - mPowerStatsThrottlePeriodCpu = builder.mPowerStatsThrottlePeriodCpu; + mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods; } /** @@ -467,8 +480,9 @@ public class BatteryStatsImpl extends BatteryStats { == RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG; } - long getPowerStatsThrottlePeriodCpu() { - return mPowerStatsThrottlePeriodCpu; + long getPowerStatsThrottlePeriod(@BatteryConsumer.PowerComponent int powerComponent) { + return mPowerStatsThrottlePeriods.get(powerComponent, + DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD); } /** @@ -477,12 +491,16 @@ public class BatteryStatsImpl extends BatteryStats { public static class Builder { private boolean mResetOnUnplugHighBatteryLevel; private boolean mResetOnUnplugAfterSignificantCharge; - private long mPowerStatsThrottlePeriodCpu; + private SparseLongArray mPowerStatsThrottlePeriods; public Builder() { mResetOnUnplugHighBatteryLevel = true; mResetOnUnplugAfterSignificantCharge = true; - mPowerStatsThrottlePeriodCpu = 60000; + mPowerStatsThrottlePeriods = new SparseLongArray(); + setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU, + TimeUnit.MINUTES.toMillis(1)); + setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, + TimeUnit.HOURS.toMillis(1)); } /** @@ -512,10 +530,11 @@ public class BatteryStatsImpl extends BatteryStats { /** * Sets the minimum amount of time (in millis) to wait between passes - * of CPU power stats collection. + * of power stats collection for the specified power component. */ - public Builder setPowerStatsThrottlePeriodCpu(long periodMs) { - mPowerStatsThrottlePeriodCpu = periodMs; + public Builder setPowerStatsThrottlePeriodMillis( + @BatteryConsumer.PowerComponent int powerComponent, long periodMs) { + mPowerStatsThrottlePeriods.put(powerComponent, periodMs); return this; } } @@ -597,7 +616,7 @@ public class BatteryStatsImpl extends BatteryStats { @SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter @VisibleForTesting public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) { - if (mPowerStatsCollectorEnabled) { + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) { return; } @@ -653,7 +672,7 @@ public class BatteryStatsImpl extends BatteryStats { */ @SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter public void updateCpuTimesForAllUids() { - if (mPowerStatsCollectorEnabled && mCpuPowerStatsCollector != null) { + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) { mCpuPowerStatsCollector.schedule(); return; } @@ -713,7 +732,8 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void ensureKernelSingleUidTimeReaderLocked() { - if (mPowerStatsCollectorEnabled || mKernelSingleUidTimeReader != null) { + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU) + || mKernelSingleUidTimeReader != null) { return; } @@ -830,8 +850,6 @@ public class BatteryStatsImpl extends BatteryStats { private final HistoryEventTracker mActiveEvents = new HistoryEventTracker(); private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator = new HistoryStepDetailsCalculatorImpl(); - private final PowerStats.DescriptorRegistry mPowerStatsDescriptorRegistry = - new PowerStats.DescriptorRegistry(); private boolean mHaveBatteryLevel = false; private boolean mBatteryPluggedIn; @@ -1838,19 +1856,28 @@ public class BatteryStatsImpl extends BatteryStats { FrameworkStatsLog.write( FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin); } + + /** + * Records a statsd event when the batterystats config file is written to disk. + */ + public void writeCommitSysConfigFile(String fileName, long durationMs) { + com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(fileName, + durationMs); + } } private final FrameworkStatsLogger mFrameworkStatsLogger; @VisibleForTesting - public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler, + public BatteryStatsImpl(@NonNull BatteryStatsConfig config, Clock clock, File historyDirectory, + @NonNull Handler handler, @NonNull PowerStatsUidResolver powerStatsUidResolver, @NonNull FrameworkStatsLogger frameworkStatsLogger, @NonNull BatteryStatsHistory.TraceDelegate traceDelegate, @NonNull BatteryStatsHistory.EventLogger eventLogger) { + mBatteryStatsConfig = config; mClock = clock; initKernelStatsReaders(); - mBatteryStatsConfig = new BatteryStatsConfig.Builder().build(); mHandler = handler; mPowerStatsUidResolver = powerStatsUidResolver; mFrameworkStatsLogger = frameworkStatsLogger; @@ -1873,7 +1900,7 @@ public class BatteryStatsImpl extends BatteryStats { mPlatformIdleStateCallback = null; mEnergyConsumerRetriever = null; mUserInfoProvider = null; - mCpuPowerStatsCollector = null; + initPowerStatsCollectors(); } private void initKernelStatsReaders() { @@ -1893,6 +1920,105 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats = new RailStats(); } + private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector, + MobileRadioPowerStatsCollector.Injector { + private PackageManager mPackageManager; + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + private NetworkStatsManager mNetworkStatsManager; + private TelephonyManager mTelephonyManager; + + void setContext(Context context) { + mPackageManager = context.getPackageManager(); + mConsumedEnergyRetriever = new PowerStatsCollector.ConsumedEnergyRetrieverImpl( + LocalServices.getService(PowerStatsInternal.class)); + mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); + } + + @Override + public Handler getHandler() { + return mHandler; + } + + @Override + public Clock getClock() { + return mClock; + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mPowerStatsUidResolver; + } + + @Override + public CpuScalingPolicies getCpuScalingPolicies() { + return mCpuScalingPolicies; + } + + @Override + public PowerProfile getPowerProfile() { + return mPowerProfile; + } + + @Override + public CpuPowerStatsCollector.KernelCpuStatsReader getKernelCpuStatsReader() { + return new CpuPowerStatsCollector.KernelCpuStatsReader(); + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> mBatteryVoltageMv; + } + + @Override + public Supplier<NetworkStats> getMobileNetworkStatsSupplier() { + return () -> readMobileNetworkStatsLocked(mNetworkStatsManager); + } + + @Override + public TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + + @Override + public LongSupplier getCallDurationSupplier() { + return () -> mPhoneOnTimer.getTotalTimeLocked(mClock.elapsedRealtime() * 1000, + STATS_SINCE_CHARGED); + } + + @Override + public LongSupplier getPhoneSignalScanDurationSupplier() { + return () -> mPhoneSignalScanningTimer.getTotalTimeLocked( + mClock.elapsedRealtime() * 1000, STATS_SINCE_CHARGED); + } + } + + private final PowerStatsCollectorInjector mPowerStatsCollectorInjector = + new PowerStatsCollectorInjector(); + + @SuppressWarnings("GuardedBy") // Accessed from constructor only + private void initPowerStatsCollectors() { + mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector, + mBatteryStatsConfig.getPowerStatsThrottlePeriod( + BatteryConsumer.POWER_COMPONENT_CPU)); + mCpuPowerStatsCollector.addConsumer(this::recordPowerStats); + + mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector( + mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)); + mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats); + } + /** * TimeBase observer. */ @@ -5738,16 +5864,19 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs); mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs); - if (mLastModemActivityInfo != null) { - if (elapsedRealtimeMs < mLastModemActivityInfo.getTimestampMillis() + if (mMobileRadioPowerStatsCollector.isEnabled()) { + mMobileRadioPowerStatsCollector.schedule(); + } else { + // Check if modem Activity info has been collected recently, don't bother + // triggering another update. + if (mLastModemActivityInfo == null + || elapsedRealtimeMs >= mLastModemActivityInfo.getTimestampMillis() + MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS) { - // Modem Activity info has been collected recently, don't bother - // triggering another update. - return false; + mExternalSync.scheduleSync("modem-data", + BatteryExternalStatsWorker.UPDATE_RADIO); + return true; } } - // Tell the caller to collect radio network/power stats. - return true; } } return false; @@ -5915,6 +6044,7 @@ public class BatteryStatsImpl extends BatteryStats { mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs); if (mConstants.PHONE_ON_EXTERNAL_STATS_COLLECTION) { scheduleSyncExternalStatsLocked("phone-on", ExternalStatsSync.UPDATE_RADIO); + mMobileRadioPowerStatsCollector.schedule(); } } } @@ -5927,6 +6057,7 @@ public class BatteryStatsImpl extends BatteryStats { mPhoneOn = false; mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs); scheduleSyncExternalStatsLocked("phone-off", ExternalStatsSync.UPDATE_RADIO); + mMobileRadioPowerStatsCollector.schedule(); } } @@ -6269,27 +6400,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - @RadioAccessTechnology - private static int mapRadioAccessNetworkTypeToRadioAccessTechnology( - @AccessNetworkConstants.RadioAccessNetworkType int dataType) { - switch (dataType) { - case AccessNetworkConstants.AccessNetworkType.NGRAN: - return RADIO_ACCESS_TECHNOLOGY_NR; - case AccessNetworkConstants.AccessNetworkType.EUTRAN: - return RADIO_ACCESS_TECHNOLOGY_LTE; - case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough - case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough - case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough - case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough - case AccessNetworkConstants.AccessNetworkType.IWLAN: - return RADIO_ACCESS_TECHNOLOGY_OTHER; - default: - Slog.w(TAG, - "Unhandled RadioAccessNetworkType (" + dataType + "), mapping to OTHER"); - return RADIO_ACCESS_TECHNOLOGY_OTHER; - } - } - @GuardedBy("this") public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) { if (!mWifiOn) { @@ -8311,7 +8421,7 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("mBsi") private void ensureMultiStateCounters(long timestampMs) { - if (mBsi.mPowerStatsCollectorEnabled) { + if (mBsi.mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) { throw new IllegalStateException("Multi-state counters used in streamlined mode"); } @@ -10612,7 +10722,8 @@ public class BatteryStatsImpl extends BatteryStats { mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs); } - if (!mBsi.mPowerStatsCollectorEnabled && mBsi.trackPerProcStateCpuTimes()) { + if (!mBsi.mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU) + && mBsi.trackPerProcStateCpuTimes()) { mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs, uptimeMs); LongArrayMultiStateCounter onBatteryCounter = @@ -10634,7 +10745,8 @@ public class BatteryStatsImpl extends BatteryStats { final int batteryConsumerProcessState = mapUidProcessStateToBatteryConsumerProcessState(uidRunningState); - if (mBsi.mSystemReady && mBsi.mPowerStatsCollectorEnabled) { + if (mBsi.mSystemReady && mBsi.mPowerStatsCollectorEnabled.get( + BatteryConsumer.POWER_COMPONENT_CPU)) { mBsi.mHistory.recordProcessStateChange(elapsedRealtimeMs, uptimeMs, mUid, batteryConsumerProcessState); } @@ -11016,11 +11128,7 @@ public class BatteryStatsImpl extends BatteryStats { mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock); } - mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile, - mPowerStatsUidResolver, () -> mBatteryVoltageMv, mHandler, - mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu()); - mCpuPowerStatsCollector.addConsumer(this::recordPowerStats); - + initPowerStatsCollectors(); mStartCount++; initTimersAndCounters(); mOnBattery = mOnBatteryInternal = false; @@ -11296,8 +11404,7 @@ public class BatteryStatsImpl extends BatteryStats { memStream.writeTo(stream); stream.flush(); mDailyFile.finishWrite(stream); - com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( - "batterystats-daily", + mFrameworkStatsLogger.writeCommitSysConfigFile("batterystats-daily", initialTimeMs + SystemClock.uptimeMillis() - startTimeMs2); } catch (IOException e) { Slog.w("BatteryStats", @@ -11809,7 +11916,7 @@ public class BatteryStatsImpl extends BatteryStats { // Store the empty state to disk to ensure consistency writeSyncLocked(); - if (mPowerStatsCollectorEnabled) { + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) { schedulePowerStatsSampleCollection(); } @@ -11953,7 +12060,7 @@ public class BatteryStatsImpl extends BatteryStats { return networkStatsManager.getWifiUidStats(); } - private static class NetworkStatsDelta { + static class NetworkStatsDelta { int mUid; int mSet; long mRxBytes; @@ -11985,9 +12092,16 @@ public class BatteryStatsImpl extends BatteryStats { public long getTxPackets() { return mTxPackets; } + + @Override + public String toString() { + return "NetworkStatsDelta{mUid=" + mUid + ", mSet=" + mSet + ", mRxBytes=" + mRxBytes + + ", mRxPackets=" + mRxPackets + ", mTxBytes=" + mTxBytes + ", mTxPackets=" + + mTxPackets + '}'; + } } - private List<NetworkStatsDelta> computeDelta(NetworkStats currentStats, + static List<NetworkStatsDelta> computeDelta(NetworkStats currentStats, NetworkStats lastStats) { List<NetworkStatsDelta> deltaList = new ArrayList<>(); for (NetworkStats.Entry entry : currentStats) { @@ -12418,13 +12532,11 @@ public class BatteryStatsImpl extends BatteryStats { addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs); // Grab a separate lock to acquire the network stats, which may do I/O. - NetworkStats delta = null; + List<NetworkStatsDelta> delta = null; synchronized (mModemNetworkLock) { final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager); if (latestStats != null) { - delta = latestStats.subtract(mLastModemNetworkStats != null - ? mLastModemNetworkStats - : new NetworkStats(0, -1)); + delta = computeDelta(latestStats, mLastModemNetworkStats); mLastModemNetworkStats = latestStats; } } @@ -12527,7 +12639,7 @@ public class BatteryStatsImpl extends BatteryStats { long totalRxPackets = 0; long totalTxPackets = 0; if (delta != null) { - for (NetworkStats.Entry entry : delta) { + for (NetworkStatsDelta entry : delta) { if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) { continue; } @@ -12568,7 +12680,7 @@ public class BatteryStatsImpl extends BatteryStats { // Now distribute proportional blame to the apps that did networking. long totalPackets = totalRxPackets + totalTxPackets; if (totalPackets > 0) { - for (NetworkStats.Entry entry : delta) { + for (NetworkStatsDelta entry : delta) { if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) { continue; } @@ -14408,17 +14520,41 @@ public class BatteryStatsImpl extends BatteryStats { /** * Notifies BatteryStatsImpl that the system server is ready. */ - public void onSystemReady() { + public void onSystemReady(Context context) { if (mCpuUidFreqTimeReader != null) { mCpuUidFreqTimeReader.onSystemReady(); } - if (mCpuPowerStatsCollector != null) { - mCpuPowerStatsCollector.setEnabled(mPowerStatsCollectorEnabled); - } + + mPowerStatsCollectorInjector.setContext(context); + + mCpuPowerStatsCollector.setEnabled( + mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)); + mCpuPowerStatsCollector.schedule(); + + mMobileRadioPowerStatsCollector.setEnabled( + mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)); + mMobileRadioPowerStatsCollector.schedule(); + mSystemReady = true; } /** + * Returns a PowerStatsCollector for the specified power component or null if unavailable. + */ + @Nullable + PowerStatsCollector getPowerStatsCollector( + @BatteryConsumer.PowerComponent int powerComponent) { + switch (powerComponent) { + case BatteryConsumer.POWER_COMPONENT_CPU: + return mCpuPowerStatsCollector; + case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO: + return mMobileRadioPowerStatsCollector; + } + return null; + } + + + /** * Force recording of all history events regardless of the "charging" state. */ @VisibleForTesting @@ -14561,9 +14697,10 @@ public class BatteryStatsImpl extends BatteryStats { stream.write(parcel.marshall()); stream.flush(); mCheckinFile.finishWrite(stream); - com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( - "batterystats-checkin", initialTimeMs - + SystemClock.uptimeMillis() - startTimeMs2); + mFrameworkStatsLogger.writeCommitSysConfigFile( + "batterystats-checkin", + initialTimeMs + SystemClock.uptimeMillis() + - startTimeMs2); } catch (IOException e) { Slog.w("BatteryStats", "Error writing checkin battery statistics", e); @@ -15437,9 +15574,10 @@ public class BatteryStatsImpl extends BatteryStats { /** * Enables or disables the PowerStatsCollector mode. */ - public void setPowerStatsCollectorEnabled(boolean enabled) { + public void setPowerStatsCollectorEnabled(@BatteryConsumer.PowerComponent int powerComponent, + boolean enabled) { synchronized (this) { - mPowerStatsCollectorEnabled = enabled; + mPowerStatsCollectorEnabled.put(powerComponent, enabled); } } @@ -15944,10 +16082,8 @@ public class BatteryStatsImpl extends BatteryStats { * Callers will need to wait for the collection to complete on the handler thread. */ public void schedulePowerStatsSampleCollection() { - if (mCpuPowerStatsCollector == null) { - return; - } mCpuPowerStatsCollector.forceSchedule(); + mMobileRadioPowerStatsCollector.forceSchedule(); } /** @@ -15965,6 +16101,7 @@ public class BatteryStatsImpl extends BatteryStats { */ public void dumpStatsSample(PrintWriter pw) { mCpuPowerStatsCollector.collectAndDump(pw); + mMobileRadioPowerStatsCollector.collectAndDump(pw); } private final Runnable mWriteAsyncRunnable = () -> { @@ -16036,7 +16173,7 @@ public class BatteryStatsImpl extends BatteryStats { + " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs) + " bytes:" + p.dataSize()); } - com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( + mFrameworkStatsLogger.writeCommitSysConfigFile( "batterystats", SystemClock.uptimeMillis() - startTimeMs); } catch (IOException e) { Slog.w(TAG, "Error writing battery statistics", e); @@ -17262,10 +17399,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(); dumpConstantsLocked(pw); - if (mCpuPowerStatsCollector != null) { - pw.println(); - mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw); - } + pw.println(); + mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw); pw.println(); dumpEnergyConsumerStatsLocked(pw); diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index 30b80ae781ff..97f09865beeb 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -27,6 +27,7 @@ import android.os.UidBatteryConsumer; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import com.android.internal.os.Clock; import com.android.internal.os.CpuScalingPolicies; @@ -43,7 +44,7 @@ import java.util.List; public class BatteryUsageStatsProvider { private static final String TAG = "BatteryUsageStatsProv"; private final Context mContext; - private boolean mPowerStatsExporterEnabled; + private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray(); private final PowerStatsExporter mPowerStatsExporter; private final PowerStatsStore mPowerStatsStore; private final PowerProfile mPowerProfile; @@ -71,14 +72,20 @@ public class BatteryUsageStatsProvider { // Power calculators are applied in the order of registration mPowerCalculators.add(new BatteryChargeCalculator()); - if (!mPowerStatsExporterEnabled) { + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) { mPowerCalculators.add( new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile)); } mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile)); mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile)); if (!BatteryStats.checkWifiOnly(mContext)) { - mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile)); + if (!mPowerStatsExporterEnabled.get( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) { + mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile)); + } + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) { + mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile)); + } } mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile)); mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile)); @@ -89,7 +96,6 @@ public class BatteryUsageStatsProvider { mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile)); mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile)); mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile)); mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile)); mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile)); @@ -228,7 +234,7 @@ public class BatteryUsageStatsProvider { } } - if (mPowerStatsExporterEnabled) { + if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) { mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder, monotonicStartTime, monotonicEndTime); } @@ -393,7 +399,10 @@ public class BatteryUsageStatsProvider { return builder.build(); } - public void setPowerStatsExporterEnabled(boolean enabled) { - mPowerStatsExporterEnabled = enabled; + /** + * Specify whether PowerStats based attribution is supported for the specified component. + */ + public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) { + mPowerStatsExporterEnabled.put(powerComponentId, enabled); } } diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index 1af127175f80..b1b2cc91d379 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java @@ -16,14 +16,11 @@ package com.android.server.power.stats; -import android.hardware.power.stats.EnergyConsumer; -import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyConsumerType; import android.os.BatteryConsumer; import android.os.Handler; import android.os.PersistableBundle; import android.os.Process; -import android.power.PowerStatsInternal; import android.util.Slog; import android.util.SparseArray; @@ -34,20 +31,11 @@ import com.android.internal.os.Clock; import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; -import com.android.server.LocalServices; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; -import java.util.List; import java.util.Locale; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.IntSupplier; -import java.util.function.Supplier; /** * Collects snapshots of power-related system statistics. @@ -63,213 +51,54 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2; private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000; + interface Injector { + Handler getHandler(); + Clock getClock(); + PowerStatsUidResolver getUidResolver(); + CpuScalingPolicies getCpuScalingPolicies(); + PowerProfile getPowerProfile(); + KernelCpuStatsReader getKernelCpuStatsReader(); + ConsumedEnergyRetriever getConsumedEnergyRetriever(); + IntSupplier getVoltageSupplier(); + + default int getDefaultCpuPowerBrackets() { + return DEFAULT_CPU_POWER_BRACKETS; + } + + default int getDefaultCpuPowerBracketsPerEnergyConsumer() { + return DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER; + } + } + + private final Injector mInjector; + private boolean mIsInitialized; - private final CpuScalingPolicies mCpuScalingPolicies; - private final PowerProfile mPowerProfile; - private final KernelCpuStatsReader mKernelCpuStatsReader; - private final PowerStatsUidResolver mUidResolver; - private final Supplier<PowerStatsInternal> mPowerStatsSupplier; - private final IntSupplier mVoltageSupplier; - private final int mDefaultCpuPowerBrackets; - private final int mDefaultCpuPowerBracketsPerEnergyConsumer; + private CpuScalingPolicies mCpuScalingPolicies; + private PowerProfile mPowerProfile; + private KernelCpuStatsReader mKernelCpuStatsReader; + private PowerStatsUidResolver mUidResolver; + private ConsumedEnergyRetriever mConsumedEnergyRetriever; + private IntSupplier mVoltageSupplier; + private int mDefaultCpuPowerBrackets; + private int mDefaultCpuPowerBracketsPerEnergyConsumer; private long[] mCpuTimeByScalingStep; private long[] mTempCpuTimeByScalingStep; private long[] mTempUidStats; private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private boolean mIsPerUidTimeInStateSupported; - private PowerStatsInternal mPowerStatsInternal; private int[] mCpuEnergyConsumerIds = new int[0]; private PowerStats.Descriptor mPowerStatsDescriptor; // Reusable instance private PowerStats mCpuPowerStats; - private CpuStatsArrayLayout mLayout; + private CpuPowerStatsLayout mLayout; private long mLastUpdateTimestampNanos; private long mLastUpdateUptimeMillis; private int mLastVoltageMv; private long[] mLastConsumedEnergyUws; - /** - * Captures the positions and lengths of sections of the stats array, such as time-in-state, - * power usage estimates etc. - */ - public static class CpuStatsArrayLayout extends StatsArrayLayout { - private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt"; - private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc"; - private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc"; - private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc"; - private static final String EXTRA_UID_BRACKETS_POSITION = "ub"; - private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us"; - - private int mDeviceCpuTimeByScalingStepPosition; - private int mDeviceCpuTimeByScalingStepCount; - private int mDeviceCpuTimeByClusterPosition; - private int mDeviceCpuTimeByClusterCount; - - private int mUidPowerBracketsPosition; - private int mUidPowerBracketCount; - - private int[] mScalingStepToPowerBracketMap; - - /** - * Declare that the stats array has a section capturing CPU time per scaling step - */ - public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) { - mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount); - mDeviceCpuTimeByScalingStepCount = scalingStepCount; - } - - public int getCpuScalingStepCount() { - return mDeviceCpuTimeByScalingStepCount; - } - - /** - * Saves the time duration in the <code>stats</code> element - * corresponding to the CPU scaling <code>state</code>. - */ - public void setTimeByScalingStep(long[] stats, int step, long value) { - stats[mDeviceCpuTimeByScalingStepPosition + step] = value; - } - - /** - * Extracts the time duration from the <code>stats</code> element - * corresponding to the CPU scaling <code>step</code>. - */ - public long getTimeByScalingStep(long[] stats, int step) { - return stats[mDeviceCpuTimeByScalingStepPosition + step]; - } - - /** - * Declare that the stats array has a section capturing CPU time in each cluster - */ - public void addDeviceSectionCpuTimeByCluster(int clusterCount) { - mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount); - mDeviceCpuTimeByClusterCount = clusterCount; - } - - public int getCpuClusterCount() { - return mDeviceCpuTimeByClusterCount; - } - - /** - * Saves the time duration in the <code>stats</code> element - * corresponding to the CPU <code>cluster</code>. - */ - public void setTimeByCluster(long[] stats, int cluster, long value) { - stats[mDeviceCpuTimeByClusterPosition + cluster] = value; - } - - /** - * Extracts the time duration from the <code>stats</code> element - * corresponding to the CPU <code>cluster</code>. - */ - public long getTimeByCluster(long[] stats, int cluster) { - return stats[mDeviceCpuTimeByClusterPosition + cluster]; - } - - /** - * Declare that the UID stats array has a section capturing CPU time per power bracket. - */ - public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) { - mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap; - updatePowerBracketCount(); - mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount); - } - - private void updatePowerBracketCount() { - mUidPowerBracketCount = 1; - for (int bracket : mScalingStepToPowerBracketMap) { - if (bracket >= mUidPowerBracketCount) { - mUidPowerBracketCount = bracket + 1; - } - } - } - - public int[] getScalingStepToPowerBracketMap() { - return mScalingStepToPowerBracketMap; - } - - public int getCpuPowerBracketCount() { - return mUidPowerBracketCount; - } - - /** - * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>. - */ - public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) { - stats[mUidPowerBracketsPosition + bracket] = value; - } - - /** - * Extracts the time in <code>bracket</code> from a UID stats array. - */ - public long getUidTimeByPowerBracket(long[] stats, int bracket) { - return stats[mUidPowerBracketsPosition + bracket]; - } - - /** - * Copies the elements of the stats array layout into <code>extras</code> - */ - public void toExtras(PersistableBundle extras) { - super.toExtras(extras); - extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION, - mDeviceCpuTimeByScalingStepPosition); - extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT, - mDeviceCpuTimeByScalingStepCount); - extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION, - mDeviceCpuTimeByClusterPosition); - extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT, - mDeviceCpuTimeByClusterCount); - extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition); - putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET, - mScalingStepToPowerBracketMap); - } - - /** - * Retrieves elements of the stats array layout from <code>extras</code> - */ - public void fromExtras(PersistableBundle extras) { - super.fromExtras(extras); - mDeviceCpuTimeByScalingStepPosition = - extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION); - mDeviceCpuTimeByScalingStepCount = - extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT); - mDeviceCpuTimeByClusterPosition = - extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION); - mDeviceCpuTimeByClusterCount = - extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT); - mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION); - mScalingStepToPowerBracketMap = - getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET); - if (mScalingStepToPowerBracketMap == null) { - mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount]; - } - updatePowerBracketCount(); - } - } - - public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, - PowerStatsUidResolver uidResolver, IntSupplier voltageSupplier, Handler handler, - long throttlePeriodMs) { - this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(), uidResolver, - () -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier, - throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS, - DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER); - } - - public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, - Handler handler, KernelCpuStatsReader kernelCpuStatsReader, - PowerStatsUidResolver uidResolver, Supplier<PowerStatsInternal> powerStatsSupplier, - IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock, - int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) { - super(handler, throttlePeriodMs, clock); - mCpuScalingPolicies = cpuScalingPolicies; - mPowerProfile = powerProfile; - mKernelCpuStatsReader = kernelCpuStatsReader; - mUidResolver = uidResolver; - mPowerStatsSupplier = powerStatsSupplier; - mVoltageSupplier = voltageSupplier; - mDefaultCpuPowerBrackets = defaultCpuPowerBrackets; - mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer; + public CpuPowerStatsCollector(Injector injector, long throttlePeriodMs) { + super(injector.getHandler(), throttlePeriodMs, injector.getClock()); + mInjector = injector; } private boolean ensureInitialized() { @@ -281,19 +110,28 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { return false; } - mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature(); - mPowerStatsInternal = mPowerStatsSupplier.get(); - - if (mPowerStatsInternal != null) { - readCpuEnergyConsumerIds(); - } + mCpuScalingPolicies = mInjector.getCpuScalingPolicies(); + mPowerProfile = mInjector.getPowerProfile(); + mKernelCpuStatsReader = mInjector.getKernelCpuStatsReader(); + mUidResolver = mInjector.getUidResolver(); + mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever(); + mVoltageSupplier = mInjector.getVoltageSupplier(); + mDefaultCpuPowerBrackets = mInjector.getDefaultCpuPowerBrackets(); + mDefaultCpuPowerBracketsPerEnergyConsumer = + mInjector.getDefaultCpuPowerBracketsPerEnergyConsumer(); + + mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.isSupportedFeature(); + mCpuEnergyConsumerIds = + mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER); + mLastConsumedEnergyUws = new long[mCpuEnergyConsumerIds.length]; + Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED); int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount(); mCpuTimeByScalingStep = new long[cpuScalingStepCount]; mTempCpuTimeByScalingStep = new long[cpuScalingStepCount]; int[] scalingStepToPowerBracketMap = initPowerBrackets(); - mLayout = new CpuStatsArrayLayout(); + mLayout = new CpuPowerStatsLayout(); mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount); mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length); mLayout.addDeviceSectionUsageDuration(); @@ -306,7 +144,8 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { mLayout.toExtras(extras); mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, - mLayout.getDeviceStatsArrayLength(), mLayout.getUidStatsArrayLength(), extras); + mLayout.getDeviceStatsArrayLength(), /* stateLabels */null, + /* stateStatsArrayLength */ 0, mLayout.getUidStatsArrayLength(), extras); mCpuPowerStats = new PowerStats(mPowerStatsDescriptor); mTempUidStats = new long[mLayout.getCpuPowerBracketCount()]; @@ -315,32 +154,6 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { return true; } - private void readCpuEnergyConsumerIds() { - EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo(); - if (energyConsumerInfo == null) { - return; - } - - List<EnergyConsumer> cpuEnergyConsumers = new ArrayList<>(); - for (EnergyConsumer energyConsumer : energyConsumerInfo) { - if (energyConsumer.type == EnergyConsumerType.CPU_CLUSTER) { - cpuEnergyConsumers.add(energyConsumer); - } - } - if (cpuEnergyConsumers.isEmpty()) { - return; - } - - cpuEnergyConsumers.sort(Comparator.comparing(c -> c.ordinal)); - - mCpuEnergyConsumerIds = new int[cpuEnergyConsumers.size()]; - for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) { - mCpuEnergyConsumerIds[i] = cpuEnergyConsumers.get(i).id; - } - mLastConsumedEnergyUws = new long[cpuEnergyConsumers.size()]; - Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED); - } - private int[] initPowerBrackets() { if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) { return initPowerBracketsFromPowerProfile(); @@ -372,6 +185,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { return stepToBracketMap; } + private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) { int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; int index = 0; @@ -531,7 +345,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { mCpuPowerStats.uidStats.clear(); // TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster - long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(this::processUidStats, + long newTimestampNanos = mKernelCpuStatsReader.readCpuStats(this::processUidStats, mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos, mTempCpuTimeByScalingStep, mTempUidStats); for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) { @@ -571,35 +385,20 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv; mLastVoltageMv = voltageMv; - CompletableFuture<EnergyConsumerResult[]> future = - mPowerStatsInternal.getEnergyConsumedAsync(mCpuEnergyConsumerIds); - EnergyConsumerResult[] results = null; - try { - results = future.get( - POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e); - } - if (results == null) { + long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mCpuEnergyConsumerIds); + if (energyUws == null) { return; } - for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) { - int id = mCpuEnergyConsumerIds[i]; - for (EnergyConsumerResult result : results) { - if (result.id == id) { - long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED - ? result.energyUWs - mLastConsumedEnergyUws[i] : 0; - if (energyDelta < 0) { - // Likely, restart of powerstats HAL - energyDelta = 0; - } - mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, - uJtoUc(energyDelta, averageVoltage)); - mLastConsumedEnergyUws[i] = result.energyUWs; - break; - } + for (int i = energyUws.length - 1; i >= 0; i--) { + long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED + ? energyUws[i] - mLastConsumedEnergyUws[i] : 0; + if (energyDelta < 0) { + // Likely, restart of powerstats HAL + energyDelta = 0; } + mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage)); + mLastConsumedEnergyUws[i] = energyUws[i]; } } @@ -652,6 +451,17 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { * Native class that retrieves CPU stats from the kernel. */ public static class KernelCpuStatsReader { + protected boolean isSupportedFeature() { + return nativeIsSupportedFeature(); + } + + protected long readCpuStats(KernelCpuStatsCallback callback, + int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos, + long[] outCpuTimeByScalingStep, long[] tempForUidStats) { + return nativeReadCpuStats(callback, scalingStepToPowerBracketMap, + lastUpdateTimestampNanos, outCpuTimeByScalingStep, tempForUidStats); + } + protected native boolean nativeIsSupportedFeature(); protected native long nativeReadCpuStats(KernelCpuStatsCallback callback, diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java new file mode 100644 index 000000000000..1bcb2c4bc5fa --- /dev/null +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import android.os.PersistableBundle; + +/** + * Captures the positions and lengths of sections of the stats array, such as time-in-state, + * power usage estimates etc. + */ +public class CpuPowerStatsLayout extends PowerStatsLayout { + private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt"; + private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc"; + private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc"; + private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc"; + private static final String EXTRA_UID_BRACKETS_POSITION = "ub"; + private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us"; + + private int mDeviceCpuTimeByScalingStepPosition; + private int mDeviceCpuTimeByScalingStepCount; + private int mDeviceCpuTimeByClusterPosition; + private int mDeviceCpuTimeByClusterCount; + + private int mUidPowerBracketsPosition; + private int mUidPowerBracketCount; + + private int[] mScalingStepToPowerBracketMap; + + /** + * Declare that the stats array has a section capturing CPU time per scaling step + */ + public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) { + mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount); + mDeviceCpuTimeByScalingStepCount = scalingStepCount; + } + + public int getCpuScalingStepCount() { + return mDeviceCpuTimeByScalingStepCount; + } + + /** + * Saves the time duration in the <code>stats</code> element + * corresponding to the CPU scaling <code>state</code>. + */ + public void setTimeByScalingStep(long[] stats, int step, long value) { + stats[mDeviceCpuTimeByScalingStepPosition + step] = value; + } + + /** + * Extracts the time duration from the <code>stats</code> element + * corresponding to the CPU scaling <code>step</code>. + */ + public long getTimeByScalingStep(long[] stats, int step) { + return stats[mDeviceCpuTimeByScalingStepPosition + step]; + } + + /** + * Declare that the stats array has a section capturing CPU time in each cluster + */ + public void addDeviceSectionCpuTimeByCluster(int clusterCount) { + mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount); + mDeviceCpuTimeByClusterCount = clusterCount; + } + + public int getCpuClusterCount() { + return mDeviceCpuTimeByClusterCount; + } + + /** + * Saves the time duration in the <code>stats</code> element + * corresponding to the CPU <code>cluster</code>. + */ + public void setTimeByCluster(long[] stats, int cluster, long value) { + stats[mDeviceCpuTimeByClusterPosition + cluster] = value; + } + + /** + * Extracts the time duration from the <code>stats</code> element + * corresponding to the CPU <code>cluster</code>. + */ + public long getTimeByCluster(long[] stats, int cluster) { + return stats[mDeviceCpuTimeByClusterPosition + cluster]; + } + + /** + * Declare that the UID stats array has a section capturing CPU time per power bracket. + */ + public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) { + mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap; + updatePowerBracketCount(); + mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount); + } + + private void updatePowerBracketCount() { + mUidPowerBracketCount = 1; + for (int bracket : mScalingStepToPowerBracketMap) { + if (bracket >= mUidPowerBracketCount) { + mUidPowerBracketCount = bracket + 1; + } + } + } + + public int[] getScalingStepToPowerBracketMap() { + return mScalingStepToPowerBracketMap; + } + + public int getCpuPowerBracketCount() { + return mUidPowerBracketCount; + } + + /** + * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>. + */ + public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) { + stats[mUidPowerBracketsPosition + bracket] = value; + } + + /** + * Extracts the time in <code>bracket</code> from a UID stats array. + */ + public long getUidTimeByPowerBracket(long[] stats, int bracket) { + return stats[mUidPowerBracketsPosition + bracket]; + } + + /** + * Copies the elements of the stats array layout into <code>extras</code> + */ + public void toExtras(PersistableBundle extras) { + super.toExtras(extras); + extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION, + mDeviceCpuTimeByScalingStepPosition); + extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT, + mDeviceCpuTimeByScalingStepCount); + extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION, + mDeviceCpuTimeByClusterPosition); + extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT, + mDeviceCpuTimeByClusterCount); + extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition); + putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET, + mScalingStepToPowerBracketMap); + } + + /** + * Retrieves elements of the stats array layout from <code>extras</code> + */ + public void fromExtras(PersistableBundle extras) { + super.fromExtras(extras); + mDeviceCpuTimeByScalingStepPosition = + extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION); + mDeviceCpuTimeByScalingStepCount = + extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT); + mDeviceCpuTimeByClusterPosition = + extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION); + mDeviceCpuTimeByClusterCount = + extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT); + mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION); + mScalingStepToPowerBracketMap = + getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET); + if (mScalingStepToPowerBracketMap == null) { + mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount]; + } + updatePowerBracketCount(); + } +} diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java index ed9414ff53a1..c34b8a8dc992 100644 --- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java @@ -29,8 +29,8 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; -public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProcessor { - private static final String TAG = "CpuAggregatedPowerStatsProcessor"; +public class CpuPowerStatsProcessor extends PowerStatsProcessor { + private static final String TAG = "CpuPowerStatsProcessor"; private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1); private static final int UNKNOWN = -1; @@ -64,7 +64,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private PowerStats.Descriptor mLastUsedDescriptor; // Cached results of parsing of current PowerStats.Descriptor. Only refreshed when // mLastUsedDescriptor changes - private CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout; + private CpuPowerStatsLayout mStatsLayout; // Sequence of steps for power estimation and intermediate results. private PowerEstimationPlan mPlan; @@ -73,8 +73,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces // Temp array for retrieval of UID power stats, to avoid repeated allocations private long[] mTmpUidStatsArray; - public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile, - CpuScalingPolicies scalingPolicies) { + public CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) { mCpuScalingPolicies = scalingPolicies; mCpuScalingStepCount = scalingPolicies.getScalingStepCount(); mScalingStepToCluster = new int[mCpuScalingStepCount]; @@ -106,7 +105,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces } mLastUsedDescriptor = descriptor; - mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout(); + mStatsLayout = new CpuPowerStatsLayout(); mStatsLayout.fromExtras(descriptor.extras); mTmpDeviceStatsArray = new long[descriptor.statsArrayLength]; @@ -527,6 +526,12 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces } @Override + String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { + // Unsupported for this power component + return null; + } + + @Override public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { unpackPowerStatsDescriptor(descriptor); StringBuilder sb = new StringBuilder(); diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java index 9ea143e5c201..c01363a9c7ba 100644 --- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java @@ -387,92 +387,14 @@ public class MobileRadioPowerCalculator extends PowerCalculator { return consumptionMah; } - private static long buildModemPowerProfileKey(@ModemPowerProfile.ModemDrainType int drainType, - @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, - int txLevel) { - long key = PowerProfile.SUBSYSTEM_MODEM; - - // Attach Modem drain type to the key if specified. - if (drainType != IGNORE) { - key |= drainType; - } - - // Attach RadioAccessTechnology to the key if specified. - switch (rat) { - case IGNORE: - // do nothing - break; - case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER: - key |= ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT; - break; - case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE: - key |= ModemPowerProfile.MODEM_RAT_TYPE_LTE; - break; - case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR: - key |= ModemPowerProfile.MODEM_RAT_TYPE_NR; - break; - default: - Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat); - } - - // Attach NR Frequency Range to the key if specified. - switch (freqRange) { - case IGNORE: - // do nothing - break; - case ServiceState.FREQUENCY_RANGE_UNKNOWN: - key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT; - break; - case ServiceState.FREQUENCY_RANGE_LOW: - key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW; - break; - case ServiceState.FREQUENCY_RANGE_MID: - key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID; - break; - case ServiceState.FREQUENCY_RANGE_HIGH: - key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH; - break; - case ServiceState.FREQUENCY_RANGE_MMWAVE: - key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE; - break; - default: - Log.w(TAG, "Unexpected NR frequency range : " + freqRange); - } - - // Attach transmission level to the key if specified. - switch (txLevel) { - case IGNORE: - // do nothing - break; - case 0: - key |= ModemPowerProfile.MODEM_TX_LEVEL_0; - break; - case 1: - key |= ModemPowerProfile.MODEM_TX_LEVEL_1; - break; - case 2: - key |= ModemPowerProfile.MODEM_TX_LEVEL_2; - break; - case 3: - key |= ModemPowerProfile.MODEM_TX_LEVEL_3; - break; - case 4: - key |= ModemPowerProfile.MODEM_TX_LEVEL_4; - break; - default: - Log.w(TAG, "Unexpected transmission level : " + txLevel); - } - return key; - } - /** * Calculates active receive radio power consumption (in milliamp-hours) from the given state's * duration. */ public double calcRxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, long rxDurationMs) { - final long rxKey = buildModemPowerProfileKey(ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, - freqRange, IGNORE); + final long rxKey = ModemPowerProfile.getAverageBatteryDrainKey( + ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE); final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN); if (Double.isNaN(drainRateMa)) { @@ -495,8 +417,8 @@ public class MobileRadioPowerCalculator extends PowerCalculator { */ public double calcTxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange, int txLevel, long txDurationMs) { - final long txKey = buildModemPowerProfileKey(ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, - freqRange, txLevel); + final long txKey = ModemPowerProfile.getAverageBatteryDrainKey( + ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel); final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(txKey, Double.NaN); if (Double.isNaN(drainRateMa)) { diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java new file mode 100644 index 000000000000..8c154e4a0875 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import android.content.pm.PackageManager; +import android.hardware.power.stats.EnergyConsumerType; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.OutcomeReceiver; +import android.os.PersistableBundle; +import android.telephony.AccessNetworkConstants; +import android.telephony.ModemActivityInfo; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.Clock; +import com.android.internal.os.PowerStats; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +public class MobileRadioPowerStatsCollector extends PowerStatsCollector { + private static final String TAG = "MobileRadioPowerStatsCollector"; + + /** + * The soonest the Mobile Radio stats can be updated due to a mobile radio power state change + * after it was last updated. + */ + @VisibleForTesting + protected static final long MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS = 1000 * 60 * 10; + + private static final long MODEM_ACTIVITY_REQUEST_TIMEOUT = 20000; + + private static final long ENERGY_UNSPECIFIED = -1; + + @VisibleForTesting + @AccessNetworkConstants.RadioAccessNetworkType + static final int[] NETWORK_TYPES = { + AccessNetworkConstants.AccessNetworkType.UNKNOWN, + AccessNetworkConstants.AccessNetworkType.GERAN, + AccessNetworkConstants.AccessNetworkType.UTRAN, + AccessNetworkConstants.AccessNetworkType.EUTRAN, + AccessNetworkConstants.AccessNetworkType.CDMA2000, + AccessNetworkConstants.AccessNetworkType.IWLAN, + AccessNetworkConstants.AccessNetworkType.NGRAN + }; + + interface Injector { + Handler getHandler(); + Clock getClock(); + PowerStatsUidResolver getUidResolver(); + PackageManager getPackageManager(); + ConsumedEnergyRetriever getConsumedEnergyRetriever(); + IntSupplier getVoltageSupplier(); + Supplier<NetworkStats> getMobileNetworkStatsSupplier(); + TelephonyManager getTelephonyManager(); + LongSupplier getCallDurationSupplier(); + LongSupplier getPhoneSignalScanDurationSupplier(); + } + + private final Injector mInjector; + + private MobileRadioPowerStatsLayout mLayout; + private boolean mIsInitialized; + + private PowerStats mPowerStats; + private long[] mDeviceStats; + private PowerStatsUidResolver mPowerStatsUidResolver; + private volatile TelephonyManager mTelephonyManager; + private LongSupplier mCallDurationSupplier; + private LongSupplier mScanDurationSupplier; + private volatile Supplier<NetworkStats> mNetworkStatsSupplier; + private ConsumedEnergyRetriever mConsumedEnergyRetriever; + private IntSupplier mVoltageSupplier; + private int[] mEnergyConsumerIds = new int[0]; + private long mLastUpdateTimestampMillis; + private ModemActivityInfo mLastModemActivityInfo; + private NetworkStats mLastNetworkStats; + private long[] mLastConsumedEnergyUws; + private int mLastVoltageMv; + private long mLastCallDuration; + private long mLastScanDuration; + + public MobileRadioPowerStatsCollector(Injector injector, long throttlePeriodMs) { + super(injector.getHandler(), throttlePeriodMs, injector.getClock()); + mInjector = injector; + } + + @Override + public void setEnabled(boolean enabled) { + if (enabled) { + PackageManager packageManager = mInjector.getPackageManager(); + super.setEnabled(packageManager != null + && packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)); + } else { + super.setEnabled(false); + } + } + + private boolean ensureInitialized() { + if (mIsInitialized) { + return true; + } + + if (!isEnabled()) { + return false; + } + + mPowerStatsUidResolver = mInjector.getUidResolver(); + mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever(); + mVoltageSupplier = mInjector.getVoltageSupplier(); + + mTelephonyManager = mInjector.getTelephonyManager(); + mNetworkStatsSupplier = mInjector.getMobileNetworkStatsSupplier(); + mCallDurationSupplier = mInjector.getCallDurationSupplier(); + mScanDurationSupplier = mInjector.getPhoneSignalScanDurationSupplier(); + + mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds( + EnergyConsumerType.MOBILE_RADIO); + mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length]; + Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED); + + mLayout = new MobileRadioPowerStatsLayout(); + mLayout.addDeviceMobileActivity(); + mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length); + mLayout.addStateStats(); + mLayout.addUidNetworkStats(); + mLayout.addDeviceSectionUsageDuration(); + mLayout.addDeviceSectionPowerEstimate(); + mLayout.addUidSectionPowerEstimate(); + + SparseArray<String> stateLabels = new SparseArray<>(); + for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) { + final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR + ? ServiceState.FREQUENCY_RANGE_COUNT : 1; + for (int freq = 0; freq < freqCount; freq++) { + int stateKey = makeStateKey(rat, freq); + StringBuilder sb = new StringBuilder(); + if (rat != BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER) { + sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); + } + if (freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + if (!sb.isEmpty()) { + sb.append(" "); + } + sb.append(ServiceState.frequencyRangeToString(freq)); + } + stateLabels.put(stateKey, !sb.isEmpty() ? sb.toString() : "other"); + } + } + + PersistableBundle extras = new PersistableBundle(); + mLayout.toExtras(extras); + PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, mLayout.getDeviceStatsArrayLength(), + stateLabels, mLayout.getStateStatsArrayLength(), mLayout.getUidStatsArrayLength(), + extras); + mPowerStats = new PowerStats(powerStatsDescriptor); + mDeviceStats = mPowerStats.stats; + + mIsInitialized = true; + return true; + } + + @Override + protected PowerStats collectStats() { + if (!ensureInitialized()) { + return null; + } + + collectModemActivityInfo(); + + collectNetworkStats(); + + if (mEnergyConsumerIds.length != 0) { + collectEnergyConsumers(); + } + + if (mPowerStats.durationMs == 0) { + setTimestamp(mClock.elapsedRealtime()); + } + + return mPowerStats; + } + + private void collectModemActivityInfo() { + if (mTelephonyManager == null) { + return; + } + + CompletableFuture<ModemActivityInfo> immediateFuture = new CompletableFuture<>(); + mTelephonyManager.requestModemActivityInfo(Runnable::run, + new OutcomeReceiver<>() { + @Override + public void onResult(ModemActivityInfo result) { + immediateFuture.complete(result); + } + + @Override + public void onError(TelephonyManager.ModemActivityInfoException e) { + Slog.w(TAG, "error reading modem stats:" + e); + immediateFuture.complete(null); + } + }); + + ModemActivityInfo activityInfo; + try { + activityInfo = immediateFuture.get(MODEM_ACTIVITY_REQUEST_TIMEOUT, + TimeUnit.MILLISECONDS); + } catch (Exception e) { + Slog.e(TAG, "Cannot acquire ModemActivityInfo"); + activityInfo = null; + } + + ModemActivityInfo deltaInfo = mLastModemActivityInfo == null + ? (activityInfo == null ? null : activityInfo.getDelta(activityInfo)) + : mLastModemActivityInfo.getDelta(activityInfo); + + mLastModemActivityInfo = activityInfo; + + if (deltaInfo == null) { + return; + } + + setTimestamp(deltaInfo.getTimestampMillis()); + mLayout.setDeviceSleepTime(mDeviceStats, deltaInfo.getSleepTimeMillis()); + mLayout.setDeviceIdleTime(mDeviceStats, deltaInfo.getIdleTimeMillis()); + + long callDuration = mCallDurationSupplier.getAsLong(); + if (callDuration >= mLastCallDuration) { + mLayout.setDeviceCallTime(mDeviceStats, callDuration - mLastCallDuration); + } + mLastCallDuration = callDuration; + + long scanDuration = mScanDurationSupplier.getAsLong(); + if (scanDuration >= mLastScanDuration) { + mLayout.setDeviceScanTime(mDeviceStats, scanDuration - mLastScanDuration); + } + mLastScanDuration = scanDuration; + + SparseArray<long[]> stateStats = mPowerStats.stateStats; + stateStats.clear(); + + if (deltaInfo.getSpecificInfoLength() == 0) { + mLayout.addRxTxTimesForRat(stateStats, + AccessNetworkConstants.AccessNetworkType.UNKNOWN, + ServiceState.FREQUENCY_RANGE_UNKNOWN, + deltaInfo.getReceiveTimeMillis(), + deltaInfo.getTransmitTimeMillis()); + } else { + for (int rat = 0; rat < NETWORK_TYPES.length; rat++) { + if (rat == AccessNetworkConstants.AccessNetworkType.NGRAN) { + for (int freq = 0; freq < ServiceState.FREQUENCY_RANGE_COUNT; freq++) { + mLayout.addRxTxTimesForRat(stateStats, rat, freq, + deltaInfo.getReceiveTimeMillis(rat, freq), + deltaInfo.getTransmitTimeMillis(rat, freq)); + } + } else { + mLayout.addRxTxTimesForRat(stateStats, rat, + ServiceState.FREQUENCY_RANGE_UNKNOWN, + deltaInfo.getReceiveTimeMillis(rat), + deltaInfo.getTransmitTimeMillis(rat)); + } + } + } + } + + private void collectNetworkStats() { + mPowerStats.uidStats.clear(); + + NetworkStats networkStats = mNetworkStatsSupplier.get(); + if (networkStats == null) { + return; + } + + List<BatteryStatsImpl.NetworkStatsDelta> delta = + BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats); + mLastNetworkStats = networkStats; + for (int i = delta.size() - 1; i >= 0; i--) { + BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i); + long rxBytes = uidDelta.getRxBytes(); + long txBytes = uidDelta.getTxBytes(); + long rxPackets = uidDelta.getRxPackets(); + long txPackets = uidDelta.getTxPackets(); + if (rxBytes == 0 && txBytes == 0 && rxPackets == 0 && txPackets == 0) { + continue; + } + + int uid = mPowerStatsUidResolver.mapUid(uidDelta.getUid()); + long[] stats = mPowerStats.uidStats.get(uid); + if (stats == null) { + stats = new long[mLayout.getUidStatsArrayLength()]; + mPowerStats.uidStats.put(uid, stats); + mLayout.setUidRxBytes(stats, rxBytes); + mLayout.setUidTxBytes(stats, txBytes); + mLayout.setUidRxPackets(stats, rxPackets); + mLayout.setUidTxPackets(stats, txPackets); + } else { + mLayout.setUidRxBytes(stats, mLayout.getUidRxBytes(stats) + rxBytes); + mLayout.setUidTxBytes(stats, mLayout.getUidTxBytes(stats) + txBytes); + mLayout.setUidRxPackets(stats, mLayout.getUidRxPackets(stats) + rxPackets); + mLayout.setUidTxPackets(stats, mLayout.getUidTxPackets(stats) + txPackets); + } + } + } + + private void collectEnergyConsumers() { + int voltageMv = mVoltageSupplier.getAsInt(); + if (voltageMv <= 0) { + Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv + + " mV) when querying energy consumers"); + return; + } + + int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv; + mLastVoltageMv = voltageMv; + + long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds); + if (energyUws == null) { + return; + } + + for (int i = energyUws.length - 1; i >= 0; i--) { + long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED + ? energyUws[i] - mLastConsumedEnergyUws[i] : 0; + if (energyDelta < 0) { + // Likely, restart of powerstats HAL + energyDelta = 0; + } + mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage)); + mLastConsumedEnergyUws[i] = energyUws[i]; + } + } + + static int makeStateKey(int rat, int freqRange) { + if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) { + return rat | (freqRange << 8); + } else { + return rat; + } + } + + private void setTimestamp(long timestamp) { + mPowerStats.durationMs = Math.max(timestamp - mLastUpdateTimestampMillis, 0); + mLastUpdateTimestampMillis = timestamp; + } + + @BatteryStats.RadioAccessTechnology + static int mapRadioAccessNetworkTypeToRadioAccessTechnology( + @AccessNetworkConstants.RadioAccessNetworkType int networkType) { + switch (networkType) { + case AccessNetworkConstants.AccessNetworkType.NGRAN: + return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR; + case AccessNetworkConstants.AccessNetworkType.EUTRAN: + return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE; + case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough + case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough + case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough + case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough + case AccessNetworkConstants.AccessNetworkType.IWLAN: + return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER; + default: + Slog.w(TAG, + "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER"); + return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER; + } + } +} diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java new file mode 100644 index 000000000000..81d7c2fa2880 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import android.annotation.NonNull; +import android.os.PersistableBundle; +import android.telephony.ModemActivityInfo; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.os.PowerStats; + +/** + * Captures the positions and lengths of sections of the stats array, such as time-in-state, + * power usage estimates etc. + */ +class MobileRadioPowerStatsLayout extends PowerStatsLayout { + private static final String TAG = "MobileRadioPowerStatsLayout"; + private static final String EXTRA_DEVICE_SLEEP_TIME_POSITION = "dt-sleep"; + private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle"; + private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan"; + private static final String EXTRA_DEVICE_CALL_TIME_POSITION = "dt-call"; + private static final String EXTRA_DEVICE_CALL_POWER_POSITION = "dp-call"; + private static final String EXTRA_STATE_RX_TIME_POSITION = "srx"; + private static final String EXTRA_STATE_TX_TIMES_POSITION = "stx"; + private static final String EXTRA_STATE_TX_TIMES_COUNT = "stxc"; + private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb"; + private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb"; + private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp"; + private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp"; + + private int mDeviceSleepTimePosition; + private int mDeviceIdleTimePosition; + private int mDeviceScanTimePosition; + private int mDeviceCallTimePosition; + private int mDeviceCallPowerPosition; + private int mStateRxTimePosition; + private int mStateTxTimesPosition; + private int mStateTxTimesCount; + private int mUidRxBytesPosition; + private int mUidTxBytesPosition; + private int mUidRxPacketsPosition; + private int mUidTxPacketsPosition; + + MobileRadioPowerStatsLayout() { + } + + MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) { + super(descriptor); + } + + void addDeviceMobileActivity() { + mDeviceSleepTimePosition = addDeviceSection(1); + mDeviceIdleTimePosition = addDeviceSection(1); + mDeviceScanTimePosition = addDeviceSection(1); + mDeviceCallTimePosition = addDeviceSection(1); + } + + void addStateStats() { + mStateRxTimePosition = addStateSection(1); + mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels(); + mStateTxTimesPosition = addStateSection(mStateTxTimesCount); + } + + void addUidNetworkStats() { + mUidRxBytesPosition = addUidSection(1); + mUidTxBytesPosition = addUidSection(1); + mUidRxPacketsPosition = addUidSection(1); + mUidTxPacketsPosition = addUidSection(1); + } + + @Override + public void addDeviceSectionPowerEstimate() { + super.addDeviceSectionPowerEstimate(); + mDeviceCallPowerPosition = addDeviceSection(1); + } + + public void setDeviceSleepTime(long[] stats, long durationMillis) { + stats[mDeviceSleepTimePosition] = durationMillis; + } + + public long getDeviceSleepTime(long[] stats) { + return stats[mDeviceSleepTimePosition]; + } + + public void setDeviceIdleTime(long[] stats, long durationMillis) { + stats[mDeviceIdleTimePosition] = durationMillis; + } + + public long getDeviceIdleTime(long[] stats) { + return stats[mDeviceIdleTimePosition]; + } + + public void setDeviceScanTime(long[] stats, long durationMillis) { + stats[mDeviceScanTimePosition] = durationMillis; + } + + public long getDeviceScanTime(long[] stats) { + return stats[mDeviceScanTimePosition]; + } + + public void setDeviceCallTime(long[] stats, long durationMillis) { + stats[mDeviceCallTimePosition] = durationMillis; + } + + public long getDeviceCallTime(long[] stats) { + return stats[mDeviceCallTimePosition]; + } + + public void setDeviceCallPowerEstimate(long[] stats, double power) { + stats[mDeviceCallPowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); + } + + public double getDeviceCallPowerEstimate(long[] stats) { + return stats[mDeviceCallPowerPosition] / MILLI_TO_NANO_MULTIPLIER; + } + + public void setStateRxTime(long[] stats, long durationMillis) { + stats[mStateRxTimePosition] = durationMillis; + } + + public long getStateRxTime(long[] stats) { + return stats[mStateRxTimePosition]; + } + + public void setStateTxTime(long[] stats, int level, int durationMillis) { + stats[mStateTxTimesPosition + level] = durationMillis; + } + + public long getStateTxTime(long[] stats, int level) { + return stats[mStateTxTimesPosition + level]; + } + + public void setUidRxBytes(long[] stats, long count) { + stats[mUidRxBytesPosition] = count; + } + + public long getUidRxBytes(long[] stats) { + return stats[mUidRxBytesPosition]; + } + + public void setUidTxBytes(long[] stats, long count) { + stats[mUidTxBytesPosition] = count; + } + + public long getUidTxBytes(long[] stats) { + return stats[mUidTxBytesPosition]; + } + + public void setUidRxPackets(long[] stats, long count) { + stats[mUidRxPacketsPosition] = count; + } + + public long getUidRxPackets(long[] stats) { + return stats[mUidRxPacketsPosition]; + } + + public void setUidTxPackets(long[] stats, long count) { + stats[mUidTxPacketsPosition] = count; + } + + public long getUidTxPackets(long[] stats) { + return stats[mUidTxPacketsPosition]; + } + + /** + * Copies the elements of the stats array layout into <code>extras</code> + */ + public void toExtras(PersistableBundle extras) { + super.toExtras(extras); + extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition); + extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition); + extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition); + extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition); + extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition); + extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition); + extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition); + extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount); + extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition); + extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition); + extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition); + extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition); + } + + /** + * Retrieves elements of the stats array layout from <code>extras</code> + */ + public void fromExtras(PersistableBundle extras) { + super.fromExtras(extras); + mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION); + mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION); + mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION); + mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION); + mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION); + mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION); + mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION); + mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT); + mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION); + mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION); + mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION); + mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION); + } + + public void addRxTxTimesForRat(SparseArray<long[]> stateStats, int networkType, int freqRange, + long rxTime, int[] txTime) { + if (txTime.length != mStateTxTimesCount) { + Slog.wtf(TAG, "Invalid TX time array size: " + txTime.length); + return; + } + + boolean nonZero = false; + if (rxTime != 0) { + nonZero = true; + } else { + for (int i = txTime.length - 1; i >= 0; i--) { + if (txTime[i] != 0) { + nonZero = true; + break; + } + } + } + + if (!nonZero) { + return; + } + + int rat = MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology( + networkType); + int stateKey = MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange); + long[] stats = stateStats.get(stateKey); + if (stats == null) { + stats = new long[getStateStatsArrayLength()]; + stateStats.put(stateKey, stats); + } + + stats[mStateRxTimePosition] += rxTime; + for (int i = mStateTxTimesCount - 1; i >= 0; i--) { + stats[mStateTxTimesPosition + i] += txTime[i]; + } + } +} diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java new file mode 100644 index 000000000000..c97c64bafcba --- /dev/null +++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import android.os.BatteryStats; +import android.telephony.CellSignalStrength; +import android.telephony.ModemActivityInfo; +import android.telephony.ServiceState; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; +import com.android.internal.power.ModemPowerProfile; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MobileRadioPowerStatsProcessor extends PowerStatsProcessor { + private static final String TAG = "MobileRadioPowerStatsProcessor"; + private static final boolean DEBUG = false; + + private static final int NUM_SIGNAL_STRENGTH_LEVELS = + CellSignalStrength.getNumSignalStrengthLevels(); + private static final int IGNORE = -1; + + private final UsageBasedPowerEstimator mSleepPowerEstimator; + private final UsageBasedPowerEstimator mIdlePowerEstimator; + private final UsageBasedPowerEstimator mCallPowerEstimator; + private final UsageBasedPowerEstimator mScanPowerEstimator; + + private static class RxTxPowerEstimators { + UsageBasedPowerEstimator mRxPowerEstimator; + UsageBasedPowerEstimator[] mTxPowerEstimators = + new UsageBasedPowerEstimator[ModemActivityInfo.getNumTxPowerLevels()]; + } + + private final SparseArray<RxTxPowerEstimators> mRxTxPowerEstimators = new SparseArray<>(); + + private PowerStats.Descriptor mLastUsedDescriptor; + private MobileRadioPowerStatsLayout mStatsLayout; + // Sequence of steps for power estimation and intermediate results. + private PowerEstimationPlan mPlan; + + private long[] mTmpDeviceStatsArray; + private long[] mTmpStateStatsArray; + private long[] mTmpUidStatsArray; + + public MobileRadioPowerStatsProcessor(PowerProfile powerProfile) { + final double sleepDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa( + PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP, + Double.NaN); + if (Double.isNaN(sleepDrainRateMa)) { + mSleepPowerEstimator = null; + } else { + mSleepPowerEstimator = new UsageBasedPowerEstimator(sleepDrainRateMa); + } + + final double idleDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa( + PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE, + Double.NaN); + if (Double.isNaN(idleDrainRateMa)) { + mIdlePowerEstimator = null; + } else { + mIdlePowerEstimator = new UsageBasedPowerEstimator(idleDrainRateMa); + } + + // Instantiate legacy power estimators + double powerRadioActiveMa = + powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, Double.NaN); + if (Double.isNaN(powerRadioActiveMa)) { + double sum = 0; + sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); + for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { + sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); + } + powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1); + } + mCallPowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa); + + mScanPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0)); + + for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) { + final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR + ? ServiceState.FREQUENCY_RANGE_COUNT : 1; + for (int freqRange = 0; freqRange < freqCount; freqRange++) { + mRxTxPowerEstimators.put( + MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange), + buildRxTxPowerEstimators(powerProfile, rat, freqRange)); + } + } + } + + private static RxTxPowerEstimators buildRxTxPowerEstimators(PowerProfile powerProfile, int rat, + int freqRange) { + RxTxPowerEstimators estimators = new RxTxPowerEstimators(); + long rxKey = ModemPowerProfile.getAverageBatteryDrainKey( + ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE); + double rxDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN); + if (Double.isNaN(rxDrainRateMa)) { + Log.w(TAG, "Unavailable Power Profile constant for key 0x" + + Long.toHexString(rxKey)); + rxDrainRateMa = 0; + } + estimators.mRxPowerEstimator = new UsageBasedPowerEstimator(rxDrainRateMa); + for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) { + long txKey = ModemPowerProfile.getAverageBatteryDrainKey( + ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel); + double txDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(txKey, + Double.NaN); + if (Double.isNaN(txDrainRateMa)) { + Log.w(TAG, "Unavailable Power Profile constant for key 0x" + + Long.toHexString(txKey)); + txDrainRateMa = 0; + } + estimators.mTxPowerEstimators[txLevel] = new UsageBasedPowerEstimator(txDrainRateMa); + } + return estimators; + } + + private static class Intermediates { + /** + * Number of received packets + */ + public long rxPackets; + /** + * Number of transmitted packets + */ + public long txPackets; + /** + * Estimated power for the RX state of the modem. + */ + public double rxPower; + /** + * Estimated power for the TX state of the modem. + */ + public double txPower; + /** + * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem. + */ + public double inactivePower; + /** + * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem. + */ + public double callPower; + /** + * Measured consumed energy from power monitoring hardware (micro-coulombs) + */ + public long consumedEnergy; + } + + @Override + void finish(PowerComponentAggregatedPowerStats stats) { + if (stats.getPowerStatsDescriptor() == null) { + return; + } + + unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor()); + + if (mPlan == null) { + mPlan = new PowerEstimationPlan(stats.getConfig()); + } + + for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) { + DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i); + Intermediates intermediates = new Intermediates(); + estimation.intermediates = intermediates; + computeDevicePowerEstimates(stats, estimation.stateValues, intermediates); + } + + if (mStatsLayout.getEnergyConsumerCount() != 0) { + double ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy(); + if (ratio != 1) { + for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) { + DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i); + adjustDevicePowerEstimates(stats, estimation.stateValues, + (Intermediates) estimation.intermediates, ratio); + } + } + } + + combineDeviceStateEstimates(); + + ArrayList<Integer> uids = new ArrayList<>(); + stats.collectUids(uids); + if (!uids.isEmpty()) { + for (int uid : uids) { + for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { + computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i)); + } + } + + for (int uid : uids) { + for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { + computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i)); + } + } + } + mPlan.resetIntermediates(); + } + + private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) { + if (descriptor.equals(mLastUsedDescriptor)) { + return; + } + + mLastUsedDescriptor = descriptor; + mStatsLayout = new MobileRadioPowerStatsLayout(descriptor); + mTmpDeviceStatsArray = new long[descriptor.statsArrayLength]; + mTmpStateStatsArray = new long[descriptor.stateStatsArrayLength]; + mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength]; + } + + /** + * Compute power estimates using the power profile. + */ + private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, + int[] deviceStates, Intermediates intermediates) { + if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) { + return; + } + + for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) { + intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i); + } + + if (mSleepPowerEstimator != null) { + intermediates.inactivePower += mSleepPowerEstimator.calculatePower( + mStatsLayout.getDeviceSleepTime(mTmpDeviceStatsArray)); + } + + if (mIdlePowerEstimator != null) { + intermediates.inactivePower += mIdlePowerEstimator.calculatePower( + mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray)); + } + + if (mScanPowerEstimator != null) { + intermediates.inactivePower += mScanPowerEstimator.calculatePower( + mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray)); + } + + stats.forEachStateStatsKey(key -> { + RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key); + stats.getStateStats(mTmpStateStatsArray, key, deviceStates); + long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray); + intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime); + for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) { + long txTime = mStatsLayout.getStateTxTime(mTmpStateStatsArray, txLevel); + intermediates.txPower += + estimators.mTxPowerEstimators[txLevel].calculatePower(txTime); + } + }); + + if (mCallPowerEstimator != null) { + intermediates.callPower = mCallPowerEstimator.calculatePower( + mStatsLayout.getDeviceCallTime(mTmpDeviceStatsArray)); + } + + mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, + intermediates.rxPower + intermediates.txPower + intermediates.inactivePower); + mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower); + stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray); + } + + /** + * Compute an adjustment ratio using the total power estimated using the power profile + * and the total power measured by hardware. + */ + private double computeEstimateAdjustmentRatioUsingConsumedEnergy() { + long totalConsumedEnergy = 0; + double totalPower = 0; + + for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) { + Intermediates intermediates = + (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates; + totalPower += intermediates.rxPower + intermediates.txPower + + intermediates.inactivePower + intermediates.callPower; + totalConsumedEnergy += intermediates.consumedEnergy; + } + + if (totalPower == 0) { + return 1; + } + + return uCtoMah(totalConsumedEnergy) / totalPower; + } + + /** + * Uniformly apply the same adjustment to all power estimates in order to ensure that the total + * estimated power matches the measured consumed power. We are not claiming that all + * averages captured in the power profile have to be off by the same percentage in reality. + */ + private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, + int[] deviceStates, Intermediates intermediates, double ratio) { + intermediates.rxPower *= ratio; + intermediates.txPower *= ratio; + intermediates.inactivePower *= ratio; + intermediates.callPower *= ratio; + + if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) { + return; + } + + mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, + intermediates.rxPower + intermediates.txPower + intermediates.inactivePower); + mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower); + stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray); + } + + /** + * This step is effectively a no-op in the cases where we track the same states for + * the entire device and all UIDs (e.g. screen on/off, on-battery/on-charger etc). However, + * if the lists of tracked states are not the same, we need to combine some estimates + * before distributing them proportionally to UIDs. + */ + private void combineDeviceStateEstimates() { + for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) { + CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i); + Intermediates cdseIntermediates = new Intermediates(); + cdse.intermediates = cdseIntermediates; + List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations; + for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) { + DeviceStateEstimation dse = deviceStateEstimations.get(j); + Intermediates intermediates = (Intermediates) dse.intermediates; + cdseIntermediates.rxPower += intermediates.rxPower; + cdseIntermediates.txPower += intermediates.txPower; + cdseIntermediates.inactivePower += intermediates.inactivePower; + cdseIntermediates.consumedEnergy += intermediates.consumedEnergy; + } + } + } + + private void computeUidRxTxTotals(PowerComponentAggregatedPowerStats stats, int uid, + UidStateEstimate uidStateEstimate) { + Intermediates intermediates = + (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates; + for (UidStateProportionalEstimate proportionalEstimate : + uidStateEstimate.proportionalEstimates) { + if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) { + continue; + } + + intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray); + intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray); + } + } + + private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid, + UidStateEstimate uidStateEstimate) { + Intermediates intermediates = + (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates; + for (UidStateProportionalEstimate proportionalEstimate : + uidStateEstimate.proportionalEstimates) { + if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) { + continue; + } + + double power = 0; + if (intermediates.rxPackets != 0) { + power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray) + / intermediates.rxPackets; + } + if (intermediates.txPackets != 0) { + power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray) + / intermediates.txPackets; + } + + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + + if (DEBUG) { + Slog.d(TAG, "UID: " + uid + + " states: " + Arrays.toString(proportionalEstimate.stateValues) + + " stats: " + Arrays.toString(mTmpUidStatsArray) + + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray) + + " rx-power: " + intermediates.rxPower + + " rx-packets: " + intermediates.rxPackets + + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray) + + " tx-power: " + intermediates.txPower + + " tx-packets: " + intermediates.txPackets + + " power: " + power); + } + } + } + + @Override + String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { + unpackPowerStatsDescriptor(descriptor); + return "idle: " + mStatsLayout.getDeviceIdleTime(stats) + + " sleep: " + mStatsLayout.getDeviceSleepTime(stats) + + " scan: " + mStatsLayout.getDeviceScanTime(stats) + + " power: " + mStatsLayout.getDevicePowerEstimate(stats); + } + + @Override + String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { + unpackPowerStatsDescriptor(descriptor); + StringBuilder sb = new StringBuilder(); + sb.append(descriptor.getStateLabel(key)); + sb.append(" rx: ").append(mStatsLayout.getStateRxTime(stats)); + sb.append(" tx: "); + for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) { + if (txLevel != 0) { + sb.append(", "); + } + sb.append(mStatsLayout.getStateTxTime(stats, txLevel)); + } + return sb.toString(); + } + + @Override + String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { + unpackPowerStatsDescriptor(descriptor); + return "rx: " + mStatsLayout.getUidRxPackets(stats) + + " tx: " + mStatsLayout.getUidTxPackets(stats) + + " power: " + mStatsLayout.getUidPowerEstimate(stats); + } +} diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java index 935695008a9a..6c4a2b6e6359 100644 --- a/services/core/java/com/android/server/power/stats/MultiStateStats.java +++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java @@ -288,6 +288,14 @@ public class MultiStateStats { } /** + * Copies time-in-state and timestamps from the supplied prototype. Does not + * copy accumulated counts. + */ + public void copyStatesFrom(MultiStateStats otherStats) { + mCounter.copyStatesFrom(otherStats.mCounter); + } + + /** * Updates the current composite state by changing one of the States supplied to the Factory * constructor. * diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java new file mode 100644 index 000000000000..62b653f61373 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import android.os.BatteryConsumer; +import android.os.PersistableBundle; + +import com.android.internal.os.PowerStats; + +public class PhoneCallPowerStatsProcessor extends PowerStatsProcessor { + private final PowerStatsLayout mStatsLayout; + private final PowerStats.Descriptor mDescriptor; + private final long[] mTmpDeviceStats; + private PowerStats.Descriptor mMobileRadioStatsDescriptor; + private MobileRadioPowerStatsLayout mMobileRadioStatsLayout; + private long[] mTmpMobileRadioDeviceStats; + + public PhoneCallPowerStatsProcessor() { + mStatsLayout = new PowerStatsLayout(); + mStatsLayout.addDeviceSectionPowerEstimate(); + PersistableBundle extras = new PersistableBundle(); + mStatsLayout.toExtras(extras); + mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_PHONE, + mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras); + mTmpDeviceStats = new long[mDescriptor.statsArrayLength]; + } + + @Override + void finish(PowerComponentAggregatedPowerStats stats) { + stats.setPowerStatsDescriptor(mDescriptor); + + PowerComponentAggregatedPowerStats mobileRadioStats = + stats.getAggregatedPowerStats().getPowerComponentStats( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); + if (mobileRadioStats == null) { + return; + } + + if (mMobileRadioStatsDescriptor == null) { + mMobileRadioStatsDescriptor = mobileRadioStats.getPowerStatsDescriptor(); + if (mMobileRadioStatsDescriptor == null) { + return; + } + + mMobileRadioStatsLayout = + new MobileRadioPowerStatsLayout( + mMobileRadioStatsDescriptor); + mTmpMobileRadioDeviceStats = new long[mMobileRadioStatsDescriptor.statsArrayLength]; + } + + MultiStateStats.States[] deviceStateConfig = + mobileRadioStats.getConfig().getDeviceStateConfig(); + + // Phone call power estimates have already been calculated by the mobile radio stats + // processor. All that remains to be done is copy the estimates over. + MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig, + states -> { + mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states); + double callPowerEstimate = + mMobileRadioStatsLayout.getDeviceCallPowerEstimate( + mTmpMobileRadioDeviceStats); + mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate); + stats.setDeviceStats(states, mTmpDeviceStats); + }); + } + + @Override + String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) { + return "power: " + mStatsLayout.getDevicePowerEstimate(stats); + } + + @Override + String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) { + // Unsupported for this power component + return null; + } + + @Override + String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) { + // Unsupported for this power component + return null; + } +} diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java index 1637022f705d..6d58307dbefa 100644 --- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java @@ -18,6 +18,7 @@ package com.android.server.power.stats; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.SparseArray; @@ -29,7 +30,10 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; import java.util.Collection; +import java.util.function.IntConsumer; /** * Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class @@ -41,22 +45,28 @@ class PowerComponentAggregatedPowerStats { static final String XML_TAG_POWER_COMPONENT = "power_component"; static final String XML_ATTR_ID = "id"; private static final String XML_TAG_DEVICE_STATS = "device-stats"; + private static final String XML_TAG_STATE_STATS = "state-stats"; + private static final String XML_ATTR_KEY = "key"; private static final String XML_TAG_UID_STATS = "uid-stats"; private static final String XML_ATTR_UID = "uid"; private static final long UNKNOWN = -1; public final int powerComponentId; - private final MultiStateStats.States[] mDeviceStateConfig; - private final MultiStateStats.States[] mUidStateConfig; + @NonNull + private final AggregatedPowerStats mAggregatedPowerStats; @NonNull private final AggregatedPowerStatsConfig.PowerComponent mConfig; + private final MultiStateStats.States[] mDeviceStateConfig; + private final MultiStateStats.States[] mUidStateConfig; private final int[] mDeviceStates; private MultiStateStats.Factory mStatsFactory; + private MultiStateStats.Factory mStateStatsFactory; private MultiStateStats.Factory mUidStatsFactory; private PowerStats.Descriptor mPowerStatsDescriptor; private long mPowerStatsTimestamp; private MultiStateStats mDeviceStats; + private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>(); private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private static class UidStats { @@ -64,7 +74,9 @@ class PowerComponentAggregatedPowerStats { public MultiStateStats stats; } - PowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) { + PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats, + @NonNull AggregatedPowerStatsConfig.PowerComponent config) { + mAggregatedPowerStats = aggregatedPowerStats; mConfig = config; powerComponentId = config.getPowerComponentId(); mDeviceStateConfig = config.getDeviceStateConfig(); @@ -74,6 +86,11 @@ class PowerComponentAggregatedPowerStats { } @NonNull + AggregatedPowerStats getAggregatedPowerStats() { + return mAggregatedPowerStats; + } + + @NonNull public AggregatedPowerStatsConfig.PowerComponent getConfig() { return mConfig; } @@ -83,16 +100,25 @@ class PowerComponentAggregatedPowerStats { return mPowerStatsDescriptor; } - void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) { + public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) { + mPowerStatsDescriptor = powerStatsDescriptor; + } + + void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, + long timestampMs) { if (mDeviceStats == null) { - createDeviceStats(); + createDeviceStats(timestampMs); } mDeviceStates[stateId] = state; if (mDeviceStateConfig[stateId].isTracked()) { if (mDeviceStats != null) { - mDeviceStats.setState(stateId, state, time); + mDeviceStats.setState(stateId, state, timestampMs); + } + for (int i = mStateStats.size() - 1; i >= 0; i--) { + MultiStateStats stateStats = mStateStats.valueAt(i); + stateStats.setState(stateId, state, timestampMs); } } @@ -100,36 +126,39 @@ class PowerComponentAggregatedPowerStats { for (int i = mUidStats.size() - 1; i >= 0; i--) { PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i); if (uidStats.stats == null) { - createUidStats(uidStats); + createUidStats(uidStats, timestampMs); } uidStats.states[stateId] = state; if (uidStats.stats != null) { - uidStats.stats.setState(stateId, state, time); + uidStats.stats.setState(stateId, state, timestampMs); } } } } void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, - long time) { + long timestampMs) { if (!mUidStateConfig[stateId].isTracked()) { return; } UidStats uidStats = getUidStats(uid); if (uidStats.stats == null) { - createUidStats(uidStats); + createUidStats(uidStats, timestampMs); } uidStats.states[stateId] = state; if (uidStats.stats != null) { - uidStats.stats.setState(stateId, state, time); + uidStats.stats.setState(stateId, state, timestampMs); } } void setDeviceStats(@AggregatedPowerStatsConfig.TrackedState int[] states, long[] values) { + if (mDeviceStats == null) { + createDeviceStats(0); + } mDeviceStats.setStats(states, values); } @@ -147,16 +176,24 @@ class PowerComponentAggregatedPowerStats { mPowerStatsDescriptor = powerStats.descriptor; if (mDeviceStats == null) { - createDeviceStats(); + createDeviceStats(timestampMs); } + for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) { + int key = powerStats.stateStats.keyAt(i); + MultiStateStats stateStats = mStateStats.get(key); + if (stateStats == null) { + stateStats = createStateStats(key, timestampMs); + } + stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs); + } mDeviceStats.increment(powerStats.stats, timestampMs); for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) { int uid = powerStats.uidStats.keyAt(i); PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid); if (uidStats.stats == null) { - createUidStats(uidStats); + createUidStats(uidStats, timestampMs); } uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs); } @@ -168,6 +205,7 @@ class PowerComponentAggregatedPowerStats { mStatsFactory = null; mUidStatsFactory = null; mDeviceStats = null; + mStateStats.clear(); for (int i = mUidStats.size() - 1; i >= 0; i--) { mUidStats.valueAt(i).stats = null; } @@ -178,6 +216,13 @@ class PowerComponentAggregatedPowerStats { if (uidStats == null) { uidStats = new UidStats(); uidStats.states = new int[mUidStateConfig.length]; + for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) { + if (mUidStateConfig[stateId].isTracked() + && stateId < mDeviceStateConfig.length + && mDeviceStateConfig[stateId].isTracked()) { + uidStats.states[stateId] = mDeviceStates[stateId]; + } + } mUidStats.put(uid, uidStats); } return uidStats; @@ -204,6 +249,26 @@ class PowerComponentAggregatedPowerStats { return false; } + boolean getStateStats(long[] outValues, int key, int[] deviceStates) { + if (deviceStates.length != mDeviceStateConfig.length) { + throw new IllegalArgumentException( + "Invalid number of tracked states: " + deviceStates.length + + " expected: " + mDeviceStateConfig.length); + } + MultiStateStats stateStats = mStateStats.get(key); + if (stateStats != null) { + stateStats.getStats(outValues, deviceStates); + return true; + } + return false; + } + + void forEachStateStatsKey(IntConsumer consumer) { + for (int i = mStateStats.size() - 1; i >= 0; i--) { + consumer.accept(mStateStats.keyAt(i)); + } + } + boolean getUidStats(long[] outValues, int uid, int[] uidStates) { if (uidStates.length != mUidStateConfig.length) { throw new IllegalArgumentException( @@ -218,7 +283,7 @@ class PowerComponentAggregatedPowerStats { return false; } - private void createDeviceStats() { + private void createDeviceStats(long timestampMs) { if (mStatsFactory == null) { if (mPowerStatsDescriptor == null) { return; @@ -229,13 +294,39 @@ class PowerComponentAggregatedPowerStats { mDeviceStats = mStatsFactory.create(); if (mPowerStatsTimestamp != UNKNOWN) { + timestampMs = mPowerStatsTimestamp; + } + if (timestampMs != UNKNOWN) { for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) { - mDeviceStats.setState(stateId, mDeviceStates[stateId], mPowerStatsTimestamp); + int state = mDeviceStates[stateId]; + mDeviceStats.setState(stateId, state, timestampMs); + for (int i = mStateStats.size() - 1; i >= 0; i--) { + MultiStateStats stateStats = mStateStats.valueAt(i); + stateStats.setState(stateId, state, timestampMs); + } + } + } + } + + private MultiStateStats createStateStats(int key, long timestampMs) { + if (mStateStatsFactory == null) { + if (mPowerStatsDescriptor == null) { + return null; } + mStateStatsFactory = new MultiStateStats.Factory( + mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig); } + + MultiStateStats stateStats = mStateStatsFactory.create(); + mStateStats.put(key, stateStats); + if (mDeviceStats != null) { + stateStats.copyStatesFrom(mDeviceStats); + } + + return stateStats; } - private void createUidStats(UidStats uidStats) { + private void createUidStats(UidStats uidStats, long timestampMs) { if (mUidStatsFactory == null) { if (mPowerStatsDescriptor == null) { return; @@ -245,9 +336,13 @@ class PowerComponentAggregatedPowerStats { } uidStats.stats = mUidStatsFactory.create(); - for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) { - if (mPowerStatsTimestamp != UNKNOWN) { - uidStats.stats.setState(stateId, uidStats.states[stateId], mPowerStatsTimestamp); + + if (mPowerStatsTimestamp != UNKNOWN) { + timestampMs = mPowerStatsTimestamp; + } + if (timestampMs != UNKNOWN) { + for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) { + uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs); } } } @@ -268,6 +363,13 @@ class PowerComponentAggregatedPowerStats { serializer.endTag(null, XML_TAG_DEVICE_STATS); } + for (int i = 0; i < mStateStats.size(); i++) { + serializer.startTag(null, XML_TAG_STATE_STATS); + serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i)); + mStateStats.valueAt(i).writeXml(serializer); + serializer.endTag(null, XML_TAG_STATE_STATS); + } + for (int i = mUidStats.size() - 1; i >= 0; i--) { int uid = mUidStats.keyAt(i); UidStats uidStats = mUidStats.valueAt(i); @@ -285,8 +387,10 @@ class PowerComponentAggregatedPowerStats { public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException { + String outerTag = parser.getName(); int eventType = parser.getEventType(); - while (eventType != XmlPullParser.END_DOCUMENT) { + while (eventType != XmlPullParser.END_DOCUMENT + && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) { if (eventType == XmlPullParser.START_TAG) { switch (parser.getName()) { case PowerStats.Descriptor.XML_TAG_DESCRIPTOR: @@ -297,17 +401,27 @@ class PowerComponentAggregatedPowerStats { break; case XML_TAG_DEVICE_STATS: if (mDeviceStats == null) { - createDeviceStats(); + createDeviceStats(UNKNOWN); } if (!mDeviceStats.readFromXml(parser)) { return false; } break; + case XML_TAG_STATE_STATS: + int key = parser.getAttributeInt(null, XML_ATTR_KEY); + MultiStateStats stats = mStateStats.get(key); + if (stats == null) { + stats = createStateStats(key, UNKNOWN); + } + if (!stats.readFromXml(parser)) { + return false; + } + break; case XML_TAG_UID_STATS: int uid = parser.getAttributeInt(null, XML_ATTR_UID); UidStats uidStats = getUidStats(uid); if (uidStats.stats == null) { - createUidStats(uidStats); + createUidStats(uidStats, UNKNOWN); } if (!uidStats.stats.readFromXml(parser)) { return false; @@ -328,6 +442,21 @@ class PowerComponentAggregatedPowerStats { mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats)); ipw.decreaseIndent(); } + + if (mStateStats.size() != 0) { + ipw.increaseIndent(); + ipw.println(mPowerStatsDescriptor.name + " states"); + ipw.increaseIndent(); + for (int i = 0; i < mStateStats.size(); i++) { + int key = mStateStats.keyAt(i); + MultiStateStats stateStats = mStateStats.valueAt(i); + stateStats.dump(ipw, stats -> + mConfig.getProcessor().stateStatsToString(mPowerStatsDescriptor, key, + stats)); + } + ipw.decreaseIndent(); + ipw.decreaseIndent(); + } } void dumpUid(IndentingPrintWriter ipw, int uid) { @@ -340,4 +469,29 @@ class PowerComponentAggregatedPowerStats { ipw.decreaseIndent(); } } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + IndentingPrintWriter ipw = new IndentingPrintWriter(sw); + ipw.increaseIndent(); + dumpDevice(ipw); + ipw.decreaseIndent(); + + int[] uids = new int[mUidStats.size()]; + for (int i = uids.length - 1; i >= 0; i--) { + uids[i] = mUidStats.keyAt(i); + } + Arrays.sort(uids); + for (int uid : uids) { + ipw.println(UserHandle.formatUid(uid)); + ipw.increaseIndent(); + dumpUid(ipw, uid); + ipw.decreaseIndent(); + } + + ipw.flush(); + + return sw.toString(); + } } diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java index ba4c127ac3d0..6a4c1f0406a9 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java @@ -32,7 +32,7 @@ public class PowerStatsAggregator { private static final long UNINITIALIZED = -1; private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig; private final BatteryStatsHistory mHistory; - private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>(); + private final SparseArray<PowerStatsProcessor> mProcessors = new SparseArray<>(); private AggregatedPowerStats mStats; private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY; private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; @@ -43,7 +43,7 @@ public class PowerStatsAggregator { mHistory = history; for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig : aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) { - AggregatedPowerStatsProcessor processor = powerComponentsConfig.getProcessor(); + PowerStatsProcessor processor = powerComponentsConfig.getProcessor(); mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor); } } diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index c76797bad66d..5dd11db2a2fc 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -17,9 +17,12 @@ package com.android.server.power.stats; import android.annotation.Nullable; +import android.hardware.power.stats.EnergyConsumer; +import android.hardware.power.stats.EnergyConsumerResult; +import android.hardware.power.stats.EnergyConsumerType; import android.os.ConditionVariable; import android.os.Handler; -import android.os.PersistableBundle; +import android.power.PowerStatsInternal; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -30,7 +33,12 @@ import com.android.internal.os.PowerStats; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; /** @@ -43,6 +51,7 @@ import java.util.function.Consumer; public abstract class PowerStatsCollector { private static final String TAG = "PowerStatsCollector"; private static final int MILLIVOLTS_PER_VOLT = 1000; + private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000; private final Handler mHandler; protected final Clock mClock; private final long mThrottlePeriodMs; @@ -50,200 +59,6 @@ public abstract class PowerStatsCollector { private boolean mEnabled; private long mLastScheduledUpdateMs = -1; - /** - * Captures the positions and lengths of sections of the stats array, such as usage duration, - * power usage estimates etc. - */ - public static class StatsArrayLayout { - private static final String EXTRA_DEVICE_POWER_POSITION = "dp"; - private static final String EXTRA_DEVICE_DURATION_POSITION = "dd"; - private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de"; - private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec"; - private static final String EXTRA_UID_POWER_POSITION = "up"; - - protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0; - - private int mDeviceStatsArrayLength; - private int mUidStatsArrayLength; - - protected int mDeviceDurationPosition; - private int mDeviceEnergyConsumerPosition; - private int mDeviceEnergyConsumerCount; - private int mDevicePowerEstimatePosition; - private int mUidPowerEstimatePosition; - - public int getDeviceStatsArrayLength() { - return mDeviceStatsArrayLength; - } - - public int getUidStatsArrayLength() { - return mUidStatsArrayLength; - } - - protected int addDeviceSection(int length) { - int position = mDeviceStatsArrayLength; - mDeviceStatsArrayLength += length; - return position; - } - - protected int addUidSection(int length) { - int position = mUidStatsArrayLength; - mUidStatsArrayLength += length; - return position; - } - - /** - * Declare that the stats array has a section capturing usage duration - */ - public void addDeviceSectionUsageDuration() { - mDeviceDurationPosition = addDeviceSection(1); - } - - /** - * Saves the usage duration in the corresponding <code>stats</code> element. - */ - public void setUsageDuration(long[] stats, long value) { - stats[mDeviceDurationPosition] = value; - } - - /** - * Extracts the usage duration from the corresponding <code>stats</code> element. - */ - public long getUsageDuration(long[] stats) { - return stats[mDeviceDurationPosition]; - } - - /** - * Declares that the stats array has a section capturing EnergyConsumer data from - * PowerStatsService. - */ - public void addDeviceSectionEnergyConsumers(int energyConsumerCount) { - mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount); - mDeviceEnergyConsumerCount = energyConsumerCount; - } - - public int getEnergyConsumerCount() { - return mDeviceEnergyConsumerCount; - } - - /** - * Saves the accumulated energy for the specified rail the corresponding - * <code>stats</code> element. - */ - public void setConsumedEnergy(long[] stats, int index, long energy) { - stats[mDeviceEnergyConsumerPosition + index] = energy; - } - - /** - * Extracts the EnergyConsumer data from a device stats array for the specified - * EnergyConsumer. - */ - public long getConsumedEnergy(long[] stats, int index) { - return stats[mDeviceEnergyConsumerPosition + index]; - } - - /** - * Declare that the stats array has a section capturing a power estimate - */ - public void addDeviceSectionPowerEstimate() { - mDevicePowerEstimatePosition = addDeviceSection(1); - } - - /** - * Converts the supplied mAh power estimate to a long and saves it in the corresponding - * element of <code>stats</code>. - */ - public void setDevicePowerEstimate(long[] stats, double power) { - stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); - } - - /** - * Extracts the power estimate from a device stats array and converts it to mAh. - */ - public double getDevicePowerEstimate(long[] stats) { - return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; - } - - /** - * Declare that the UID stats array has a section capturing a power estimate - */ - public void addUidSectionPowerEstimate() { - mUidPowerEstimatePosition = addUidSection(1); - } - - /** - * Converts the supplied mAh power estimate to a long and saves it in the corresponding - * element of <code>stats</code>. - */ - public void setUidPowerEstimate(long[] stats, double power) { - stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); - } - - /** - * Extracts the power estimate from a UID stats array and converts it to mAh. - */ - public double getUidPowerEstimate(long[] stats) { - return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; - } - - /** - * Copies the elements of the stats array layout into <code>extras</code> - */ - public void toExtras(PersistableBundle extras) { - extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition); - extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, - mDeviceEnergyConsumerPosition); - extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, - mDeviceEnergyConsumerCount); - extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition); - extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition); - } - - /** - * Retrieves elements of the stats array layout from <code>extras</code> - */ - public void fromExtras(PersistableBundle extras) { - mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION); - mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION); - mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT); - mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION); - mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION); - } - - protected void putIntArray(PersistableBundle extras, String key, int[] array) { - if (array == null) { - return; - } - - StringBuilder sb = new StringBuilder(); - for (int value : array) { - if (!sb.isEmpty()) { - sb.append(','); - } - sb.append(value); - } - extras.putString(key, sb.toString()); - } - - protected int[] getIntArray(PersistableBundle extras, String key) { - String string = extras.getString(key); - if (string == null) { - return null; - } - String[] values = string.trim().split(","); - int[] result = new int[values.length]; - for (int i = 0; i < values.length; i++) { - try { - result[i] = Integer.parseInt(values[i]); - } catch (NumberFormatException e) { - Slog.wtf(TAG, "Invalid CSV format: " + string); - return null; - } - } - return result; - } - } - @GuardedBy("this") @SuppressWarnings("unchecked") private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList(); @@ -389,9 +204,83 @@ public abstract class PowerStatsCollector { } /** Calculate charge consumption (in microcoulombs) from a given energy and voltage */ - protected long uJtoUc(long deltaEnergyUj, int avgVoltageMv) { + protected static long uJtoUc(long deltaEnergyUj, int avgVoltageMv) { // To overflow, a 3.7V 10000mAh battery would need to completely drain 69244 times // since the last snapshot. Round off to the nearest whole long. return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv; } + + interface ConsumedEnergyRetriever { + int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType); + + @Nullable + long[] getConsumedEnergyUws(int[] energyConsumerIds); + } + + static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever { + private final PowerStatsInternal mPowerStatsInternal; + + ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal) { + mPowerStatsInternal = powerStatsInternal; + } + + @Override + public int[] getEnergyConsumerIds(int energyConsumerType) { + if (mPowerStatsInternal == null) { + return new int[0]; + } + + EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo(); + if (energyConsumerInfo == null) { + return new int[0]; + } + + List<EnergyConsumer> energyConsumers = new ArrayList<>(); + for (EnergyConsumer energyConsumer : energyConsumerInfo) { + if (energyConsumer.type == energyConsumerType) { + energyConsumers.add(energyConsumer); + } + } + if (energyConsumers.isEmpty()) { + return new int[0]; + } + + energyConsumers.sort(Comparator.comparing(c -> c.ordinal)); + + int[] ids = new int[energyConsumers.size()]; + for (int i = 0; i < ids.length; i++) { + ids[i] = energyConsumers.get(i).id; + } + return ids; + } + + @Override + public long[] getConsumedEnergyUws(int[] energyConsumerIds) { + CompletableFuture<EnergyConsumerResult[]> future = + mPowerStatsInternal.getEnergyConsumedAsync(energyConsumerIds); + EnergyConsumerResult[] results = null; + try { + results = future.get( + POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e); + } + + if (results == null) { + return null; + } + + long[] energy = new long[energyConsumerIds.length]; + for (int i = 0; i < energyConsumerIds.length; i++) { + int id = energyConsumerIds[i]; + for (EnergyConsumerResult result : results) { + if (result.id == id) { + energy[i] = result.energyUWs; + break; + } + } + } + return energy; + } + } } diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java index 4f4ddca6c3fc..f6b198a88fc2 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java @@ -139,7 +139,7 @@ public class PowerStatsExporter { return; } - PowerStatsCollector.StatsArrayLayout layout = new PowerStatsCollector.StatsArrayLayout(); + PowerStatsLayout layout = new PowerStatsLayout(); layout.fromExtras(descriptor.extras); long[] deviceStats = new long[descriptor.statsArrayLength]; @@ -164,9 +164,20 @@ public class PowerStatsExporter { deviceScope.addConsumedPower(powerComponentId, totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED); + if (layout.isUidPowerAttributionSupported()) { + populateUidBatteryConsumers(batteryUsageStatsBuilder, powerComponent, + powerComponentStats, layout); + } + } + + private static void populateUidBatteryConsumers( + BatteryUsageStats.Builder batteryUsageStatsBuilder, + AggregatedPowerStatsConfig.PowerComponent powerComponent, + PowerComponentAggregatedPowerStats powerComponentStats, + PowerStatsLayout layout) { + int powerComponentId = powerComponent.getPowerComponentId(); + PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor(); long[] uidStats = new long[descriptor.uidStatsArrayLength]; - ArrayList<Integer> uids = new ArrayList<>(); - powerComponentStats.collectUids(uids); boolean breakDownByProcState = batteryUsageStatsBuilder.isProcessStateDataNeeded() @@ -177,6 +188,8 @@ public class PowerStatsExporter { double[] powerByProcState = new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1]; double powerAllApps = 0; + ArrayList<Integer> uids = new ArrayList<>(); + powerComponentStats.collectUids(uids); for (int uid : uids) { UidBatteryConsumer.Builder builder = batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid); diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java new file mode 100644 index 000000000000..aa96409e85e9 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import android.os.PersistableBundle; +import android.util.Slog; + +import com.android.internal.os.PowerStats; + +/** + * Captures the positions and lengths of sections of the stats array, such as usage duration, + * power usage estimates etc. + */ +public class PowerStatsLayout { + private static final String TAG = "PowerStatsLayout"; + private static final String EXTRA_DEVICE_POWER_POSITION = "dp"; + private static final String EXTRA_DEVICE_DURATION_POSITION = "dd"; + private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de"; + private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec"; + private static final String EXTRA_UID_POWER_POSITION = "up"; + + protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0; + protected static final int UNSUPPORTED = -1; + + private int mDeviceStatsArrayLength; + private int mStateStatsArrayLength; + private int mUidStatsArrayLength; + + protected int mDeviceDurationPosition = UNSUPPORTED; + private int mDeviceEnergyConsumerPosition; + private int mDeviceEnergyConsumerCount; + private int mDevicePowerEstimatePosition = UNSUPPORTED; + private int mUidPowerEstimatePosition = UNSUPPORTED; + + public PowerStatsLayout() { + } + + public PowerStatsLayout(PowerStats.Descriptor descriptor) { + fromExtras(descriptor.extras); + } + + public int getDeviceStatsArrayLength() { + return mDeviceStatsArrayLength; + } + + public int getStateStatsArrayLength() { + return mStateStatsArrayLength; + } + + public int getUidStatsArrayLength() { + return mUidStatsArrayLength; + } + + protected int addDeviceSection(int length) { + int position = mDeviceStatsArrayLength; + mDeviceStatsArrayLength += length; + return position; + } + + protected int addStateSection(int length) { + int position = mStateStatsArrayLength; + mStateStatsArrayLength += length; + return position; + } + + protected int addUidSection(int length) { + int position = mUidStatsArrayLength; + mUidStatsArrayLength += length; + return position; + } + + /** + * Declare that the stats array has a section capturing usage duration + */ + public void addDeviceSectionUsageDuration() { + mDeviceDurationPosition = addDeviceSection(1); + } + + /** + * Saves the usage duration in the corresponding <code>stats</code> element. + */ + public void setUsageDuration(long[] stats, long value) { + stats[mDeviceDurationPosition] = value; + } + + /** + * Extracts the usage duration from the corresponding <code>stats</code> element. + */ + public long getUsageDuration(long[] stats) { + return stats[mDeviceDurationPosition]; + } + + /** + * Declares that the stats array has a section capturing EnergyConsumer data from + * PowerStatsService. + */ + public void addDeviceSectionEnergyConsumers(int energyConsumerCount) { + mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount); + mDeviceEnergyConsumerCount = energyConsumerCount; + } + + public int getEnergyConsumerCount() { + return mDeviceEnergyConsumerCount; + } + + /** + * Saves the accumulated energy for the specified rail the corresponding + * <code>stats</code> element. + */ + public void setConsumedEnergy(long[] stats, int index, long energy) { + stats[mDeviceEnergyConsumerPosition + index] = energy; + } + + /** + * Extracts the EnergyConsumer data from a device stats array for the specified + * EnergyConsumer. + */ + public long getConsumedEnergy(long[] stats, int index) { + return stats[mDeviceEnergyConsumerPosition + index]; + } + + /** + * Declare that the stats array has a section capturing a power estimate + */ + public void addDeviceSectionPowerEstimate() { + mDevicePowerEstimatePosition = addDeviceSection(1); + } + + /** + * Converts the supplied mAh power estimate to a long and saves it in the corresponding + * element of <code>stats</code>. + */ + public void setDevicePowerEstimate(long[] stats, double power) { + stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); + } + + /** + * Extracts the power estimate from a device stats array and converts it to mAh. + */ + public double getDevicePowerEstimate(long[] stats) { + return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; + } + + /** + * Declare that the UID stats array has a section capturing a power estimate + */ + public void addUidSectionPowerEstimate() { + mUidPowerEstimatePosition = addUidSection(1); + } + + /** + * Returns true if power for this component is attributed to UIDs (apps). + */ + public boolean isUidPowerAttributionSupported() { + return mUidPowerEstimatePosition != UNSUPPORTED; + } + + /** + * Converts the supplied mAh power estimate to a long and saves it in the corresponding + * element of <code>stats</code>. + */ + public void setUidPowerEstimate(long[] stats, double power) { + stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER); + } + + /** + * Extracts the power estimate from a UID stats array and converts it to mAh. + */ + public double getUidPowerEstimate(long[] stats) { + return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER; + } + + /** + * Copies the elements of the stats array layout into <code>extras</code> + */ + public void toExtras(PersistableBundle extras) { + extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition); + extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, + mDeviceEnergyConsumerPosition); + extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, + mDeviceEnergyConsumerCount); + extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition); + extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition); + } + + /** + * Retrieves elements of the stats array layout from <code>extras</code> + */ + public void fromExtras(PersistableBundle extras) { + mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION); + mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION); + mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT); + mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION); + mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION); + } + + protected void putIntArray(PersistableBundle extras, String key, int[] array) { + if (array == null) { + return; + } + + StringBuilder sb = new StringBuilder(); + for (int value : array) { + if (!sb.isEmpty()) { + sb.append(','); + } + sb.append(value); + } + extras.putString(key, sb.toString()); + } + + protected int[] getIntArray(PersistableBundle extras, String key) { + String string = extras.getString(key); + if (string == null) { + return null; + } + String[] values = string.trim().split(","); + int[] result = new int[values.length]; + for (int i = 0; i < values.length; i++) { + try { + result[i] = Integer.parseInt(values[i]); + } catch (NumberFormatException e) { + Slog.wtf(TAG, "Invalid CSV format: " + string); + return null; + } + } + return result; + } +} diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java index 7feb9643fb8f..0d5c5422b45c 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java @@ -27,7 +27,7 @@ import java.util.Collections; import java.util.List; /* - * The power estimation algorithm used by AggregatedPowerStatsProcessor can roughly be + * The power estimation algorithm used by PowerStatsProcessor can roughly be * described like this: * * 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using @@ -39,8 +39,8 @@ import java.util.List; * 2. For each UID, compute the proportion of the combined estimates in each state * and attribute the corresponding portion of the total power estimate in that state to the UID. */ -abstract class AggregatedPowerStatsProcessor { - private static final String TAG = "AggregatedPowerStatsProcessor"; +abstract class PowerStatsProcessor { + private static final String TAG = "PowerStatsProcessor"; private static final int INDEX_DOES_NOT_EXIST = -1; private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0; @@ -49,6 +49,8 @@ abstract class AggregatedPowerStatsProcessor { abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats); + abstract String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats); + abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats); protected static class PowerEstimationPlan { diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index 51c9d0ae5e5d..f2b4136c51ed 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -4,58 +4,6 @@ package { default_applicable_licenses: ["frameworks_base_license"], } -filegroup { - name: "power_stats_ravenwood_tests", - srcs: [ - "src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java", - "src/com/android/server/power/stats/AggregatedPowerStatsTest.java", - "src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java", - "src/com/android/server/power/stats/AudioPowerCalculatorTest.java", - "src/com/android/server/power/stats/BatteryChargeCalculatorTest.java", - "src/com/android/server/power/stats/BatteryStatsCounterTest.java", - "src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java", - "src/com/android/server/power/stats/BatteryStatsDualTimerTest.java", - "src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java", - "src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java", - "src/com/android/server/power/stats/BatteryStatsHistoryTest.java", - "src/com/android/server/power/stats/BatteryStatsImplTest.java", - "src/com/android/server/power/stats/BatteryStatsNoteTest.java", - "src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java", - "src/com/android/server/power/stats/BatteryStatsSensorTest.java", - "src/com/android/server/power/stats/BatteryStatsServTest.java", - "src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java", - "src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java", - "src/com/android/server/power/stats/BatteryStatsTimerTest.java", - "src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java", - "src/com/android/server/power/stats/BatteryUsageStatsTest.java", - "src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java", - "src/com/android/server/power/stats/CameraPowerCalculatorTest.java", - "src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java", - "src/com/android/server/power/stats/CpuPowerCalculatorTest.java", - "src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java", - "src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java", - "src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java", - "src/com/android/server/power/stats/GnssPowerCalculatorTest.java", - "src/com/android/server/power/stats/IdlePowerCalculatorTest.java", - "src/com/android/server/power/stats/LongSamplingCounterArrayTest.java", - "src/com/android/server/power/stats/LongSamplingCounterTest.java", - "src/com/android/server/power/stats/MemoryPowerCalculatorTest.java", - "src/com/android/server/power/stats/MultiStateStatsTest.java", - "src/com/android/server/power/stats/PowerStatsAggregatorTest.java", - "src/com/android/server/power/stats/PowerStatsCollectorTest.java", - "src/com/android/server/power/stats/PowerStatsExporterTest.java", - "src/com/android/server/power/stats/PowerStatsSchedulerTest.java", - "src/com/android/server/power/stats/PowerStatsStoreTest.java", - "src/com/android/server/power/stats/PowerStatsUidResolverTest.java", - "src/com/android/server/power/stats/ScreenPowerCalculatorTest.java", - "src/com/android/server/power/stats/SensorPowerCalculatorTest.java", - "src/com/android/server/power/stats/UserPowerCalculatorTest.java", - "src/com/android/server/power/stats/VideoPowerCalculatorTest.java", - "src/com/android/server/power/stats/WakelockPowerCalculatorTest.java", - "src/com/android/server/power/stats/WifiPowerCalculatorTest.java", - ], -} - android_test { name: "PowerStatsTests", @@ -79,7 +27,6 @@ android_test { "servicestests-utils", "platform-test-annotations", "flag-junit", - "ravenwood-junit", ], libs: [ @@ -112,17 +59,20 @@ android_ravenwood_test { name: "PowerStatsTestsRavenwood", static_libs: [ "services.core", - "modules-utils-binary-xml", + "coretests-aidl", + "ravenwood-junit", + "truth", "androidx.annotation_annotation", "androidx.test.rules", - "truth", + "androidx.test.uiautomator_uiautomator", + "modules-utils-binary-xml", + "flag-junit", ], srcs: [ - ":power_stats_ravenwood_tests", - - "src/com/android/server/power/stats/BatteryUsageStatsRule.java", - "src/com/android/server/power/stats/MockBatteryStatsImpl.java", - "src/com/android/server/power/stats/MockClock.java", + "src/com/android/server/power/stats/*.java", + ], + java_resources: [ + "res/xml/power_profile*.xml", ], auto_gen_config: true, } diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING index 6d3db1cb6c23..fb243616292d 100644 --- a/services/tests/powerstatstests/TEST_MAPPING +++ b/services/tests/powerstatstests/TEST_MAPPING @@ -12,7 +12,11 @@ "ravenwood-presubmit": [ { "name": "PowerStatsTestsRavenwood", - "host": true + "host": true, + "options": [ + {"include-filter": "com.android.server.power.stats"}, + {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"} + ] } ], "postsubmit": [ diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java index ca7de7c3f325..9975190424b5 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.os.BatteryConsumer; import android.os.PersistableBundle; +import android.util.SparseArray; import android.util.Xml; import androidx.test.filters.SmallTest; @@ -43,6 +44,9 @@ public class AggregatedPowerStatsTest { private static final int TEST_POWER_COMPONENT = 1077; private static final int APP_1 = 27; private static final int APP_2 = 42; + private static final int COMPONENT_STATE_0 = 0; + private static final int COMPONENT_STATE_1 = 1; + private static final int COMPONENT_STATE_2 = 2; private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig; private PowerStats.Descriptor mPowerComponentDescriptor; @@ -59,8 +63,10 @@ public class AggregatedPowerStatsTest { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE); - mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2, 3, - PersistableBundle.forPair("speed", "fast")); + SparseArray<String> stateLabels = new SparseArray<>(); + stateLabels.put(COMPONENT_STATE_1, "one"); + mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2, + stateLabels, 1, 3, PersistableBundle.forPair("speed", "fast")); } @Test @@ -107,6 +113,9 @@ public class AggregatedPowerStatsTest { ps.stats[0] = 100; ps.stats[1] = 987; + ps.stateStats.put(COMPONENT_STATE_0, new long[]{1111}); + ps.stateStats.put(COMPONENT_STATE_1, new long[]{5000}); + ps.uidStats.put(APP_1, new long[]{389, 0, 739}); ps.uidStats.put(APP_2, new long[]{278, 314, 628}); @@ -120,11 +129,14 @@ public class AggregatedPowerStatsTest { ps.stats[0] = 444; ps.stats[1] = 0; + ps.stateStats.clear(); + ps.stateStats.put(COMPONENT_STATE_1, new long[]{1000}); + ps.stateStats.put(COMPONENT_STATE_2, new long[]{9000}); + ps.uidStats.put(APP_1, new long[]{0, 0, 400}); ps.uidStats.put(APP_2, new long[]{100, 200, 300}); stats.addPowerStats(ps, 5000); - return stats; } @@ -147,6 +159,31 @@ public class AggregatedPowerStatsTest { AggregatedPowerStatsConfig.SCREEN_STATE_OTHER)) .isEqualTo(new long[]{222, 0}); + assertThat(getStateStats(stats, COMPONENT_STATE_0, + AggregatedPowerStatsConfig.POWER_STATE_BATTERY, + AggregatedPowerStatsConfig.SCREEN_STATE_ON)) + .isEqualTo(new long[]{1111}); + + assertThat(getStateStats(stats, COMPONENT_STATE_1, + AggregatedPowerStatsConfig.POWER_STATE_BATTERY, + AggregatedPowerStatsConfig.SCREEN_STATE_ON)) + .isEqualTo(new long[]{5500}); + + assertThat(getStateStats(stats, COMPONENT_STATE_1, + AggregatedPowerStatsConfig.POWER_STATE_BATTERY, + AggregatedPowerStatsConfig.SCREEN_STATE_OTHER)) + .isEqualTo(new long[]{500}); + + assertThat(getStateStats(stats, COMPONENT_STATE_2, + AggregatedPowerStatsConfig.POWER_STATE_BATTERY, + AggregatedPowerStatsConfig.SCREEN_STATE_ON)) + .isEqualTo(new long[]{4500}); + + assertThat(getStateStats(stats, COMPONENT_STATE_2, + AggregatedPowerStatsConfig.POWER_STATE_BATTERY, + AggregatedPowerStatsConfig.SCREEN_STATE_OTHER)) + .isEqualTo(new long[]{4500}); + assertThat(getUidDeviceStats(stats, APP_1, AggregatedPowerStatsConfig.POWER_STATE_BATTERY, @@ -191,14 +228,26 @@ public class AggregatedPowerStatsTest { } private static long[] getDeviceStats(AggregatedPowerStats stats, int... states) { - long[] out = new long[states.length]; - stats.getPowerComponentStats(TEST_POWER_COMPONENT).getDeviceStats(out, states); + PowerComponentAggregatedPowerStats powerComponentStats = + stats.getPowerComponentStats(TEST_POWER_COMPONENT); + long[] out = new long[powerComponentStats.getPowerStatsDescriptor().statsArrayLength]; + powerComponentStats.getDeviceStats(out, states); + return out; + } + + private static long[] getStateStats(AggregatedPowerStats stats, int key, int... states) { + PowerComponentAggregatedPowerStats powerComponentStats = + stats.getPowerComponentStats(TEST_POWER_COMPONENT); + long[] out = new long[powerComponentStats.getPowerStatsDescriptor().stateStatsArrayLength]; + powerComponentStats.getStateStats(out, key, states); return out; } private static long[] getUidDeviceStats(AggregatedPowerStats stats, int uid, int... states) { - long[] out = new long[states.length]; - stats.getPowerComponentStats(TEST_POWER_COMPONENT).getUidStats(out, uid, states); + PowerComponentAggregatedPowerStats powerComponentStats = + stats.getPowerComponentStats(TEST_POWER_COMPONENT); + long[] out = new long[powerComponentStats.getPowerStatsDescriptor().uidStatsArrayLength]; + powerComponentStats.getUidStats(out, uid, states); return out; } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java index 3ab1c2eab6ca..9b45ca79fdab 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java @@ -16,9 +16,11 @@ package com.android.server.power.stats; - import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + +import android.content.Context; import android.os.BatteryManager; import android.os.BatteryUsageStats; import android.platform.test.ravenwood.RavenwoodRule; @@ -28,6 +30,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.PowerProfile; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,6 +49,11 @@ public class BatteryChargeCalculatorTest { public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0); + @Before + public void setup() { + mStatsRule.getBatteryStats().onSystemReady(mock(Context.class)); + } + @Test public void testDischargeTotals() { // Nominal battery capacity should be ignored diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java index 997b7712a9dd..0a9c8c00444a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java @@ -36,6 +36,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.platform.test.ravenwood.RavenwoodRule; import android.power.PowerStatsInternal; import android.util.IntArray; import android.util.SparseArray; @@ -47,6 +48,7 @@ import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.PowerProfile; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import java.util.Arrays; @@ -59,7 +61,10 @@ import java.util.concurrent.CompletableFuture; * atest FrameworksServicesTests:BatteryExternalStatsWorkerTest */ @SuppressWarnings("GuardedBy") +@android.platform.test.annotations.DisabledOnRavenwood public class BatteryExternalStatsWorkerTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private BatteryExternalStatsWorker mBatteryExternalStatsWorker; private TestBatteryStatsImpl mBatteryStatsImpl; private TestPowerStatsInternal mPowerStatsInternal; @@ -215,7 +220,8 @@ public class BatteryExternalStatsWorkerTest { public class TestBatteryStatsImpl extends BatteryStatsImpl { public TestBatteryStatsImpl(Context context) { - super(Clock.SYSTEM_CLOCK, null, null, null, null, null, null); + super(new BatteryStatsConfig.Builder().build(), Clock.SYSTEM_CLOCK, null, null, null, + null, null, null); mPowerProfile = new PowerProfile(context, true /* forTest */); SparseArray<int[]> cpusByPolicy = new SparseArray<>(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java index 4d3fcb611f24..ad05b5124955 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java @@ -18,25 +18,37 @@ package com.android.server.power.stats; import static android.os.BatteryStats.STATS_SINCE_CHARGED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.app.ActivityManager; import android.os.BatteryStats; import android.os.WorkSource; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.ArrayMap; import android.view.Display; import androidx.test.filters.SmallTest; -import junit.framework.TestCase; +import org.junit.Rule; +import org.junit.Test; /** * Test BatteryStatsImpl onBatteryBackgroundTimeBase TimeBase. */ -public class BatteryStatsBackgroundStatsTest extends TestCase { +public class BatteryStatsBackgroundStatsTest { + + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); private static final int UID = 10500; /** Test that BatteryStatsImpl.Uid.mOnBatteryBackgroundTimeBase works correctly. */ @SmallTest + @Test public void testBgTimeBase() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); @@ -105,6 +117,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase { /** Test that BatteryStatsImpl.Uid.mOnBatteryScreenOffBackgroundTimeBase works correctly. */ @SmallTest + @Test public void testScreenOffBgTimeBase() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); @@ -153,6 +166,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase { } @SmallTest + @Test public void testWifiScan() throws Exception { final MockClock clocks = new MockClock(); MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); @@ -195,11 +209,13 @@ public class BatteryStatsBackgroundStatsTest extends TestCase { } @SmallTest + @Test public void testAppBluetoothScan() throws Exception { doTestAppBluetoothScanInternal(new WorkSource(UID)); } @SmallTest + @Test public void testAppBluetoothScan_workChain() throws Exception { WorkSource ws = new WorkSource(); ws.createWorkChain().addNode(UID, "foo"); @@ -275,6 +291,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase { } @SmallTest + @Test public void testJob() throws Exception { final MockClock clocks = new MockClock(); MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); @@ -336,6 +353,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase { } @SmallTest + @Test public void testSyncs() throws Exception { final MockClock clocks = new MockClock(); MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java index 3f101a96d36c..4dfc3fcec916 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java @@ -16,20 +16,20 @@ package com.android.server.power.stats; +import static org.junit.Assert.assertEquals; + import android.os.Binder; import android.os.Process; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.ArraySet; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderTransactionNameResolver; -import junit.framework.TestCase; - +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Collection; @@ -37,9 +37,14 @@ import java.util.Collection; /** * Test cases for android.os.BatteryStats, system server Binder call stats. */ -@RunWith(AndroidJUnit4.class) @SmallTest -public class BatteryStatsBinderCallStatsTest extends TestCase { +@android.platform.test.annotations.DisabledOnRavenwood(blockedBy = BinderCallsStats.class) +public class BatteryStatsBinderCallStatsTest { + + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); private static final int TRANSACTION_CODE1 = 100; private static final int TRANSACTION_CODE2 = 101; @@ -89,7 +94,6 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { assertEquals(500, value.recordedCpuTimeMicros); } - @Test public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java index 6e62147ac6c1..eff1b7b852d9 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java @@ -118,7 +118,8 @@ public class BatteryStatsCpuTimesTest { mClocks = new MockClock(); Handler handler = new Handler(Looper.getMainLooper()); mPowerStatsUidResolver = new PowerStatsUidResolver(); - mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks, null, handler, mPowerStatsUidResolver) + mBatteryStatsImpl = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG, + mClocks, null, handler, mPowerStatsUidResolver) .setTestCpuScalingPolicies() .setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader) .setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader) diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index c58c92b47dd3..e40a3e314e58 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -403,7 +403,7 @@ public class BatteryStatsHistoryTest { @Test public void recordPowerStats() { - PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, 2, + PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, null, 0, 2, new PersistableBundle()); PowerStats powerStats = new PowerStats(descriptor); powerStats.durationMs = 100; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java index 7ae111711b6b..9a64ce19254b 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java @@ -25,15 +25,21 @@ import android.os.BatteryStatsManager; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.UidBatteryConsumer; +import android.platform.test.ravenwood.RavenwoodRule; +import org.junit.Rule; import org.junit.Test; /** * Test BatteryStatsManager and CellularBatteryStats to ensure that valid data is being reported * and that invalid data is not reported. */ +@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test") public class BatteryStatsManagerTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Test public void testBatteryUsageStatsDataConsistency() { BatteryStatsManager bsm = getContext().getSystemService(BatteryStatsManager.class); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java index 07cefa9ae878..afbe9159b66a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java @@ -170,8 +170,8 @@ public class BatteryStatsNoteTest { public void testNoteStartWakeLocked_isolatedUid() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms PowerStatsUidResolver uidResolver = new PowerStatsUidResolver(); - MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null, - new Handler(Looper.getMainLooper()), uidResolver); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG, + clocks, null, new Handler(Looper.getMainLooper()), uidResolver); int pid = 10; String name = "name"; @@ -212,8 +212,8 @@ public class BatteryStatsNoteTest { public void testNoteStartWakeLocked_isolatedUidRace() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms PowerStatsUidResolver uidResolver = new PowerStatsUidResolver(); - MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null, - new Handler(Looper.getMainLooper()), uidResolver); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG, + clocks, null, new Handler(Looper.getMainLooper()), uidResolver); int pid = 10; String name = "name"; @@ -256,8 +256,8 @@ public class BatteryStatsNoteTest { public void testNoteLongPartialWakelockStart_isolatedUid() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms PowerStatsUidResolver uidResolver = new PowerStatsUidResolver(); - MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null, - new Handler(Looper.getMainLooper()), uidResolver); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG, + clocks, null, new Handler(Looper.getMainLooper()), uidResolver); bi.setRecordAllHistoryLocked(true); bi.forceRecordAllHistory(); @@ -311,8 +311,8 @@ public class BatteryStatsNoteTest { public void testNoteLongPartialWakelockStart_isolatedUidRace() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms PowerStatsUidResolver uidResolver = new PowerStatsUidResolver(); - MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null, - new Handler(Looper.getMainLooper()), uidResolver); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG, + clocks, null, new Handler(Looper.getMainLooper()), uidResolver); bi.setRecordAllHistoryLocked(true); bi.forceRecordAllHistory(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java index a0fb631812f4..d29bf1abd7a3 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java @@ -18,21 +18,32 @@ package com.android.server.power.stats; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + import android.content.Context; import android.os.BatteryManager; +import android.platform.test.ravenwood.RavenwoodRule; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.IOException; +import java.nio.file.Files; + @SmallTest @RunWith(AndroidJUnit4.class) public class BatteryStatsResetTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + private static final int BATTERY_NOMINAL_VOLTAGE_MV = 3700; private static final int BATTERY_CAPACITY_UAH = 4_000_000; private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100; @@ -79,13 +90,11 @@ public class BatteryStatsResetTest { private long mBatteryChargeTimeToFullSeconds; @Before - public void setUp() { - final Context context = InstrumentationRegistry.getContext(); - + public void setUp() throws IOException { mMockClock = new MockClock(); - mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, context.getFilesDir()); - mBatteryStatsImpl.onSystemReady(); - + mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, + Files.createTempDirectory("BatteryStatsResetTest").toFile()); + mBatteryStatsImpl.onSystemReady(mock(Context.class)); // Set up the battery state. Start off with a fully charged plugged in battery. mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java index c4561b16d73c..3931201aaf03 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java @@ -28,6 +28,7 @@ import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.ArraySet; import androidx.test.InstrumentationRegistry; @@ -38,6 +39,7 @@ import androidx.test.uiautomator.UiDevice; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,8 +48,10 @@ import java.util.concurrent.TimeUnit; @LargeTest @RunWith(AndroidJUnit4.class) -@android.platform.test.annotations.IgnoreUnderRavenwood +@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test") public class BatteryStatsUserLifecycleTests { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private static final long POLL_INTERVAL_MS = 500; private static final long USER_REMOVE_TIMEOUT_MS = 5_000; @@ -65,6 +69,10 @@ public class BatteryStatsUserLifecycleTests { @BeforeClass public static void setUpOnce() { + if (RavenwoodRule.isOnRavenwood()) { + return; + } + assumeTrue(UserManager.getMaxSupportedUsers() > 1); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index 296ad0e939de..2d7cb2245c0a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -24,7 +24,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.annotation.XmlRes; +import android.content.Context; +import android.content.res.Resources; import android.net.NetworkStats; import android.os.BatteryConsumer; import android.os.BatteryStats; @@ -35,9 +36,9 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.UidBatteryConsumer; import android.os.UserBatteryConsumer; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.SparseArray; - -import androidx.test.InstrumentationRegistry; +import android.util.Xml; import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.PowerProfile; @@ -47,6 +48,7 @@ import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.mockito.stubbing.Answer; +import org.xmlpull.v1.XmlPullParser; import java.io.File; import java.io.IOException; @@ -81,6 +83,7 @@ public class BatteryUsageStatsRule implements TestRule { private boolean[] mSupportedStandardBuckets; private String[] mCustomPowerComponentNames; private Throwable mThrowable; + private final BatteryStatsImpl.BatteryStatsConfig.Builder mBatteryStatsConfigBuilder; public BatteryUsageStatsRule() { this(0); @@ -94,6 +97,11 @@ public class BatteryUsageStatsRule implements TestRule { mCpusByPolicy.put(4, new int[]{4, 5, 6, 7}); mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000}); mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000}); + mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder() + .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU, + 10000) + .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, + 10000); } private void initBatteryStats() { @@ -107,7 +115,8 @@ public class BatteryUsageStatsRule implements TestRule { } clearDirectory(); } - mBatteryStats = new MockBatteryStatsImpl(mMockClock, mHistoryDir, mHandler); + mBatteryStats = new MockBatteryStatsImpl(mBatteryStatsConfigBuilder.build(), + mMockClock, mHistoryDir, mHandler, new PowerStatsUidResolver()); mBatteryStats.setPowerProfile(mPowerProfile); mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy)); synchronized (mBatteryStats) { @@ -116,8 +125,6 @@ public class BatteryUsageStatsRule implements TestRule { } mBatteryStats.informThatAllExternalStatsAreFlushed(); - mBatteryStats.onSystemReady(); - if (mDisplayCount != -1) { mBatteryStats.setDisplayCountLocked(mDisplayCount); } @@ -148,11 +155,27 @@ public class BatteryUsageStatsRule implements TestRule { return this; } - public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) { - mPowerProfile.forceInitForTesting(InstrumentationRegistry.getContext(), xmlId); + public BatteryUsageStatsRule setTestPowerProfile(String resourceName) { + mPowerProfile.initForTesting(resolveParser(resourceName)); return this; } + public static XmlPullParser resolveParser(String resourceName) { + if (RavenwoodRule.isOnRavenwood()) { + try { + return Xml.resolvePullParser(BatteryUsageStatsRule.class.getClassLoader() + .getResourceAsStream("res/xml/" + resourceName + ".xml")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + Context context = androidx.test.InstrumentationRegistry.getContext(); + Resources resources = context.getResources(); + int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName()); + return resources.getXml(resId); + } + } + public BatteryUsageStatsRule setCpuScalingPolicy(int policy, int[] relatedCpus, int[] frequencies) { if (mDefaultCpuScalingPolicy) { @@ -265,6 +288,12 @@ public class BatteryUsageStatsRule implements TestRule { return this; } + public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent, + long throttleMs) { + mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(powerComponent, throttleMs); + return this; + } + public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) { mScreenOn = screenOn; return this; @@ -291,23 +320,21 @@ public class BatteryUsageStatsRule implements TestRule { } private void before() { - initBatteryStats(); HandlerThread bgThread = new HandlerThread("bg thread"); bgThread.setUncaughtExceptionHandler((thread, throwable)-> { mThrowable = throwable; }); bgThread.start(); mHandler = new Handler(bgThread.getLooper()); - mBatteryStats.setHandler(mHandler); + + initBatteryStats(); mBatteryStats.setOnBatteryInternal(true); mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0); } private void after() throws Throwable { - if (mHandler != null) { - waitForBackgroundThread(); - } + waitForBackgroundThread(); } public void waitForBackgroundThread() throws Throwable { @@ -316,11 +343,12 @@ public class BatteryUsageStatsRule implements TestRule { } ConditionVariable done = new ConditionVariable(); - mHandler.post(done::open); - assertThat(done.block(10000)).isTrue(); - - if (mThrowable != null) { - throw mThrowable; + if (mHandler.post(done::open)) { + boolean success = done.block(5000); + if (mThrowable != null) { + throw mThrowable; + } + assertThat(success).isTrue(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java index 29e2f5ee163a..e4ab227a4840 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java @@ -46,6 +46,7 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.Process; import android.os.SystemClock; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.Settings; import android.util.ArrayMap; import android.util.DebugUtils; @@ -74,9 +75,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; @LargeTest -@RunWith(AndroidJUnit4.class) -@android.platform.test.annotations.IgnoreUnderRavenwood +@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test") public class BstatsCpuTimesValidationTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private static final String TAG = BstatsCpuTimesValidationTest.class.getSimpleName(); private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp"; @@ -112,10 +115,15 @@ public class BstatsCpuTimesValidationTest { private static boolean sCpuFreqTimesAvailable; private static boolean sPerProcStateTimesAvailable; - @Rule public TestName testName = new TestName(); + @Rule(order = 1) + public TestName testName = new TestName(); @BeforeClass public static void setupOnce() throws Exception { + if (RavenwoodRule.isOnRavenwood()) { + return; + } + sContext = InstrumentationRegistry.getContext(); sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, @@ -127,6 +135,10 @@ public class BstatsCpuTimesValidationTest { @AfterClass public static void tearDownOnce() throws Exception { + if (RavenwoodRule.isOnRavenwood()) { + return; + } + executeCmd("cmd deviceidle whitelist -" + TEST_PKG); if (sBatteryStatsConstsUpdated) { Settings.Global.putString(sContext.getContentResolver(), diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java index 64d5414bf66c..ad2939284471 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java @@ -23,65 +23,127 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; -import android.content.Context; -import android.hardware.power.stats.EnergyConsumer; -import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyConsumerType; import android.os.BatteryConsumer; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; -import android.power.PowerStatsInternal; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.SparseArray; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.frameworks.powerstatstests.R; +import com.android.internal.os.Clock; import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParserException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; +import java.io.IOException; +import java.util.function.IntSupplier; @RunWith(AndroidJUnit4.class) @SmallTest public class CpuPowerStatsCollectorTest { + + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + private static final int ISOLATED_UID = 99123; private static final int UID_1 = 42; private static final int UID_2 = 99; - private Context mContext; private final MockClock mMockClock = new MockClock(); private final HandlerThread mHandlerThread = new HandlerThread("test"); private Handler mHandler; private PowerStats mCollectedStats; - private PowerProfile mPowerProfile; + private PowerProfile mPowerProfile = new PowerProfile(); @Mock private PowerStatsUidResolver mUidResolver; @Mock private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader; @Mock - private PowerStatsInternal mPowerStatsInternal; + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; private CpuScalingPolicies mCpuScalingPolicies; + private class TestInjector implements CpuPowerStatsCollector.Injector { + private final int mDefaultCpuPowerBrackets; + private final int mDefaultCpuPowerBracketsPerEnergyConsumer; + + TestInjector(int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) { + mDefaultCpuPowerBrackets = defaultCpuPowerBrackets; + mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer; + } + + @Override + public Handler getHandler() { + return mHandler; + } + + @Override + public Clock getClock() { + return mMockClock; + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mUidResolver; + } + + @Override + public CpuScalingPolicies getCpuScalingPolicies() { + return mCpuScalingPolicies; + } + + @Override + public PowerProfile getPowerProfile() { + return mPowerProfile; + } + + @Override + public CpuPowerStatsCollector.KernelCpuStatsReader getKernelCpuStatsReader() { + return mMockKernelCpuStatsReader; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> 3500; + } + + @Override + public int getDefaultCpuPowerBrackets() { + return mDefaultCpuPowerBrackets; + } + + @Override + public int getDefaultCpuPowerBracketsPerEnergyConsumer() { + return mDefaultCpuPowerBracketsPerEnergyConsumer; + } + }; + @Before - public void setup() { + public void setup() throws XmlPullParserException, IOException { MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getContext(); - mHandlerThread.start(); mHandler = mHandlerThread.getThreadHandler(); - when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true); + when(mMockKernelCpuStatsReader.isSupportedFeature()).thenReturn(true); when(mUidResolver.mapUid(anyInt())).thenAnswer(invocation -> { int uid = invocation.getArgument(0); if (uid == ISOLATED_UID) { @@ -90,12 +152,13 @@ public class CpuPowerStatsCollectorTest { return uid; } }); + when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt())).thenReturn(new int[0]); } @Test public void powerBrackets_specifiedInPowerProfile() { - mPowerProfile = new PowerProfile(mContext); - mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets); + mPowerProfile.initForTesting( + BatteryUsageStatsRule.resolveParser("power_profile_test_power_brackets")); mCpuScalingPolicies = new CpuScalingPolicies( new SparseArray<>() {{ put(0, new int[]{0}); @@ -114,8 +177,7 @@ public class CpuPowerStatsCollectorTest { @Test public void powerBrackets_default_noEnergyConsumers() { - mPowerProfile = new PowerProfile(mContext); - mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test")); mockCpuScalingPolicies(2); CpuPowerStatsCollector collector = createCollector(3, 0); @@ -134,8 +196,7 @@ public class CpuPowerStatsCollectorTest { @Test public void powerBrackets_moreBracketsThanStates() { - mPowerProfile = new PowerProfile(mContext); - mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test")); mockCpuScalingPolicies(2); CpuPowerStatsCollector collector = createCollector(8, 0); @@ -146,8 +207,7 @@ public class CpuPowerStatsCollectorTest { @Test public void powerBrackets_energyConsumers() throws Exception { - mPowerProfile = new PowerProfile(mContext); - mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test")); mockCpuScalingPolicies(2); mockEnergyConsumers(); @@ -159,8 +219,7 @@ public class CpuPowerStatsCollectorTest { @Test public void powerStatsDescriptor() throws Exception { - mPowerProfile = new PowerProfile(mContext); - mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test")); mockCpuScalingPolicies(2); mockEnergyConsumers(); @@ -170,8 +229,8 @@ public class CpuPowerStatsCollectorTest { assertThat(descriptor.name).isEqualTo("cpu"); assertThat(descriptor.statsArrayLength).isEqualTo(13); assertThat(descriptor.uidStatsArrayLength).isEqualTo(5); - CpuPowerStatsCollector.CpuStatsArrayLayout layout = - new CpuPowerStatsCollector.CpuStatsArrayLayout(); + CpuPowerStatsLayout layout = + new CpuPowerStatsLayout(); layout.fromExtras(descriptor.extras); long[] deviceStats = new long[descriptor.statsArrayLength]; @@ -209,8 +268,8 @@ public class CpuPowerStatsCollectorTest { mockEnergyConsumers(); CpuPowerStatsCollector collector = createCollector(8, 0); - CpuPowerStatsCollector.CpuStatsArrayLayout layout = - new CpuPowerStatsCollector.CpuStatsArrayLayout(); + CpuPowerStatsLayout layout = + new CpuPowerStatsLayout(); layout.fromExtras(collector.getPowerStatsDescriptor().extras); mockKernelCpuStats(new long[]{1111, 2222, 3333}, @@ -296,10 +355,9 @@ public class CpuPowerStatsCollectorTest { private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) { - CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies, - mPowerProfile, mHandler, mMockKernelCpuStatsReader, mUidResolver, - () -> mPowerStatsInternal, () -> 3500, 60_000, mMockClock, - defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer); + CpuPowerStatsCollector collector = new CpuPowerStatsCollector( + new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer), + 0); collector.addConsumer(stats -> mCollectedStats = stats); collector.setEnabled(true); return collector; @@ -307,7 +365,7 @@ public class CpuPowerStatsCollectorTest { private void mockKernelCpuStats(long[] deviceStats, SparseArray<long[]> uidToCpuStats, long expectedLastUpdateTimestampMs, long newLastUpdateTimestampMs) { - when(mMockKernelCpuStatsReader.nativeReadCpuStats( + when(mMockKernelCpuStatsReader.readCpuStats( any(CpuPowerStatsCollector.KernelCpuStatsCallback.class), any(int[].class), anyLong(), any(long[].class), any(long[].class))) .thenAnswer(invocation -> { @@ -335,63 +393,18 @@ public class CpuPowerStatsCollectorTest { }); } - @SuppressWarnings("unchecked") - private void mockEnergyConsumers() throws Exception { - when(mPowerStatsInternal.getEnergyConsumerInfo()) - .thenReturn(new EnergyConsumer[]{ - new EnergyConsumer() {{ - id = 1; - type = EnergyConsumerType.CPU_CLUSTER; - ordinal = 0; - name = "CPU0"; - }}, - new EnergyConsumer() {{ - id = 2; - type = EnergyConsumerType.CPU_CLUSTER; - ordinal = 1; - name = "CPU4"; - }}, - new EnergyConsumer() {{ - id = 3; - type = EnergyConsumerType.BLUETOOTH; - name = "BT"; - }}, - }); - - CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class); - when(future1.get(anyLong(), any(TimeUnit.class))) - .thenReturn(new EnergyConsumerResult[]{ - new EnergyConsumerResult() {{ - id = 1; - energyUWs = 1000; - }}, - new EnergyConsumerResult() {{ - id = 2; - energyUWs = 2000; - }} - }); - - CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class); - when(future2.get(anyLong(), any(TimeUnit.class))) - .thenReturn(new EnergyConsumerResult[]{ - new EnergyConsumerResult() {{ - id = 1; - energyUWs = 1500; - }}, - new EnergyConsumerResult() {{ - id = 2; - energyUWs = 2700; - }} - }); - - when(mPowerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2}))) - .thenReturn(future1) - .thenReturn(future2); + private void mockEnergyConsumers() { + reset(mConsumedEnergyRetriever); + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER)) + .thenReturn(new int[]{1, 2}); + when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{1, 2}))) + .thenReturn(new long[]{1000, 2000}) + .thenReturn(new long[]{1500, 2700}); } private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) { - CpuPowerStatsCollector.CpuStatsArrayLayout layout = - new CpuPowerStatsCollector.CpuStatsArrayLayout(); + CpuPowerStatsLayout layout = + new CpuPowerStatsLayout(); layout.fromExtras(collector.getPowerStatsDescriptor().extras); return layout.getScalingStepToPowerBracketMap(); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java index cbce7e804de5..70c40f5052f0 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java @@ -28,6 +28,8 @@ import android.os.IBinder; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.RavenwoodFlagsValueProvider; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.DeviceConfig; import androidx.test.InstrumentationRegistry; @@ -52,11 +54,15 @@ import java.util.regex.Pattern; @RunWith(AndroidJUnit4.class) @LargeTest -@android.platform.test.annotations.IgnoreUnderRavenwood +@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test") public class CpuPowerStatsCollectorValidationTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = - DeviceFlagsValueProvider.createCheckFlagsRule(); + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Rule(order = 1) + public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood() + ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule() + : DeviceFlagsValueProvider.createCheckFlagsRule(); private static final int WORK_DURATION_MS = 2000; private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp"; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java index 5c0e26887505..6b5da81954d0 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java @@ -30,6 +30,7 @@ import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SC import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; import android.os.BatteryConsumer; import android.os.PersistableBundle; @@ -55,7 +56,7 @@ import java.util.Map; @RunWith(AndroidJUnit4.class) @SmallTest -public class CpuAggregatedPowerStatsProcessorTest { +public class CpuPowerStatsProcessorTest { @Rule(order = 0) public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() .setProvideMainThread(true) @@ -77,7 +78,7 @@ public class CpuAggregatedPowerStatsProcessorTest { .setCpuPowerBracket(2, 0, 2); private AggregatedPowerStatsConfig.PowerComponent mConfig; - private CpuAggregatedPowerStatsProcessor mProcessor; + private CpuPowerStatsProcessor mProcessor; private MockPowerComponentAggregatedPowerStats mStats; @Before @@ -86,7 +87,7 @@ public class CpuAggregatedPowerStatsProcessorTest { .trackDeviceStates(STATE_POWER, STATE_SCREEN) .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE); - mProcessor = new CpuAggregatedPowerStatsProcessor( + mProcessor = new CpuPowerStatsProcessor( mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies()); } @@ -197,7 +198,7 @@ public class CpuAggregatedPowerStatsProcessorTest { private static class MockPowerComponentAggregatedPowerStats extends PowerComponentAggregatedPowerStats { - private final CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout; + private final CpuPowerStatsLayout mStatsLayout; private final PowerStats.Descriptor mDescriptor; private HashMap<String, long[]> mDeviceStats = new HashMap<>(); private HashMap<String, long[]> mUidStats = new HashMap<>(); @@ -207,8 +208,8 @@ public class CpuAggregatedPowerStatsProcessorTest { MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config, boolean useEnergyConsumers) { - super(config); - mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout(); + super(new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config); + mStatsLayout = new CpuPowerStatsLayout(); mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3); mStatsLayout.addDeviceSectionCpuTimeByCluster(2); mStatsLayout.addDeviceSectionUsageDuration(); @@ -222,8 +223,8 @@ public class CpuAggregatedPowerStatsProcessorTest { PersistableBundle extras = new PersistableBundle(); mStatsLayout.toExtras(extras); mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, - mStatsLayout.getDeviceStatsArrayLength(), mStatsLayout.getUidStatsArrayLength(), - extras); + mStatsLayout.getDeviceStatsArrayLength(), null, 0, + mStatsLayout.getUidStatsArrayLength(), extras); } @Override diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java index e02386656cb5..f035465dd1df 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java @@ -16,16 +16,26 @@ package com.android.server.power.stats; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.ravenwood.RavenwoodRule; import android.system.suspend.internal.WakeLockInfo; import androidx.test.filters.SmallTest; -import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; import java.nio.charset.Charset; -@android.platform.test.annotations.IgnoreUnderRavenwood -public class KernelWakelockReaderTest extends TestCase { +@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency") +public class KernelWakelockReaderTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + /** * Helper class that builds the mock Kernel module file /d/wakeup_sources. */ @@ -105,14 +115,14 @@ public class KernelWakelockReaderTest extends TestCase { private KernelWakelockReader mReader; - @Override + @Before public void setUp() throws Exception { - super.setUp(); mReader = new KernelWakelockReader(); } // ------------------------- Legacy Wakelock Stats Test ------------------------ @SmallTest + @Test public void testParseEmptyFile() throws Exception { KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true, new KernelWakelockStats()); @@ -121,6 +131,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testOnlyHeader() throws Exception { byte[] buffer = new ProcFileBuilder().getBytes(); @@ -131,6 +142,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testOneWakelock() throws Exception { byte[] buffer = new ProcFileBuilder() .addLine("Wakelock", 34, 123, 456) // Milliseconds @@ -150,6 +162,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testTwoWakelocks() throws Exception { byte[] buffer = new ProcFileBuilder() .addLine("Wakelock", 1, 10) @@ -166,6 +179,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testDuplicateWakelocksAccumulate() throws Exception { byte[] buffer = new ProcFileBuilder() .addLine("Wakelock", 1, 10) // Milliseconds @@ -184,6 +198,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testWakelocksBecomeStale() throws Exception { KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -209,6 +224,7 @@ public class KernelWakelockReaderTest extends TestCase { // -------------------- SystemSuspend Wakelock Stats Test ------------------- @SmallTest + @Test public void testEmptyWakeLockInfoList() { KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0], new KernelWakelockStats()); @@ -217,6 +233,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testOneWakeLockInfo() { WakeLockInfo[] wlStats = new WakeLockInfo[1]; wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000, 500); // Milliseconds @@ -235,6 +252,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testTwoWakeLockInfos() { WakeLockInfo[] wlStats = new WakeLockInfo[2]; wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds @@ -258,6 +276,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testWakeLockInfosBecomeStale() { WakeLockInfo[] wlStats = new WakeLockInfo[1]; wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds @@ -288,6 +307,7 @@ public class KernelWakelockReaderTest extends TestCase { // -------------------- Aggregate Wakelock Stats Tests -------------------- @SmallTest + @Test public void testAggregateStatsEmpty() throws Exception { KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -300,6 +320,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testAggregateStatsNoNativeWakelocks() throws Exception { KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -320,6 +341,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testAggregateStatsNoKernelWakelocks() throws Exception { KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -339,6 +361,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception { KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -364,6 +387,7 @@ public class KernelWakelockReaderTest extends TestCase { } @SmallTest + @Test public void testAggregateStatsUpdate() throws Exception { KernelWakelockStats staleStats = new KernelWakelockStats(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java index 888a1688c2a1..9b810bc01b4d 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java @@ -26,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.annotation.Nullable; import android.app.usage.NetworkStatsManager; import android.net.NetworkCapabilities; import android.net.NetworkStats; @@ -34,6 +35,7 @@ import android.os.BatteryStats; import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.UidBatteryConsumer; +import android.platform.test.ravenwood.RavenwoodRule; import android.telephony.AccessNetworkConstants; import android.telephony.ActivityStatsTechSpecificInfo; import android.telephony.CellSignalStrength; @@ -46,8 +48,6 @@ import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.frameworks.powerstatstests.R; - import com.google.common.collect.Range; import org.junit.Rule; @@ -56,23 +56,29 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import java.util.ArrayList; +import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest @SuppressWarnings("GuardedBy") public class MobileRadioPowerCalculatorTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + private static final double PRECISION = 0.00001; private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101; @Mock NetworkStatsManager mNetworkStatsManager; - @Rule + @Rule(order = 1) public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); @Test public void testCounterBasedModel() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator) + mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator") .initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); @@ -126,10 +132,10 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); // Note application network activity - NetworkStats networkStats = new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100)) - .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0, + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100), + mockNetworkStatsEntry("cellular", APP_UID2, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111)); mStatsRule.setNetworkStats(networkStats); @@ -192,7 +198,7 @@ public class MobileRadioPowerCalculatorTest { @Test public void testCounterBasedModel_multipleDefinedRat() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive) + mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator_multiactive") .initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); @@ -246,10 +252,10 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); // Note application network activity - NetworkStats networkStats = new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100)) - .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0, + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100), + mockNetworkStatsEntry("cellular", APP_UID2, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111)); mStatsRule.setNetworkStats(networkStats); @@ -349,7 +355,7 @@ public class MobileRadioPowerCalculatorTest { @Test public void testCounterBasedModel_legacyPowerProfile() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem") .initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); @@ -403,10 +409,10 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); // Note application network activity - NetworkStats networkStats = new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100)) - .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0, + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100), + mockNetworkStatsEntry("cellular", APP_UID2, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111)); mStatsRule.setNetworkStats(networkStats); @@ -469,7 +475,7 @@ public class MobileRadioPowerCalculatorTest { @Test public void testTimerBasedModel_byProcessState() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem") .initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID); @@ -521,8 +527,8 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); // Note application network activity - mStatsRule.setNetworkStats(new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + mStatsRule.setNetworkStats(mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100))); stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000, @@ -531,8 +537,8 @@ public class MobileRadioPowerCalculatorTest { uid.setProcessStateForTest( BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000); - mStatsRule.setNetworkStats(new NetworkStats(12000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + mStatsRule.setNetworkStats(mockNetworkStats(12000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200))); stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000, @@ -586,7 +592,7 @@ public class MobileRadioPowerCalculatorTest { @Test public void testMeasuredEnergyBasedModel_mobileRadioActiveTimeModel() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem") .setPerUidModemModel( BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME) .initMeasuredEnergyStatsLocked(); @@ -619,8 +625,8 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneOnLocked(9800, 9800); // Note application network activity - NetworkStats networkStats = new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)); mStatsRule.setNetworkStats(networkStats); @@ -662,11 +668,9 @@ public class MobileRadioPowerCalculatorTest { .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } - - @Test public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive) + mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator_multiactive") .setPerUidModemModel( BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX) .initMeasuredEnergyStatsLocked(); @@ -728,10 +732,10 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); // Note application network activity - NetworkStats networkStats = new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100)) - .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0, + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100), + mockNetworkStatsEntry("cellular", APP_UID2, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 2000, 30, 111)); mStatsRule.setNetworkStats(networkStats); @@ -850,7 +854,7 @@ public class MobileRadioPowerCalculatorTest { @Test public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel_legacyPowerProfile() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem") .setPerUidModemModel( BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX) .initMeasuredEnergyStatsLocked(); @@ -908,8 +912,8 @@ public class MobileRadioPowerCalculatorTest { stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665); // Note application network activity - NetworkStats networkStats = new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)); mStatsRule.setNetworkStats(networkStats); @@ -957,7 +961,7 @@ public class MobileRadioPowerCalculatorTest { @Test public void testMeasuredEnergyBasedModel_byProcessState() { - mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem) + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem") .initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID); @@ -988,8 +992,8 @@ public class MobileRadioPowerCalculatorTest { new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); // Note application network activity - mStatsRule.setNetworkStats(new NetworkStats(10000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + mStatsRule.setNetworkStats(mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100))); stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager); @@ -997,8 +1001,8 @@ public class MobileRadioPowerCalculatorTest { uid.setProcessStateForTest( BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000); - mStatsRule.setNetworkStats(new NetworkStats(12000, 1) - .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + mStatsRule.setNetworkStats(mockNetworkStats(12000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200))); stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager); @@ -1047,4 +1051,40 @@ public class MobileRadioPowerCalculatorTest { final ModemActivityInfo emptyMai = new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L); stats.noteModemControllerActivity(emptyMai, 0, 0, 0, mNetworkStatsManager); } + + private NetworkStats mockNetworkStats(int elapsedTime, int initialSize, + NetworkStats.Entry... entries) { + NetworkStats stats; + if (RavenwoodRule.isOnRavenwood()) { + stats = mock(NetworkStats.class); + when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator()); + } else { + stats = new NetworkStats(elapsedTime, initialSize); + for (NetworkStats.Entry entry : entries) { + stats = stats.addEntry(entry); + } + } + return stats; + } + + private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid, + int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, + long rxPackets, long txBytes, long txPackets, long operations) { + if (RavenwoodRule.isOnRavenwood()) { + NetworkStats.Entry entry = mock(NetworkStats.Entry.class); + when(entry.getUid()).thenReturn(uid); + when(entry.getMetered()).thenReturn(metered); + when(entry.getRoaming()).thenReturn(roaming); + when(entry.getDefaultNetwork()).thenReturn(defaultNetwork); + when(entry.getRxBytes()).thenReturn(rxBytes); + when(entry.getRxPackets()).thenReturn(rxPackets); + when(entry.getTxBytes()).thenReturn(txBytes); + when(entry.getTxPackets()).thenReturn(txPackets); + when(entry.getOperations()).thenReturn(operations); + return entry; + } else { + return new NetworkStats.Entry(iface, uid, set, tag, metered, + roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations); + } + } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java new file mode 100644 index 000000000000..f93c4da3d8d0 --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2021 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.server.power.stats; + +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.power.stats.EnergyConsumerType; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.OutcomeReceiver; +import android.platform.test.ravenwood.RavenwoodRule; +import android.telephony.AccessNetworkConstants; +import android.telephony.ActivityStatsTechSpecificInfo; +import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.ModemActivityInfo; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.IndentingPrintWriter; + +import com.android.internal.os.Clock; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.StringWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +public class MobileRadioPowerStatsCollectorTest { + private static final int APP_UID1 = 42; + private static final int APP_UID2 = 24; + private static final int APP_UID3 = 44; + private static final int ISOLATED_UID = 99123; + + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = + new BatteryUsageStatsRule().setPowerStatsThrottlePeriodMillis( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 10000); + + private MockBatteryStatsImpl mBatteryStats; + + private final MockClock mClock = mStatsRule.getMockClock(); + + @Mock + private Context mContext; + @Mock + private PackageManager mPackageManager; + @Mock + private TelephonyManager mTelephony; + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + @Mock + private Supplier<NetworkStats> mNetworkStatsSupplier; + @Mock + private PowerStatsUidResolver mPowerStatsUidResolver; + @Mock + private LongSupplier mCallDurationSupplier; + @Mock + private LongSupplier mScanDurationSupplier; + + private final List<PowerStats> mRecordedPowerStats = new ArrayList<>(); + + private MobileRadioPowerStatsCollector.Injector mInjector = + new MobileRadioPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mPowerStatsUidResolver; + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> 3500; + } + + @Override + public Supplier<NetworkStats> getMobileNetworkStatsSupplier() { + return mNetworkStatsSupplier; + } + + @Override + public TelephonyManager getTelephonyManager() { + return mTelephony; + } + + @Override + public LongSupplier getCallDurationSupplier() { + return mCallDurationSupplier; + } + + @Override + public LongSupplier getPhoneSignalScanDurationSupplier() { + return mScanDurationSupplier; + } + }; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mPowerStatsUidResolver.mapUid(anyInt())).thenAnswer(invocation -> { + int uid = invocation.getArgument(0); + if (uid == ISOLATED_UID) { + return APP_UID2; + } else { + return uid; + } + }); + mBatteryStats = mStatsRule.getBatteryStats(); + } + + @SuppressWarnings("GuardedBy") + @Test + public void triggering() throws Throwable { + PowerStatsCollector collector = mBatteryStats.getPowerStatsCollector( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); + collector.addConsumer(mRecordedPowerStats::add); + + mBatteryStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, + true); + + mockModemActivityInfo(1000, 2000, 3000, 600, new int[]{100, 200, 300, 400, 500}); + + // This should trigger a sample collection + mBatteryStats.onSystemReady(mContext); + + mStatsRule.waitForBackgroundThread(); + assertThat(mRecordedPowerStats).hasSize(1); + + mRecordedPowerStats.clear(); + mStatsRule.setTime(20000, 20000); + mBatteryStats.notePhoneOnLocked(mClock.realtime, mClock.uptime); + mStatsRule.waitForBackgroundThread(); + assertThat(mRecordedPowerStats).hasSize(1); + + mRecordedPowerStats.clear(); + mStatsRule.setTime(40000, 40000); + mBatteryStats.notePhoneOffLocked(mClock.realtime, mClock.uptime); + mStatsRule.waitForBackgroundThread(); + assertThat(mRecordedPowerStats).hasSize(1); + + mRecordedPowerStats.clear(); + mStatsRule.setTime(45000, 55000); + mBatteryStats.noteMobileRadioPowerStateLocked( + DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, APP_UID1, mClock.realtime, + mClock.uptime); + mStatsRule.setTime(50001, 50001); + // Elapsed time under the throttling threshold - shouldn't trigger stats collection + mBatteryStats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW, + 0, APP_UID1, mClock.realtime, mClock.uptime); + mStatsRule.waitForBackgroundThread(); + assertThat(mRecordedPowerStats).hasSize(1); + + mRecordedPowerStats.clear(); + mStatsRule.setTime(50002, 50002); + mBatteryStats.noteMobileRadioPowerStateLocked( + DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, APP_UID1, mClock.realtime, + mClock.uptime); + mStatsRule.setTime(55000, 50000); + // Elapsed time under the throttling threshold - shouldn't trigger stats collection + mBatteryStats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW, + 0, APP_UID1, mClock.realtime, mClock.uptime); + mStatsRule.waitForBackgroundThread(); + assertThat(mRecordedPowerStats).isEmpty(); + } + + @Test + public void collectStats() throws Throwable { + PowerStats powerStats = collectPowerStats(true); + assertThat(powerStats.durationMs).isEqualTo(100); + + PowerStats.Descriptor descriptor = powerStats.descriptor; + MobileRadioPowerStatsLayout layout = + new MobileRadioPowerStatsLayout(descriptor); + assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200); + assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300); + assertThat(layout.getDeviceCallTime(powerStats.stats)).isEqualTo(40000); + assertThat(layout.getDeviceScanTime(powerStats.stats)).isEqualTo(60000); + assertThat(layout.getConsumedEnergy(powerStats.stats, 0)) + .isEqualTo((64321 - 10000) * 1000 / 3500); + + assertThat(powerStats.stateStats.size()).isEqualTo(2); + long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey( + BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR, + ServiceState.FREQUENCY_RANGE_MMWAVE + )); + assertThat(layout.getStateRxTime(state1)).isEqualTo(6000); + assertThat(layout.getStateTxTime(state1, 0)).isEqualTo(1000); + assertThat(layout.getStateTxTime(state1, 1)).isEqualTo(2000); + assertThat(layout.getStateTxTime(state1, 2)).isEqualTo(3000); + assertThat(layout.getStateTxTime(state1, 3)).isEqualTo(4000); + assertThat(layout.getStateTxTime(state1, 4)).isEqualTo(5000); + + long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey( + BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + ServiceState.FREQUENCY_RANGE_LOW + )); + assertThat(layout.getStateRxTime(state2)).isEqualTo(7000); + assertThat(layout.getStateTxTime(state2, 0)).isEqualTo(8000); + assertThat(layout.getStateTxTime(state2, 1)).isEqualTo(9000); + assertThat(layout.getStateTxTime(state2, 2)).isEqualTo(1000); + assertThat(layout.getStateTxTime(state2, 3)).isEqualTo(2000); + assertThat(layout.getStateTxTime(state2, 4)).isEqualTo(3000); + + assertThat(powerStats.uidStats.size()).isEqualTo(2); + long[] actual1 = powerStats.uidStats.get(APP_UID1); + assertThat(layout.getUidRxBytes(actual1)).isEqualTo(1000); + assertThat(layout.getUidTxBytes(actual1)).isEqualTo(2000); + assertThat(layout.getUidRxPackets(actual1)).isEqualTo(100); + assertThat(layout.getUidTxPackets(actual1)).isEqualTo(200); + + // Combines APP_UID2 and ISOLATED_UID + long[] actual2 = powerStats.uidStats.get(APP_UID2); + assertThat(layout.getUidRxBytes(actual2)).isEqualTo(6000); + assertThat(layout.getUidTxBytes(actual2)).isEqualTo(3000); + assertThat(layout.getUidRxPackets(actual2)).isEqualTo(60); + assertThat(layout.getUidTxPackets(actual2)).isEqualTo(30); + + assertThat(powerStats.uidStats.get(ISOLATED_UID)).isNull(); + assertThat(powerStats.uidStats.get(APP_UID3)).isNull(); + } + + @Test + public void collectStats_noPerNetworkTypeData() throws Throwable { + PowerStats powerStats = collectPowerStats(false); + assertThat(powerStats.durationMs).isEqualTo(100); + + PowerStats.Descriptor descriptor = powerStats.descriptor; + MobileRadioPowerStatsLayout layout = + new MobileRadioPowerStatsLayout(descriptor); + assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200); + assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300); + assertThat(layout.getConsumedEnergy(powerStats.stats, 0)) + .isEqualTo((64321 - 10000) * 1000 / 3500); + + assertThat(powerStats.stateStats.size()).isEqualTo(1); + long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey( + AccessNetworkConstants.AccessNetworkType.UNKNOWN, + ServiceState.FREQUENCY_RANGE_UNKNOWN + )); + assertThat(layout.getStateRxTime(stateStats)).isEqualTo(6000); + assertThat(layout.getStateTxTime(stateStats, 0)).isEqualTo(1000); + assertThat(layout.getStateTxTime(stateStats, 1)).isEqualTo(2000); + assertThat(layout.getStateTxTime(stateStats, 2)).isEqualTo(3000); + assertThat(layout.getStateTxTime(stateStats, 3)).isEqualTo(4000); + assertThat(layout.getStateTxTime(stateStats, 4)).isEqualTo(5000); + + assertThat(powerStats.uidStats.size()).isEqualTo(2); + long[] actual1 = powerStats.uidStats.get(APP_UID1); + assertThat(layout.getUidRxBytes(actual1)).isEqualTo(1000); + assertThat(layout.getUidTxBytes(actual1)).isEqualTo(2000); + assertThat(layout.getUidRxPackets(actual1)).isEqualTo(100); + assertThat(layout.getUidTxPackets(actual1)).isEqualTo(200); + + // Combines APP_UID2 and ISOLATED_UID + long[] actual2 = powerStats.uidStats.get(APP_UID2); + assertThat(layout.getUidRxBytes(actual2)).isEqualTo(6000); + assertThat(layout.getUidTxBytes(actual2)).isEqualTo(3000); + assertThat(layout.getUidRxPackets(actual2)).isEqualTo(60); + assertThat(layout.getUidTxPackets(actual2)).isEqualTo(30); + + assertThat(powerStats.uidStats.get(ISOLATED_UID)).isNull(); + assertThat(powerStats.uidStats.get(APP_UID3)).isNull(); + } + + @Test + public void dump() throws Throwable { + PowerStats powerStats = collectPowerStats(true); + StringWriter sw = new StringWriter(); + IndentingPrintWriter pw = new IndentingPrintWriter(sw); + powerStats.dump(pw); + pw.flush(); + String dump = sw.toString(); + assertThat(dump).contains("duration=100"); + assertThat(dump).contains( + "stats=[200, 300, 60000, 40000, " + ((64321 - 10000) * 1000 / 3500) + ", 0, 0, 0]"); + assertThat(dump).contains("state LTE: [7000, 8000, 9000, 1000, 2000, 3000]"); + assertThat(dump).contains("state NR MMWAVE: [6000, 1000, 2000, 3000, 4000, 5000]"); + assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 0]"); + assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 0]"); + } + + private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable { + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + collector.setEnabled(true); + + when(mConsumedEnergyRetriever.getEnergyConsumerIds( + EnergyConsumerType.MOBILE_RADIO)).thenReturn(new int[]{777}); + + if (perNetworkTypeData) { + mockModemActivityInfo(1000, 2000, 3000, + AccessNetworkConstants.AccessNetworkType.NGRAN, + ServiceState.FREQUENCY_RANGE_MMWAVE, + 600, new int[]{100, 200, 300, 400, 500}, + AccessNetworkConstants.AccessNetworkType.EUTRAN, + ServiceState.FREQUENCY_RANGE_LOW, + 700, new int[]{800, 900, 100, 200, 300}); + } else { + mockModemActivityInfo(1000, 2000, 3000, 600, new int[]{100, 200, 300, 400, 500}); + } + mockNetworkStats(1000, + 4321, 321, 1234, 23, + 4000, 40, 2000, 20); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777}))) + .thenReturn(new long[]{10000}); + + when(mCallDurationSupplier.getAsLong()).thenReturn(10000L); + when(mScanDurationSupplier.getAsLong()).thenReturn(20000L); + + collector.collectStats(); + + if (perNetworkTypeData) { + mockModemActivityInfo(1100, 2200, 3300, + AccessNetworkConstants.AccessNetworkType.NGRAN, + ServiceState.FREQUENCY_RANGE_MMWAVE, + 6600, new int[]{1100, 2200, 3300, 4400, 5500}, + AccessNetworkConstants.AccessNetworkType.EUTRAN, + ServiceState.FREQUENCY_RANGE_LOW, + 7700, new int[]{8800, 9900, 1100, 2200, 3300}); + } else { + mockModemActivityInfo(1100, 2200, 3300, 6600, new int[]{1100, 2200, 3300, 4400, 5500}); + } + mockNetworkStats(1100, + 5321, 421, 3234, 223, + 8000, 80, 4000, 40); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777}))) + .thenReturn(new long[]{64321}); + when(mCallDurationSupplier.getAsLong()).thenReturn(50000L); + when(mScanDurationSupplier.getAsLong()).thenReturn(80000L); + + mStatsRule.setTime(20000, 20000); + return collector.collectStats(); + } + + private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, + int networkType1, int freqRange1, int rxTimeMs1, @NonNull int[] txTimeMs1, + int networkType2, int freqRange2, int rxTimeMs2, @NonNull int[] txTimeMs2) { + ModemActivityInfo info = new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, + new ActivityStatsTechSpecificInfo[]{ + new ActivityStatsTechSpecificInfo(networkType1, freqRange1, txTimeMs1, + rxTimeMs1), + new ActivityStatsTechSpecificInfo(networkType2, freqRange2, txTimeMs2, + rxTimeMs2)}); + doAnswer(invocation -> { + OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException> + receiver = invocation.getArgument(1); + receiver.onResult(info); + return null; + }).when(mTelephony).requestModemActivityInfo(any(), any()); + } + + private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, + int rxTimeMs, @NonNull int[] txTimeMs) { + ModemActivityInfo info = new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, txTimeMs, + rxTimeMs); + doAnswer(invocation -> { + OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException> + receiver = invocation.getArgument(1); + receiver.onResult(info); + return null; + }).when(mTelephony).requestModemActivityInfo(any(), any()); + } + + private void mockNetworkStats(long elapsedRealtime, + long rxBytes1, long rxPackets1, long txBytes1, long txPackets1, + long rxBytes2, long rxPackets2, long txBytes2, long txPackets2) { + NetworkStats stats; + if (RavenwoodRule.isOnRavenwood()) { + stats = mock(NetworkStats.class); + List<NetworkStats.Entry> entries = List.of( + mockNetworkStatsEntry(APP_UID1, rxBytes1, rxPackets1, txBytes1, txPackets1), + mockNetworkStatsEntry(APP_UID2, rxBytes2, rxPackets2, txBytes2, txPackets2), + mockNetworkStatsEntry(ISOLATED_UID, rxBytes2 / 2, rxPackets2 / 2, txBytes2 / 2, + txPackets2 / 2), + mockNetworkStatsEntry(APP_UID3, 314, 281, 314, 281)); + when(stats.iterator()).thenAnswer(inv -> entries.iterator()); + } else { + stats = new NetworkStats(elapsedRealtime, 1) + .addEntry(new NetworkStats.Entry("mobile", APP_UID1, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes1, rxPackets1, + txBytes1, txPackets1, 100)) + .addEntry(new NetworkStats.Entry("mobile", APP_UID2, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes2, rxPackets2, + txBytes2, txPackets2, 111)) + .addEntry(new NetworkStats.Entry("mobile", ISOLATED_UID, 0, 0, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes2 / 2, rxPackets2 / 2, + txBytes2 / 2, txPackets2 / 2, 111)) + .addEntry(new NetworkStats.Entry("mobile", APP_UID3, 0, 0, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 314, 281, 314, 281, 111)); + } + when(mNetworkStatsSupplier.get()).thenReturn(stats); + } + + private static NetworkStats.Entry mockNetworkStatsEntry(int uid, long rxBytes, long rxPackets, + long txBytes, long txPackets) { + NetworkStats.Entry entry = mock(NetworkStats.Entry.class); + when(entry.getUid()).thenReturn(uid); + when(entry.getMetered()).thenReturn(METERED_NO); + when(entry.getRoaming()).thenReturn(ROAMING_NO); + when(entry.getDefaultNetwork()).thenReturn(DEFAULT_NETWORK_NO); + when(entry.getRxBytes()).thenReturn(rxBytes); + when(entry.getRxPackets()).thenReturn(rxPackets); + when(entry.getTxBytes()).thenReturn(txBytes); + when(entry.getTxPackets()).thenReturn(txPackets); + when(entry.getOperations()).thenReturn(100L); + return entry; + } + + @Test + public void networkTypeConstants() throws Throwable { + Class<AccessNetworkConstants.AccessNetworkType> clazz = + AccessNetworkConstants.AccessNetworkType.class; + for (Field field : clazz.getDeclaredFields()) { + final int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) + && field.getType().equals(int.class)) { + boolean found = false; + int value = field.getInt(null); + for (int i = 0; i < MobileRadioPowerStatsCollector.NETWORK_TYPES.length; i++) { + if (MobileRadioPowerStatsCollector.NETWORK_TYPES[i] == value) { + found = true; + break; + } + } + assertWithMessage("New network type, " + field.getName() + " not represented in " + + MobileRadioPowerStatsCollector.class).that(found).isTrue(); + } + } + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java new file mode 100644 index 000000000000..4ac7ad8d07ff --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; +import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE; + +import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.power.stats.EnergyConsumerType; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.Handler; +import android.os.OutcomeReceiver; +import android.os.Process; +import android.platform.test.ravenwood.RavenwoodRule; +import android.telephony.ModemActivityInfo; +import android.telephony.TelephonyManager; + +import com.android.internal.os.Clock; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +public class MobileRadioPowerStatsProcessorTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + private static final double PRECISION = 0.00001; + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101; + private static final int MOBILE_RADIO_ENERGY_CONSUMER_ID = 1; + private static final int VOLTAGE_MV = 3500; + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + @Mock + private Context mContext; + @Mock + private PowerStatsUidResolver mPowerStatsUidResolver; + @Mock + private PackageManager mPackageManager; + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + @Mock + private Supplier<NetworkStats> mNetworkStatsSupplier; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private LongSupplier mCallDurationSupplier; + @Mock + private LongSupplier mScanDurationSupplier; + + private final MobileRadioPowerStatsCollector.Injector mInjector = + new MobileRadioPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mPowerStatsUidResolver; + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> VOLTAGE_MV; + } + + @Override + public Supplier<NetworkStats> getMobileNetworkStatsSupplier() { + return mNetworkStatsSupplier; + } + + @Override + public TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + + @Override + public LongSupplier getCallDurationSupplier() { + return mCallDurationSupplier; + } + + @Override + public LongSupplier getPhoneSignalScanDurationSupplier() { + return mScanDurationSupplier; + } + }; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mPowerStatsUidResolver.mapUid(anyInt())) + .thenAnswer(invocation -> invocation.getArgument(0)); + } + + @Test + public void powerProfileModel() { + // No power monitoring hardware + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO)) + .thenReturn(new int[0]); + + mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator"); + + MobileRadioPowerStatsProcessor processor = + new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()); + + AggregatedPowerStatsConfig.PowerComponent config = + new AggregatedPowerStatsConfig.PowerComponent( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) + .trackDeviceStates(STATE_POWER, STATE_SCREEN) + .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE) + .setProcessor(processor); + + PowerComponentAggregatedPowerStats aggregatedStats = + new PowerComponentAggregatedPowerStats( + new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config); + + aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0); + aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0); + aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); + aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); + + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + collector.setEnabled(true); + + // Initial empty ModemActivityInfo. + mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L)); + + // Establish a baseline + aggregatedStats.addPowerStats(collector.collectStats(), 0); + + // Turn the screen off after 2.5 seconds + aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, + 5000); + + // Note application network activity + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100), + mockNetworkStatsEntry("cellular", APP_UID2, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111)); + + when(mNetworkStatsSupplier.get()).thenReturn(networkStats); + + ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, + new int[]{100, 200, 300, 400, 500}, 600); + mockModemActivityInfo(mai); + + when(mCallDurationSupplier.getAsLong()).thenReturn(200L); + when(mScanDurationSupplier.getAsLong()).thenReturn(5555L); + + mStatsRule.setTime(10_000, 10_000); + + PowerStats powerStats = collector.collectStats(); + + aggregatedStats.addPowerStats(powerStats, 10_000); + + processor.finish(aggregatedStats); + + MobileRadioPowerStatsLayout statsLayout = + new MobileRadioPowerStatsLayout( + aggregatedStats.getPowerStatsDescriptor()); + + // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) + // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) + // + 1440 mA * 300 ms (level 2 TX drain rate * level 2 TX duration) + // + 1800 mA * 400 ms (level 3 TX drain rate * level 3 TX duration) + // + 2160 mA * 500 ms (level 4 TX drain rate * level 4 TX duration) + // + 1440 mA * 600 ms (RX drain rate * RX duration) + // + 360 mA * 3000 ms (idle drain rate * idle duration) + // + 70 mA * 2000 ms (sleep drain rate * sleep duration) + // _________________ + // = 4604000 mA-ms or 1.27888 mA-h + // 25% of 1.27888 = 0.319722 + // 75% of 1.27888 = 0.959166 + double totalPower = 0; + long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength]; + aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.319722); + totalPower += statsLayout.getDevicePowerEstimate(deviceStats); + + aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.959166); + totalPower += statsLayout.getDevicePowerEstimate(deviceStats); + + assertThat(totalPower).isWithin(PRECISION).of(1.27888); + + // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) + // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) + // + 1440 mA * 300 ms (level 2 TX drain rate * level 2 TX duration) + // + 1800 mA * 400 ms (level 3 TX drain rate * level 3 TX duration) + // + 2160 mA * 500 ms (level 4 TX drain rate * level 4 TX duration) + // + 1440 mA * 600 ms (RX drain rate * RX duration) + // _________________ + // = 3384000 mA-ms or 0.94 mA-h + double uidPower1 = 0; + long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength]; + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.17625); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.17625); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.3525); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + double uidPower2 = 0; + aggregatedStats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.05875); + uidPower2 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.17625); + uidPower2 += statsLayout.getUidPowerEstimate(uidStats); + + assertThat(uidPower1 + uidPower2) + .isWithin(PRECISION).of(0.94); + + // 3/4 of total packets were sent by APP_UID so 75% of total + assertThat(uidPower1 / (uidPower1 + uidPower2)) + .isWithin(PRECISION).of(0.75); + } + + @Test + public void measuredEnergyModel() { + // PowerStats hardware is available + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO)) + .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID}); + + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem") + .initMeasuredEnergyStatsLocked(); + + MobileRadioPowerStatsProcessor processor = + new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()); + + AggregatedPowerStatsConfig.PowerComponent config = + new AggregatedPowerStatsConfig.PowerComponent( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) + .trackDeviceStates(STATE_POWER, STATE_SCREEN) + .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE) + .setProcessor(processor); + + PowerComponentAggregatedPowerStats aggregatedStats = + new PowerComponentAggregatedPowerStats( + new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config); + + aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0); + aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0); + aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); + aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); + + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + collector.setEnabled(true); + + // Initial empty ModemActivityInfo. + mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L)); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws( + new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{0}); + + // Establish a baseline + aggregatedStats.addPowerStats(collector.collectStats(), 0); + + // Turn the screen off after 2.5 seconds + aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, + 5000); + + // Note application network activity + NetworkStats networkStats = mockNetworkStats(10000, 1, + mockNetworkStatsEntry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100), + mockNetworkStatsEntry("cellular", APP_UID2, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111)); + + when(mNetworkStatsSupplier.get()).thenReturn(networkStats); + + ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, + new int[]{100, 200, 300, 400, 500}, 600); + mockModemActivityInfo(mai); + + mStatsRule.setTime(10_000, 10_000); + + long energyUws = 10_000_000L * VOLTAGE_MV / 1000L; + when(mConsumedEnergyRetriever.getConsumedEnergyUws( + new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws}); + + when(mCallDurationSupplier.getAsLong()).thenReturn(200L); + when(mScanDurationSupplier.getAsLong()).thenReturn(5555L); + + PowerStats powerStats = collector.collectStats(); + + aggregatedStats.addPowerStats(powerStats, 10_000); + + processor.finish(aggregatedStats); + + MobileRadioPowerStatsLayout statsLayout = + new MobileRadioPowerStatsLayout( + aggregatedStats.getPowerStatsDescriptor()); + + // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh + double totalPower = 0; + long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength]; + aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.671837); + totalPower += statsLayout.getDevicePowerEstimate(deviceStats); + assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.022494); + totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats); + + aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(2.01596); + totalPower += statsLayout.getDevicePowerEstimate(deviceStats); + assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.067484); + totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats); + + // These estimates are supposed to add up to the measured energy, 2.77778 mAh + assertThat(totalPower).isWithin(PRECISION).of(2.77778); + + double uidPower1 = 0; + long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength]; + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.198236); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.198236); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.396473); + uidPower1 += statsLayout.getUidPowerEstimate(uidStats); + + double uidPower2 = 0; + aggregatedStats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.066078); + uidPower2 += statsLayout.getUidPowerEstimate(uidStats); + + aggregatedStats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.198236); + uidPower2 += statsLayout.getUidPowerEstimate(uidStats); + + // Total power attributed to apps is significantly less than the grand total, + // because we only attribute TX/RX to apps but not maintaining a connection with the cell. + assertThat(uidPower1 + uidPower2) + .isWithin(PRECISION).of(1.057259); + + // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it + assertThat(uidPower1 / (uidPower1 + uidPower2)) + .isWithin(PRECISION).of(0.75); + } + + private int[] states(int... states) { + return states; + } + + private void mockModemActivityInfo(ModemActivityInfo emptyMai) { + doAnswer(invocation -> { + OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException> + receiver = invocation.getArgument(1); + receiver.onResult(emptyMai); + return null; + }).when(mTelephonyManager).requestModemActivityInfo(any(), any()); + } + + private NetworkStats mockNetworkStats(int elapsedTime, int initialSize, + NetworkStats.Entry... entries) { + NetworkStats stats; + if (RavenwoodRule.isOnRavenwood()) { + stats = mock(NetworkStats.class); + when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator()); + } else { + stats = new NetworkStats(elapsedTime, initialSize); + for (NetworkStats.Entry entry : entries) { + stats = stats.addEntry(entry); + } + } + return stats; + } + + private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid, + int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, + long rxPackets, long txBytes, long txPackets, long operations) { + if (RavenwoodRule.isOnRavenwood()) { + NetworkStats.Entry entry = mock(NetworkStats.Entry.class); + when(entry.getUid()).thenReturn(uid); + when(entry.getMetered()).thenReturn(metered); + when(entry.getRoaming()).thenReturn(roaming); + when(entry.getDefaultNetwork()).thenReturn(defaultNetwork); + when(entry.getRxBytes()).thenReturn(rxBytes); + when(entry.getRxPackets()).thenReturn(rxPackets); + when(entry.getTxBytes()).thenReturn(txBytes); + when(entry.getTxPackets()).thenReturn(txPackets); + when(entry.getOperations()).thenReturn(operations); + return entry; + } else { + return new NetworkStats.Entry(iface, uid, set, tag, metered, + roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations); + } + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java index 9f069130502f..da3834633552 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java @@ -52,6 +52,8 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { // The mNetworkStats will be used for both wifi and mobile categories private NetworkStats mNetworkStats; private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync(); + public static final BatteryStatsConfig DEFAULT_CONFIG = + new BatteryStatsConfig.Builder().build(); MockBatteryStatsImpl() { this(new MockClock()); @@ -66,12 +68,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler) { - this(clock, historyDirectory, handler, new PowerStatsUidResolver()); + this(DEFAULT_CONFIG, clock, historyDirectory, handler, new PowerStatsUidResolver()); } - MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler, - PowerStatsUidResolver powerStatsUidResolver) { - super(clock, historyDirectory, handler, powerStatsUidResolver, + MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory, + Handler handler, PowerStatsUidResolver powerStatsUidResolver) { + super(config, clock, historyDirectory, handler, powerStatsUidResolver, mock(FrameworkStatsLogger.class), mock(BatteryStatsHistory.TraceDelegate.class), mock(BatteryStatsHistory.EventLogger.class)); initTimersAndCounters(); @@ -276,10 +278,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { public void writeSyncLocked() { } - public void setHandler(Handler handler) { - mHandler = handler; - } - @Override protected void updateBatteryPropertiesLocked() { } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java new file mode 100644 index 000000000000..dadcf3f3871e --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2024 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.server.power.stats; + +import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.power.stats.EnergyConsumerType; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.Handler; +import android.os.OutcomeReceiver; +import android.platform.test.ravenwood.RavenwoodRule; +import android.telephony.ModemActivityInfo; +import android.telephony.TelephonyManager; + +import com.android.internal.os.Clock; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +public class PhoneCallPowerStatsProcessorTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + private static final double PRECISION = 0.00001; + private static final int VOLTAGE_MV = 3500; + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + @Mock + private Context mContext; + @Mock + private PowerStatsUidResolver mPowerStatsUidResolver; + @Mock + private PackageManager mPackageManager; + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + @Mock + private Supplier<NetworkStats> mNetworkStatsSupplier; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private LongSupplier mCallDurationSupplier; + @Mock + private LongSupplier mScanDurationSupplier; + + private final MobileRadioPowerStatsCollector.Injector mInjector = + new MobileRadioPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mPowerStatsUidResolver; + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> VOLTAGE_MV; + } + + @Override + public Supplier<NetworkStats> getMobileNetworkStatsSupplier() { + return mNetworkStatsSupplier; + } + + @Override + public TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + + @Override + public LongSupplier getCallDurationSupplier() { + return mCallDurationSupplier; + } + + @Override + public LongSupplier getPhoneSignalScanDurationSupplier() { + return mScanDurationSupplier; + } + }; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mPowerStatsUidResolver.mapUid(anyInt())) + .thenAnswer(invocation -> invocation.getArgument(0)); + + // No power monitoring hardware + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO)) + .thenReturn(new int[0]); + + mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem"); + } + + @Test + public void copyEstimatesFromMobileRadioPowerStats() { + MobileRadioPowerStatsProcessor mobileStatsProcessor = + new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()); + + PhoneCallPowerStatsProcessor phoneStatsProcessor = + new PhoneCallPowerStatsProcessor(); + + AggregatedPowerStatsConfig aggregatedPowerStatsConfig = new AggregatedPowerStatsConfig(); + aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) + .trackDeviceStates(STATE_POWER, STATE_SCREEN) + .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE) + .setProcessor(mobileStatsProcessor); + aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE, + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) + .setProcessor(phoneStatsProcessor); + + AggregatedPowerStats aggregatedPowerStats = + new AggregatedPowerStats(aggregatedPowerStatsConfig); + PowerComponentAggregatedPowerStats mobileRadioStats = + aggregatedPowerStats.getPowerComponentStats( + BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); + + aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0); + aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0); + + MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0); + collector.setEnabled(true); + + // Initial empty ModemActivityInfo. + mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L)); + + // Establish a baseline + aggregatedPowerStats.addPowerStats(collector.collectStats(), 0); + + // Turn the screen off after 2.5 seconds + aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + + ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, + new int[]{100, 200, 300, 400, 500}, 600); + mockModemActivityInfo(mai); + + // A phone call was made + when(mCallDurationSupplier.getAsLong()).thenReturn(7000L); + + mStatsRule.setTime(10_000, 10_000); + + aggregatedPowerStats.addPowerStats(collector.collectStats(), 10_000); + + mobileStatsProcessor.finish(mobileRadioStats); + + PowerComponentAggregatedPowerStats stats = + aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE); + phoneStatsProcessor.finish(stats); + + PowerStatsLayout statsLayout = + new PowerStatsLayout(stats.getPowerStatsDescriptor()); + + long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.7); + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(2.1); + } + + private void mockModemActivityInfo(ModemActivityInfo emptyMai) { + doAnswer(invocation -> { + OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException> + receiver = invocation.getArgument(1); + receiver.onResult(emptyMai); + return null; + }).when(mTelephonyManager).requestModemActivityInfo(any(), any()); + } + + private int[] states(int... states) { + return states; + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java index 2ea86a4527eb..03b02cfde146 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java @@ -76,9 +76,8 @@ public class PowerStatsAggregatorTest { @Test public void stateUpdates() { - PowerStats.Descriptor descriptor = - new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, - new PersistableBundle()); + PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, + "majorDrain", 1, null, 0, 1, new PersistableBundle()); PowerStats powerStats = new PowerStats(descriptor); mClock.currentTime = 1222156800000L; // An important date in world history @@ -186,9 +185,8 @@ public class PowerStatsAggregatorTest { @Test public void incompatiblePowerStats() { - PowerStats.Descriptor descriptor = - new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, - new PersistableBundle()); + PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, + "majorDrain", 1, null, 0, 1, new PersistableBundle()); PowerStats powerStats = new PowerStats(descriptor); mHistory.forceRecordAllHistory(); @@ -209,7 +207,7 @@ public class PowerStatsAggregatorTest { advance(1000); - descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1, + descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, null, 0, 1, PersistableBundle.forPair("something", "changed")); powerStats = new PowerStats(descriptor); powerStats.stats = new long[]{20000}; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java index 17a7d3ecf9d3..df1200bb6b1a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java @@ -18,11 +18,22 @@ package com.android.server.power.stats; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.hardware.power.stats.EnergyConsumer; +import android.hardware.power.stats.EnergyConsumerResult; +import android.hardware.power.stats.EnergyConsumerType; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.PersistableBundle; +import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.ravenwood.RavenwoodRule; +import android.power.PowerStatsInternal; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -34,6 +45,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + @RunWith(AndroidJUnit4.class) @SmallTest public class PowerStatsCollectorTest { @@ -57,7 +71,8 @@ public class PowerStatsCollectorTest { mMockClock) { @Override protected PowerStats collectStats() { - return new PowerStats(new PowerStats.Descriptor(0, 0, 0, new PersistableBundle())); + return new PowerStats( + new PowerStats.Descriptor(0, 0, null, 0, 0, new PersistableBundle())); } }; mCollector.addConsumer(stats -> mCollectedStats = stats); @@ -92,4 +107,74 @@ public class PowerStatsCollectorTest { mHandler.post(done::open); done.block(); } + + @Test + @DisabledOnRavenwood + public void consumedEnergyRetriever() throws Exception { + PowerStatsInternal powerStatsInternal = mock(PowerStatsInternal.class); + mockEnergyConsumers(powerStatsInternal); + + PowerStatsCollector.ConsumedEnergyRetrieverImpl retriever = + new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal); + int[] energyConsumerIds = retriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER); + assertThat(energyConsumerIds).isEqualTo(new int[]{1, 2}); + long[] energy = retriever.getConsumedEnergyUws(energyConsumerIds); + assertThat(energy).isEqualTo(new long[]{1000, 2000}); + energy = retriever.getConsumedEnergyUws(energyConsumerIds); + assertThat(energy).isEqualTo(new long[]{1500, 2700}); + } + + @SuppressWarnings("unchecked") + private void mockEnergyConsumers(PowerStatsInternal powerStatsInternal) throws Exception { + when(powerStatsInternal.getEnergyConsumerInfo()) + .thenReturn(new EnergyConsumer[]{ + new EnergyConsumer() {{ + id = 1; + type = EnergyConsumerType.CPU_CLUSTER; + ordinal = 0; + name = "CPU0"; + }}, + new EnergyConsumer() {{ + id = 2; + type = EnergyConsumerType.CPU_CLUSTER; + ordinal = 1; + name = "CPU4"; + }}, + new EnergyConsumer() {{ + id = 3; + type = EnergyConsumerType.BLUETOOTH; + name = "BT"; + }}, + }); + + CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class); + when(future1.get(anyLong(), any(TimeUnit.class))) + .thenReturn(new EnergyConsumerResult[]{ + new EnergyConsumerResult() {{ + id = 1; + energyUWs = 1000; + }}, + new EnergyConsumerResult() {{ + id = 2; + energyUWs = 2000; + }} + }); + + CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class); + when(future2.get(anyLong(), any(TimeUnit.class))) + .thenReturn(new EnergyConsumerResult[]{ + new EnergyConsumerResult() {{ + id = 1; + energyUWs = 1500; + }}, + new EnergyConsumerResult() {{ + id = 2; + energyUWs = 2700; + }} + }); + + when(powerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2}))) + .thenReturn(future1) + .thenReturn(future2); + } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java index 18d7b909150b..412fc88dcd27 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java @@ -77,7 +77,7 @@ public class PowerStatsExporterTest { private PowerStatsStore mPowerStatsStore; private PowerStatsAggregator mPowerStatsAggregator; private BatteryStatsHistory mHistory; - private CpuPowerStatsCollector.CpuStatsArrayLayout mCpuStatsArrayLayout; + private CpuPowerStatsLayout mCpuStatsArrayLayout; private PowerStats.Descriptor mPowerStatsDescriptor; @Before @@ -93,7 +93,7 @@ public class PowerStatsExporterTest { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessor( - new CpuAggregatedPowerStatsProcessor(mStatsRule.getPowerProfile(), + new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies())); mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config); @@ -102,9 +102,10 @@ public class PowerStatsExporterTest { mMonotonicClock, null, null); mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory); - mCpuStatsArrayLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout(); + mCpuStatsArrayLayout = new CpuPowerStatsLayout(); mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1); mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1); + mCpuStatsArrayLayout.addDeviceSectionUsageDuration(); mCpuStatsArrayLayout.addDeviceSectionPowerEstimate(); mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0}); mCpuStatsArrayLayout.addUidSectionPowerEstimate(); @@ -113,7 +114,7 @@ public class PowerStatsExporterTest { mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, mCpuStatsArrayLayout.getDeviceStatsArrayLength(), - mCpuStatsArrayLayout.getUidStatsArrayLength(), extras); + null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras); } @Test @@ -126,20 +127,20 @@ public class PowerStatsExporterTest { BatteryUsageStats actual = builder.build(); String message = "Actual BatteryUsageStats: " + actual; - assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); - assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); + assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016); + assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_ANY, 13.5); + BatteryConsumer.PROCESS_STATE_ANY, 3.97099); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_FOREGROUND, 7.47); + BatteryConsumer.PROCESS_STATE_FOREGROUND, 2.198082); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_BACKGROUND, 6.03); + BatteryConsumer.PROCESS_STATE_BACKGROUND, 1.772916); assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_ANY, 12.03); + BatteryConsumer.PROCESS_STATE_ANY, 3.538999); assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 12.03); + BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.538999); actual.close(); } @@ -154,20 +155,20 @@ public class PowerStatsExporterTest { BatteryUsageStats actual = builder.build(); String message = "Actual BatteryUsageStats: " + actual; - assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4); - assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4); + assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749); + assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_ANY, 4.06); + BatteryConsumer.PROCESS_STATE_ANY, 1.193332); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_FOREGROUND, 1.35); + BatteryConsumer.PROCESS_STATE_FOREGROUND, 0.397749); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_BACKGROUND, 2.70); + BatteryConsumer.PROCESS_STATE_BACKGROUND, 0.795583); assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_ANY, 11.33); + BatteryConsumer.PROCESS_STATE_ANY, 3.333249); assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 11.33); + BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.333249); actual.close(); } @@ -182,13 +183,13 @@ public class PowerStatsExporterTest { BatteryUsageStats actual = builder.build(); String message = "Actual BatteryUsageStats: " + actual; - assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); - assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53); + assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016); + assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016); assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_ANY, 13.5); + BatteryConsumer.PROCESS_STATE_ANY, 3.97099); assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU, - BatteryConsumer.PROCESS_STATE_ANY, 12.03); + BatteryConsumer.PROCESS_STATE_ANY, 3.538999); UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream() .filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null); // There shouldn't be any per-procstate data diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java index af83be04db7d..02e446aa1859 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java @@ -35,7 +35,7 @@ import java.util.Objects; @RunWith(AndroidJUnit4.class) @SmallTest -public class AggregatedPowerStatsProcessorTest { +public class PowerStatsProcessorTest { @Test public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() { @@ -44,8 +44,8 @@ public class AggregatedPowerStatsProcessorTest { .trackDeviceStates(STATE_POWER, STATE_SCREEN) .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE); - AggregatedPowerStatsProcessor.PowerEstimationPlan plan = - new AggregatedPowerStatsProcessor.PowerEstimationPlan(config); + PowerStatsProcessor.PowerEstimationPlan plan = + new PowerStatsProcessor.PowerEstimationPlan(config); assertThat(deviceStateEstimatesToStrings(plan)) .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]"); assertThat(combinedDeviceStatsToStrings(plan)) @@ -65,8 +65,8 @@ public class AggregatedPowerStatsProcessorTest { .trackDeviceStates(STATE_POWER, STATE_SCREEN) .trackUidStates(STATE_POWER, STATE_PROCESS_STATE); - AggregatedPowerStatsProcessor.PowerEstimationPlan plan = - new AggregatedPowerStatsProcessor.PowerEstimationPlan(config); + PowerStatsProcessor.PowerEstimationPlan plan = + new PowerStatsProcessor.PowerEstimationPlan(config); assertThat(deviceStateEstimatesToStrings(plan)) .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]"); @@ -81,13 +81,13 @@ public class AggregatedPowerStatsProcessorTest { } private static List<String> deviceStateEstimatesToStrings( - AggregatedPowerStatsProcessor.PowerEstimationPlan plan) { + PowerStatsProcessor.PowerEstimationPlan plan) { return plan.deviceStateEstimations.stream() .map(dse -> dse.stateValues).map(Arrays::toString).toList(); } private static List<String> combinedDeviceStatsToStrings( - AggregatedPowerStatsProcessor.PowerEstimationPlan plan) { + PowerStatsProcessor.PowerEstimationPlan plan) { return plan.combinedDeviceStateEstimations.stream() .map(cds -> cds.deviceStateEstimations) .map(dses -> dses.stream() @@ -97,7 +97,7 @@ public class AggregatedPowerStatsProcessorTest { } private static List<String> uidStateEstimatesToStrings( - AggregatedPowerStatsProcessor.PowerEstimationPlan plan, + PowerStatsProcessor.PowerEstimationPlan plan, AggregatedPowerStatsConfig.PowerComponent config) { MultiStateStats.States[] uidStateConfig = config.getUidStateConfig(); return plan.uidStateEstimates.stream() diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java index 80cbe0da402e..d67d40862e95 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java @@ -18,11 +18,14 @@ package com.android.server.power.stats; import static com.google.common.truth.Truth.assertThat; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.KernelSingleProcessCpuThreadReader; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,7 +33,10 @@ import java.io.IOException; @SmallTest @RunWith(AndroidJUnit4.class) +@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency") public class SystemServerCpuThreadReaderTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test public void testReadDelta() throws IOException { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java index 8e53d5285cc4..ef0b570a1354 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java @@ -31,9 +31,10 @@ import android.os.Process; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.RavenwoodFlagsValueProvider; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.KernelCpuSpeedReader; @@ -46,7 +47,6 @@ import com.android.server.power.optimization.Flags; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -55,17 +55,21 @@ import java.util.ArrayList; import java.util.Collection; @SmallTest -@RunWith(AndroidJUnit4.class) @SuppressWarnings("GuardedBy") public class SystemServicePowerCalculatorTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Rule(order = 1) + public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood() + ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule() + : DeviceFlagsValueProvider.createCheckFlagsRule(); private static final double PRECISION = 0.000001; private static final int APP_UID1 = 100; private static final int APP_UID2 = 200; - @Rule + @Rule(order = 2) public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720) .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200}) |