diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java index dc93a473fe448..ed1643df92fbd 100644 --- a/core/java/android/util/SparseDoubleArray.java +++ b/core/java/android/util/SparseDoubleArray.java @@ -81,9 +81,17 @@ public class SparseDoubleArray implements Cloneable { * if no such mapping has been made. */ public double get(int key) { + return get(key, 0); + } + + /** + * Gets the double mapped from the specified key, or the specified value + * if no such mapping has been made. + */ + public double get(int key, double valueIfKeyNotFound) { final int index = mValues.indexOfKey(key); if (index < 0) { - return 0.0d; + return valueIfKeyNotFound; } return valueAt(index); } @@ -137,6 +145,13 @@ public class SparseDoubleArray implements Cloneable { return Double.longBitsToDouble(mValues.valueAt(index)); } + /** + * Removes all key-value mappings from this SparseDoubleArray. + */ + public void clear() { + mValues.clear(); + } + /** * {@inheritDoc} * diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 21f719c74aa80..9cb887fa13e67 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -16026,6 +16026,18 @@ public class BatteryStatsImpl extends BatteryStats { iPw.decreaseIndent(); } + /** + * Dump Power Profile + */ + @GuardedBy("this") + public void dumpPowerProfileLocked(PrintWriter pw) { + final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " "); + iPw.printf("Power Profile: \n"); + iPw.increaseIndent(); + mPowerProfile.dump(iPw); + iPw.decreaseIndent(); + } + final ReentrantLock mWriteLock = new ReentrantLock(); @GuardedBy("this") diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 4d19b35b1e166..7f8acccd72bb5 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -17,24 +17,31 @@ package com.android.internal.os; +import android.annotation.LongDef; import android.annotation.StringDef; +import android.annotation.XmlRes; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.power.ModemPowerProfile; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -258,6 +265,35 @@ public class PowerProfile { @Retention(RetentionPolicy.SOURCE) public @interface PowerGroup {} + /** + * Constants for generating a 64bit power constant key. + * + * The bitfields of a key describes what its corresponding power constant represents: + * [63:40] - RESERVED + * [39:32] - {@link Subsystem} (max count = 16). + * [31:0] - per Subsystem fields, see {@link ModemPowerProfile}. + * + */ + private static final int SUBSYSTEM_SHIFT = 32; + private static final long SUBSYSTEM_MASK = 0xF << SUBSYSTEM_SHIFT; + /** + * Power constant not associated with a subsystem. + */ + public static final long SUBSYSTEM_NONE = 0 << SUBSYSTEM_SHIFT; + /** + * Modem power constant. + */ + public static final long SUBSYSTEM_MODEM = 1 << SUBSYSTEM_SHIFT; + + @LongDef(prefix = { "SUBSYSTEM_" }, value = { + SUBSYSTEM_NONE, + SUBSYSTEM_MODEM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Subsystem {} + + private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFFFFFF; + /** * A map from Power Use Item to its power consumption. */ @@ -268,12 +304,16 @@ public class PowerProfile { */ static final HashMap sPowerArrayMap = new HashMap<>(); + static final ModemPowerProfile sModemPowerProfile = new ModemPowerProfile(); + private static final String TAG_DEVICE = "device"; private static final String TAG_ITEM = "item"; private static final String TAG_ARRAY = "array"; private static final String TAG_ARRAYITEM = "value"; private static final String ATTR_NAME = "name"; + private static final String TAG_MODEM = "modem"; + private static final Object sLock = new Object(); @VisibleForTesting @@ -289,19 +329,40 @@ public class PowerProfile { public PowerProfile(Context context, boolean forTest) { // Read the XML file for the given profile (normally only one per device) synchronized (sLock) { - if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { - readPowerValuesFromXml(context, forTest); - } - initCpuClusters(); - initDisplays(); + final int xmlId = forTest ? com.android.internal.R.xml.power_profile_test + : com.android.internal.R.xml.power_profile; + initLocked(context, xmlId); } } - private void readPowerValuesFromXml(Context context, boolean forTest) { - final int id = forTest ? com.android.internal.R.xml.power_profile_test : - com.android.internal.R.xml.power_profile; + /** + * Reinitialize the PowerProfile with the provided XML. + * WARNING: use only for testing! + */ + @VisibleForTesting + public void forceInitForTesting(Context context, @XmlRes int xmlId) { + synchronized (sLock) { + sPowerItemMap.clear(); + sPowerArrayMap.clear(); + sModemPowerProfile.clear(); + initLocked(context, xmlId); + } + + } + + @GuardedBy("sLock") + private void initLocked(Context context, @XmlRes int xmlId) { + if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { + readPowerValuesFromXml(context, xmlId); + } + initCpuClusters(); + initDisplays(); + initModem(); + } + + private void readPowerValuesFromXml(Context context, @XmlRes int xmlId) { final Resources resources = context.getResources(); - XmlResourceParser parser = resources.getXml(id); + XmlResourceParser parser = resources.getXml(xmlId); boolean parsingArray = false; ArrayList array = new ArrayList<>(); String arrayName = null; @@ -340,6 +401,8 @@ public class PowerProfile { array.add(value); } } + } else if (element.equals(TAG_MODEM)) { + sModemPowerProfile.parseFromXml(parser); } } if (parsingArray) { @@ -515,6 +578,39 @@ public class PowerProfile { return mNumDisplays; } + private void initModem() { + handleDeprecatedModemConstant(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP, + POWER_MODEM_CONTROLLER_SLEEP, 0); + handleDeprecatedModemConstant(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE, + POWER_MODEM_CONTROLLER_SLEEP, 0); + handleDeprecatedModemConstant( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX, + POWER_MODEM_CONTROLLER_RX, 0); + handleDeprecatedModemConstant( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0, POWER_MODEM_CONTROLLER_TX, 0); + handleDeprecatedModemConstant( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1, POWER_MODEM_CONTROLLER_TX, 1); + handleDeprecatedModemConstant( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2, POWER_MODEM_CONTROLLER_TX, 2); + handleDeprecatedModemConstant( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3, POWER_MODEM_CONTROLLER_TX, 3); + handleDeprecatedModemConstant( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4, POWER_MODEM_CONTROLLER_TX, 4); + } + + private void handleDeprecatedModemConstant(int key, String deprecatedKey, int level) { + final double drain = sModemPowerProfile.getAverageBatteryDrainMa(key); + if (!Double.isNaN(drain)) return; // Value already set, don't overwrite it. + + final double deprecatedDrain = getAveragePower(deprecatedKey, level); + sModemPowerProfile.setPowerConstant(key, Double.toString(deprecatedDrain)); + } + /** * Returns the number of memory bandwidth buckets defined in power_profile.xml, or a * default value if the subsystem has no recorded value. @@ -559,6 +655,43 @@ public class PowerProfile { return getAveragePowerOrDefault(type, 0); } + /** + * Returns the average current in mA consumed by a subsystem's specified operation, or the given + * default value if the subsystem has no recorded value. + * + * @param key that describes a subsystem's battery draining operation + * The key is built from multiple constant, see {@link Subsystem} and + * {@link ModemPowerProfile}. + * @param defaultValue the value to return if the subsystem has no recorded value. + * @return the average current in milliAmps. + */ + public double getAverageBatteryDrainOrDefaultMa(long key, double defaultValue) { + final long subsystemType = key & SUBSYSTEM_MASK; + final int subsystemFields = (int) (key & SUBSYSTEM_FIELDS_MASK); + + final double value; + if (subsystemType == SUBSYSTEM_MODEM) { + value = sModemPowerProfile.getAverageBatteryDrainMa(subsystemFields); + } else { + value = Double.NaN; + } + + if (Double.isNaN(value)) return defaultValue; + return value; + } + + /** + * Returns the average current in mA consumed by a subsystem's specified operation. + * + * @param key that describes a subsystem's battery draining operation + * The key is built from multiple constant, see {@link Subsystem} and + * {@link ModemPowerProfile}. + * @return the average current in milliAmps. + */ + public double getAverageBatteryDrainMa(long key) { + return getAverageBatteryDrainOrDefaultMa(key, 0); + } + /** * Returns the average current in mA consumed by the subsystem for the given level. * @@ -784,6 +917,25 @@ public class PowerProfile { PowerProfileProto.BATTERY_CAPACITY); } + /** + * Dump the PowerProfile values. + */ + public void dump(PrintWriter pw) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + sPowerItemMap.forEach((key, value) -> { + ipw.print(key, value); + ipw.println(); + }); + sPowerArrayMap.forEach((key, value) -> { + ipw.print(key, Arrays.toString(value)); + ipw.println(); + }); + ipw.println("Modem values:"); + ipw.increaseIndent(); + sModemPowerProfile.dump(ipw); + ipw.decreaseIndent(); + } + // Writes items in sPowerItemMap to proto if exists. private void writePowerConstantToProto(ProtoOutputStream proto, String key, long fieldId) { if (sPowerItemMap.containsKey(key)) { diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java new file mode 100644 index 0000000000000..456ff4ba8a268 --- /dev/null +++ b/core/java/com/android/internal/power/ModemPowerProfile.java @@ -0,0 +1,442 @@ +/* + * 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.internal.power; + +import android.annotation.IntDef; +import android.content.res.XmlResourceParser; +import android.telephony.ModemActivityInfo; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.Slog; +import android.util.SparseDoubleArray; + +import com.android.internal.telephony.util.ArrayUtils; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * ModemPowerProfile for handling the modem element in the power_profile.xml + */ +public class ModemPowerProfile { + private static final String TAG = "ModemPowerProfile"; + + private static final String TAG_SLEEP = "sleep"; + private static final String TAG_IDLE = "idle"; + private static final String TAG_ACTIVE = "active"; + private static final String TAG_RECEIVE = "receive"; + private static final String TAG_TRANSMIT = "transmit"; + private static final String ATTR_RAT = "rat"; + private static final String ATTR_NR_FREQUENCY = "nrFrequency"; + private static final String ATTR_LEVEL = "level"; + + /** + * A flattened list of the modem power constant extracted from the given XML parser. + * + * The bitfields of a key describes what its corresponding power constant represents: + * [31:28] - {@link ModemDrainType} (max count = 16). + * [27:24] - {@link ModemTxLevel} (only for {@link MODEM_DRAIN_TYPE_TX}) (max count = 16). + * [23:20] - {@link ModemRatType} (max count = 16). + * [19:16] - {@link ModemNrFrequencyRange} (only for {@link MODEM_RAT_TYPE_NR}) + * (max count = 16). + * [15:0] - RESERVED + */ + private final SparseDoubleArray mPowerConstants = new SparseDoubleArray(); + + private static final int MODEM_DRAIN_TYPE_SHIFT = 28; + private static final int MODEM_DRAIN_TYPE_MASK = 0xF << MODEM_DRAIN_TYPE_SHIFT; + + private static final int MODEM_TX_LEVEL_SHIFT = 24; + private static final int MODEM_TX_LEVEL_MASK = 0xF << MODEM_TX_LEVEL_SHIFT; + + private static final int MODEM_RAT_TYPE_SHIFT = 20; + private static final int MODEM_RAT_TYPE_MASK = 0xF << MODEM_RAT_TYPE_SHIFT; + + private static final int MODEM_NR_FREQUENCY_RANGE_SHIFT = 16; + private static final int MODEM_NR_FREQUENCY_RANGE_MASK = 0xF << MODEM_NR_FREQUENCY_RANGE_SHIFT; + + /** + * Corresponds to the overall modem battery drain while asleep. + */ + public static final int MODEM_DRAIN_TYPE_SLEEP = 0 << MODEM_DRAIN_TYPE_SHIFT; + + /** + * Corresponds to the overall modem battery drain while idle. + */ + public static final int MODEM_DRAIN_TYPE_IDLE = 1 << MODEM_DRAIN_TYPE_SHIFT; + + /** + * Corresponds to the modem battery drain while receiving data. A specific Rx battery drain + * power constant can be selected using a bitwise OR (|) with {@link ModemRatType} and + * {@link ModemNrFrequencyRange} (when applicable). + */ + public static final int MODEM_DRAIN_TYPE_RX = 2 << MODEM_DRAIN_TYPE_SHIFT; + + /** + * Corresponds to the modem battery drain while receiving data. + * {@link ModemTxLevel} must be specified with this drain type. + * Specific Tx battery drain power constanta can be selected using a bitwise OR (|) with + * {@link ModemRatType} and {@link ModemNrFrequencyRange} (when applicable). + */ + public static final int MODEM_DRAIN_TYPE_TX = 3 << MODEM_DRAIN_TYPE_SHIFT; + + @IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = { + MODEM_DRAIN_TYPE_SLEEP, + MODEM_DRAIN_TYPE_IDLE, + MODEM_DRAIN_TYPE_RX, + MODEM_DRAIN_TYPE_TX, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModemDrainType { + } + + private static final String[] MODEM_DRAIN_TYPE_NAMES = + new String[]{"SLEEP", "IDLE", "RX", "TX"}; + + /** + * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_0}. + */ + public static final int MODEM_TX_LEVEL_0 = 0 << MODEM_TX_LEVEL_SHIFT; + + /** + * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_1}. + */ + public static final int MODEM_TX_LEVEL_1 = 1 << MODEM_TX_LEVEL_SHIFT; + + /** + * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_2}. + */ + public static final int MODEM_TX_LEVEL_2 = 2 << MODEM_TX_LEVEL_SHIFT; + + /** + * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_3}. + */ + public static final int MODEM_TX_LEVEL_3 = 3 << MODEM_TX_LEVEL_SHIFT; + + /** + * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_4}. + */ + public static final int MODEM_TX_LEVEL_4 = 4 << MODEM_TX_LEVEL_SHIFT; + + private static final int MODEM_TX_LEVEL_COUNT = 5; + + @IntDef(prefix = {"MODEM_TX_LEVEL_"}, value = { + MODEM_TX_LEVEL_0, + MODEM_TX_LEVEL_1, + MODEM_TX_LEVEL_2, + MODEM_TX_LEVEL_3, + MODEM_TX_LEVEL_4, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModemTxLevel { + } + + /** + * Fallback for any active modem usage that does not match specified Radio Access Technology + * (RAT) power constants. + */ + public static final int MODEM_RAT_TYPE_DEFAULT = 0 << MODEM_RAT_TYPE_SHIFT; + + /** + * Corresponds to active modem usage on 4G {@link TelephonyManager#NETWORK_TYPE_LTE} RAT. + */ + public static final int MODEM_RAT_TYPE_LTE = 1 << MODEM_RAT_TYPE_SHIFT; + + /** + * Corresponds to active modem usage on 5G {@link TelephonyManager#NETWORK_TYPE_NR} RAT. + */ + public static final int MODEM_RAT_TYPE_NR = 2 << MODEM_RAT_TYPE_SHIFT; + + @IntDef(prefix = {"MODEM_RAT_TYPE_"}, value = { + MODEM_RAT_TYPE_DEFAULT, + MODEM_RAT_TYPE_LTE, + MODEM_RAT_TYPE_NR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModemRatType { + } + + private static final String[] MODEM_RAT_TYPE_NAMES = new String[]{"DEFAULT", "LTE", "NR"}; + + /** + * Fallback for any active 5G modem usage that does not match specified NR frequency power + * constants. + */ + public static final int MODEM_NR_FREQUENCY_RANGE_DEFAULT = 0 << MODEM_NR_FREQUENCY_RANGE_SHIFT; + + /** + * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_LOW}. + */ + public static final int MODEM_NR_FREQUENCY_RANGE_LOW = 1 << MODEM_NR_FREQUENCY_RANGE_SHIFT; + + /** + * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MID}. + */ + public static final int MODEM_NR_FREQUENCY_RANGE_MID = 2 << MODEM_NR_FREQUENCY_RANGE_SHIFT; + + /** + * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_HIGH}. + */ + public static final int MODEM_NR_FREQUENCY_RANGE_HIGH = 3 << MODEM_NR_FREQUENCY_RANGE_SHIFT; + + /** + * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MMWAVE}. + */ + public static final int MODEM_NR_FREQUENCY_RANGE_MMWAVE = 4 << MODEM_NR_FREQUENCY_RANGE_SHIFT; + + @IntDef(prefix = {"MODEM_NR_FREQUENCY_RANGE_"}, value = { + MODEM_RAT_TYPE_DEFAULT, + MODEM_NR_FREQUENCY_RANGE_LOW, + MODEM_NR_FREQUENCY_RANGE_MID, + MODEM_NR_FREQUENCY_RANGE_HIGH, + MODEM_NR_FREQUENCY_RANGE_MMWAVE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModemNrFrequencyRange { + } + + private static final String[] MODEM_NR_FREQUENCY_RANGE_NAMES = + new String[]{"DEFAULT", "LOW", "MID", "HIGH", "MMWAVE"}; + + public ModemPowerProfile() { + } + + /** + * Generates a ModemPowerProfile object from the element of a power_profile.xml + */ + public void parseFromXml(XmlResourceParser parser) throws IOException, + XmlPullParserException { + final int depth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, depth)) { + final String name = parser.getName(); + switch (name) { + case TAG_SLEEP: + if (parser.next() != XmlPullParser.TEXT) { + continue; + } + final String sleepDrain = parser.getText(); + setPowerConstant(MODEM_DRAIN_TYPE_SLEEP, sleepDrain); + break; + case TAG_IDLE: + if (parser.next() != XmlPullParser.TEXT) { + continue; + } + final String idleDrain = parser.getText(); + setPowerConstant(MODEM_DRAIN_TYPE_IDLE, idleDrain); + break; + case TAG_ACTIVE: + parseActivePowerConstantsFromXml(parser); + break; + default: + Slog.e(TAG, "Unexpected element parsed: " + name); + } + } + } + + /** Parse the XML element */ + private void parseActivePowerConstantsFromXml(XmlResourceParser parser) + throws IOException, XmlPullParserException { + // Parse attributes to get the type of active modem usage the power constants are for. + final int ratType; + final int nrfType; + try { + ratType = getTypeFromAttribute(parser, ATTR_RAT, MODEM_RAT_TYPE_SHIFT, + MODEM_RAT_TYPE_NAMES); + if (ratType == MODEM_RAT_TYPE_NR) { + nrfType = getTypeFromAttribute(parser, ATTR_NR_FREQUENCY, + MODEM_NR_FREQUENCY_RANGE_SHIFT, MODEM_NR_FREQUENCY_RANGE_NAMES); + } else { + nrfType = 0; + } + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed parse to active modem power constants", iae); + return; + } + + // Parse and populate the active modem use power constants. + final int depth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, depth)) { + final String name = parser.getName(); + switch (name) { + case TAG_RECEIVE: + if (parser.next() != XmlPullParser.TEXT) { + continue; + } + final String rxDrain = parser.getText(); + final int rxKey = MODEM_DRAIN_TYPE_RX | ratType | nrfType; + setPowerConstant(rxKey, rxDrain); + break; + case TAG_TRANSMIT: + final int level = XmlUtils.readIntAttribute(parser, ATTR_LEVEL, -1); + if (parser.next() != XmlPullParser.TEXT) { + continue; + } + final String txDrain = parser.getText(); + if (level < 0 || level >= MODEM_TX_LEVEL_COUNT) { + Slog.e(TAG, + "Unexpected tx level: " + level + ". Must be between 0 and " + ( + MODEM_TX_LEVEL_COUNT - 1)); + continue; + } + final int modemTxLevel = level << MODEM_TX_LEVEL_SHIFT; + Slog.d("MWACHENS", + "parsing tx at level:" + level + ", aka 0x" + Integer.toHexString( + modemTxLevel)); + final int txKey = MODEM_DRAIN_TYPE_TX | modemTxLevel | ratType | nrfType; + setPowerConstant(txKey, txDrain); + break; + default: + Slog.e(TAG, "Unexpected element parsed: " + name); + } + } + } + + private static int getTypeFromAttribute(XmlResourceParser parser, String attr, int shift, + String[] names) { + final String value = XmlUtils.readStringAttribute(parser, attr); + final int index = ArrayUtils.indexOf(names, value); + if (value == null) { + // Attribute was not specified, just use the default. + return 0; + } + if (index < 0) { + throw new IllegalArgumentException( + "Unexpected " + attr + " value : " + value + ". Acceptable values are " + + Arrays.toString(names)); + } + return index << shift; + } + + /** + * Set the average battery drain in milli-amps of the modem for a given drain type. + * + * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel}, + * {@link ModemRatType}, and {@link ModemNrFrequencyRange}.key + * @param value the battery dram in milli-amps for the given key. + */ + public void setPowerConstant(int key, String value) { + try { + mPowerConstants.put(key, Double.valueOf(value)); + } catch (Exception e) { + Slog.e(TAG, "Failed to set power constant 0x" + Integer.toHexString( + key) + "(" + keyToString(key) + ") to " + value, e); + } + } + + /** + * 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. + * + * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel}, + * {@link ModemRatType}, and {@link ModemNrFrequencyRange}. + */ + public double getAverageBatteryDrainMa(int key) { + int bestKey = key; + double value; + value = mPowerConstants.get(bestKey, Double.NaN); + if (!Double.isNaN(value)) return value; + // The power constant for given key was not explicitly set. Try to fallback to possible + // defaults. + + if ((bestKey & MODEM_NR_FREQUENCY_RANGE_MASK) != MODEM_NR_FREQUENCY_RANGE_DEFAULT) { + // Fallback to NR Frequency default value + bestKey &= ~MODEM_NR_FREQUENCY_RANGE_MASK; + bestKey |= MODEM_NR_FREQUENCY_RANGE_DEFAULT; + value = mPowerConstants.get(bestKey, Double.NaN); + if (!Double.isNaN(value)) return value; + } + + if ((bestKey & MODEM_RAT_TYPE_MASK) != MODEM_RAT_TYPE_DEFAULT) { + // Fallback to RAT default value + bestKey &= ~MODEM_RAT_TYPE_MASK; + bestKey |= MODEM_RAT_TYPE_DEFAULT; + value = mPowerConstants.get(bestKey, Double.NaN); + if (!Double.isNaN(value)) return value; + } + + Slog.w(TAG, + "getAverageBatteryDrainMaH called with unexpected key: 0x" + Integer.toHexString( + key) + ", " + keyToString(key)); + return Double.NaN; + } + + private static String keyToString(int key) { + StringBuilder sb = new StringBuilder(); + final int drainType = key & MODEM_DRAIN_TYPE_MASK; + appendFieldToString(sb, "drain", MODEM_DRAIN_TYPE_NAMES, + drainType >> MODEM_DRAIN_TYPE_SHIFT); + sb.append(","); + + if (drainType == MODEM_DRAIN_TYPE_TX) { + final int txLevel = (key & MODEM_TX_LEVEL_MASK) >> MODEM_TX_LEVEL_SHIFT; + sb.append("level:"); + sb.append(txLevel); + sb.append(","); + } + + final int ratType = key & MODEM_RAT_TYPE_MASK; + appendFieldToString(sb, "RAT", MODEM_RAT_TYPE_NAMES, ratType >> MODEM_RAT_TYPE_SHIFT); + + if (ratType == MODEM_RAT_TYPE_NR) { + sb.append(","); + final int nrFreq = key & MODEM_NR_FREQUENCY_RANGE_MASK; + appendFieldToString(sb, "nrFreq", MODEM_NR_FREQUENCY_RANGE_NAMES, + nrFreq >> MODEM_NR_FREQUENCY_RANGE_SHIFT); + } + return sb.toString(); + } + + private static void appendFieldToString(StringBuilder sb, String fieldName, String[] names, + int index) { + sb.append(fieldName); + sb.append(":"); + if (index < 0 || index >= names.length) { + sb.append("UNKNOWN("); + sb.append(index); + sb.append(")"); + } else { + sb.append(names[index]); + } + } + + /** + * Clear this ModemPowerProfile power constants. + */ + public void clear() { + mPowerConstants.clear(); + } + + + /** + * Dump this ModemPowerProfile power constants. + */ + public void dump(PrintWriter pw) { + final int size = mPowerConstants.size(); + for (int i = 0; i < size; i++) { + pw.print(keyToString(mPowerConstants.keyAt(i))); + pw.print("="); + pw.println(mPowerConstants.valueAt(i)); + } + } +} diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index d310736ae1215..fc63657f04d07 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -144,17 +144,49 @@ 2 - - 0 - 0 - 0 - - 0 - 0 - 0 - 0 - 0 - + + + + 0 + + 0 + + + + 0 + + + 0 + 0 + 0 + 0 + 0 + + + 0 @@ -163,5 +195,4 @@ 0 0 - diff --git a/core/tests/coretests/res/xml/power_profile_test_modem.xml b/core/tests/coretests/res/xml/power_profile_test_modem.xml new file mode 100644 index 0000000000000..ff36a9c94e0b1 --- /dev/null +++ b/core/tests/coretests/res/xml/power_profile_test_modem.xml @@ -0,0 +1,151 @@ + + + + + + + 10 + + 20 + + + 30 + + 40 + 50 + 60 + 70 + 80 + + + + + + 1 + + 2 + + + 3 + + 4 + 5 + 6 + 7 + 8 + + + + 13 + + 14 + 15 + 16 + 17 + 18 + + + + 53 + + 54 + 55 + 56 + 57 + 58 + + + + + + 1 + + 2 + + + 3 + + 4 + 5 + 6 + 7 + 8 + + + + 10 + + 20 + 30 + 40 + 50 + 60 + + + + 13 + + 14 + 15 + 16 + 17 + 18 + + + + 23 + + 24 + 25 + 26 + 27 + 28 + + + + 33 + + 34 + 35 + 36 + 37 + 38 + + + + 43 + + 44 + 45 + 46 + 47 + 48 + + + + 53 + + 54 + 55 + 56 + 57 + 58 + + + 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 88ee405483dba..1efd78bc13fc9 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -21,25 +21,43 @@ 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 android.annotation.XmlRes; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; + import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.frameworks.coretests.R; +import com.android.internal.power.ModemPowerProfile; +import com.android.internal.util.XmlUtils; + import junit.framework.TestCase; import org.junit.Before; import org.junit.Test; /* - * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml + * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml and + * frameworks/base/core/tests/coretests/res/xml/power_profile_test_modem.xml + * + * Run with: + * atest com.android.internal.os.PowerProfileTest */ @SmallTest public class PowerProfileTest extends TestCase { + static final String TAG_TEST_MODEM = "test-modem"; + static final String ATTR_NAME = "name"; + private PowerProfile mProfile; + private Context mContext; @Before public void setUp() { - mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true); + mContext = InstrumentationRegistry.getContext(); + mProfile = new PowerProfile(mContext, true); } @Test @@ -67,4 +85,396 @@ public class PowerProfileTest extends TestCase { assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO)); } + @Test + public void testModemPowerProfile_defaultRat() throws Exception { + final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem, + "testModemPowerProfile_defaultRat"); + ModemPowerProfile mpp = new ModemPowerProfile(); + mpp.parseFromXml(parser); + assertEquals(10.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP)); + assertEquals(20.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE)); + + // Only default RAT was defined, all other RAT's should fallback to the default value. + assertEquals(30.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(30.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(30.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + + assertEquals(40.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(40.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(40.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + + assertEquals(50.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(50.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(50.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + + assertEquals(60.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(60.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(60.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + + assertEquals(70.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(70.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(70.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + + assertEquals(80.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + assertEquals(80.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + assertEquals(80.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + } + + @Test + public void testModemPowerProfile_partiallyDefined() throws Exception { + final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem, + "testModemPowerProfile_partiallyDefined"); + ModemPowerProfile mpp = new ModemPowerProfile(); + mpp.parseFromXml(parser); + assertEquals(1.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP)); + assertEquals(2.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE)); + + assertEquals(3.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(4.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(5.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(6.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(7.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(8.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + // LTE RAT power constants were not defined, fallback to defaults + assertEquals(3.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(4.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(5.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(6.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(7.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(8.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + // Non-mmwave NR frequency power constants were not defined, fallback to defaults + assertEquals(13.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(14.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(15.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(16.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(17.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(18.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(13.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(14.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(15.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(16.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(17.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(18.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(53.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(54.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(55.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(56.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(57.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(58.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + } + + @Test + public void testModemPowerProfile_fullyDefined() throws Exception { + final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem, + "testModemPowerProfile_fullyDefined"); + ModemPowerProfile mpp = new ModemPowerProfile(); + mpp.parseFromXml(parser); + assertEquals(1.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP)); + assertEquals(2.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE)); + + // Only default RAT was defined, all other RAT's should fallback to the default value. + assertEquals(3.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(4.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(5.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(6.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(7.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(8.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(10.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(20.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(30.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(40.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(50.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(60.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(23.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(24.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(25.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(26.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(27.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(28.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(33.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(34.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(35.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(36.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(37.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(38.0, mpp.getAverageBatteryDrainMa( + ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX + | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(43.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(44.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(45.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(46.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(47.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(48.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + + assertEquals(53.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_RX)); + assertEquals(54.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0)); + assertEquals(55.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1)); + assertEquals(56.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2)); + assertEquals(57.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3)); + assertEquals(58.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR + | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE + | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4)); + } + + private XmlResourceParser getTestModemElement(@XmlRes int xmlId, String elementName) + throws Exception { + 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(); + if (e == null) break; + if (!e.equals(element)) continue; + + final String name = parser.getAttributeValue(null, ATTR_NAME); + if (!name.equals(elementName)) continue; + + return parser; + } + fail("Unanable to find element " + element + " with name " + elementName); + return null; + } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 8cb20404b3e16..555a3963b72e8 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -2227,6 +2227,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub pw.println(" --read-daily: read-load last written daily stats."); pw.println(" --settings: dump the settings key/values related to batterystats"); pw.println(" --cpu: dump cpu stats for debugging purpose"); + pw.println(" --power-profile: dump the power profile constants"); pw.println(" : optional name of package to filter output by."); pw.println(" -h: print this help text."); pw.println("Battery stats (batterystats) commands:"); @@ -2264,6 +2265,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + private void dumpPowerProfile(PrintWriter pw) { + synchronized (mStats) { + mStats.dumpPowerProfileLocked(pw); + } + } + private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) { i++; if (i >= args.length) { @@ -2406,6 +2413,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub } else if ("--measured-energy".equals(arg)) { dumpMeasuredEnergyStats(pw); return; + } else if ("--power-profile".equals(arg)) { + dumpPowerProfile(pw); + return; } else if ("-a".equals(arg)) { flags |= BatteryStats.DUMP_VERBOSE; } else if (arg.length() > 0 && arg.charAt(0) == '-'){