diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java new file mode 100644 index 0000000000000..9c3bb761b9f7f --- /dev/null +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.BatteryStats; +import android.os.UserHandle; +import android.util.SparseArray; + +import java.util.List; + +/** + * Estimates power consumed by the ambient display + */ +public class AmbientDisplayPowerCalculator extends PowerCalculator { + + private final PowerProfile mPowerProfile; + + public AmbientDisplayPowerCalculator(PowerProfile powerProfile) { + mPowerProfile = powerProfile; + } + + /** + * Ambient display power is the additional power the screen takes while in ambient display/ + * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should + * be hidden {@link BatteryStatsHelper#shouldHideSipper(BatterySipper)}, but should not be + * included in smearing {@link BatteryStatsHelper#removeHiddenBatterySippers(List)}. + */ + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + + long ambientDisplayMs = batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000; + double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY) + * ambientDisplayMs / (60 * 60 * 1000); + if (power > 0) { + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0); + bs.usagePowerMah = power; + bs.usageTimeMs = ambientDisplayMs; + bs.sumPower(); + sippers.add(bs); + } + } +} diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index 5337ac3cc27e7..c8805ddd3c433 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -132,6 +132,10 @@ public class BatterySipper implements Comparable { public double wifiPowerMah; public double systemServiceCpuPowerMah; + // Do not include this sipper in results because it is included + // in an aggregate sipper. + public boolean isAggregated; + // **************** // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto) // so the ordinal values (and therefore the order) must never change. diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 92dd4dda39df0..267674592b02b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -54,9 +54,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.Locale; /** * A helper class for retrieving the power usage information for all applications and services. @@ -77,6 +75,8 @@ public class BatteryStatsHelper { final private boolean mCollectBatteryBroadcast; final private boolean mWifiOnly; + private List mPowerCalculators; + @UnsupportedAppUsage private IBatteryStats mBatteryInfo; private BatteryStats mStats; @@ -94,18 +94,6 @@ public class BatteryStatsHelper { @UnsupportedAppUsage private final List mUsageList = new ArrayList<>(); - /** - * List of apps using wifi power. - */ - private final List mWifiSippers = new ArrayList<>(); - - /** - * List of apps using bluetooth power. - */ - private final List mBluetoothSippers = new ArrayList<>(); - - private final SparseArray> mUserSippers = new SparseArray<>(); - private final List mMobilemsppList = new ArrayList<>(); private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; @@ -114,8 +102,6 @@ public class BatteryStatsHelper { long mRawUptimeUs; long mBatteryRealtimeUs; long mBatteryUptimeUs; - long mTypeBatteryRealtimeUs; - long mTypeBatteryUptimeUs; long mBatteryTimeRemainingUs; long mChargeTimeRemainingUs; @@ -133,21 +119,6 @@ public class BatteryStatsHelper { private double mMinDrainedPower; private double mMaxDrainedPower; - PowerCalculator mCpuPowerCalculator; - SystemServicePowerCalculator mSystemServicePowerCalculator; - PowerCalculator mWakelockPowerCalculator; - MobileRadioPowerCalculator mMobileRadioPowerCalculator; - PowerCalculator mWifiPowerCalculator; - PowerCalculator mBluetoothPowerCalculator; - PowerCalculator mSensorPowerCalculator; - PowerCalculator mCameraPowerCalculator; - PowerCalculator mFlashlightPowerCalculator; - PowerCalculator mMemoryPowerCalculator; - PowerCalculator mMediaPowerCalculator; - - boolean mHasWifiPowerReporting = false; - boolean mHasBluetoothPowerReporting = false; - public static boolean checkWifiOnly(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); @@ -157,21 +128,6 @@ public class BatteryStatsHelper { return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); } - public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) { - return stats.hasWifiActivityReporting() && - profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 && - profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 && - profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0; - } - - public static boolean checkHasBluetoothPowerReporting(BatteryStats stats, - PowerProfile profile) { - return stats.hasBluetoothActivityReporting() && - profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 && - profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 && - profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0; - } - @UnsupportedAppUsage public BatteryStatsHelper(Context context) { this(context, true); @@ -308,31 +264,7 @@ public class BatteryStatsHelper { } public static String makemAh(double power) { - if (power == 0) return "0"; - - final String format; - if (power < .00001) { - format = "%.8f"; - } else if (power < .0001) { - format = "%.7f"; - } else if (power < .001) { - format = "%.6f"; - } else if (power < .01) { - format = "%.5f"; - } else if (power < .1) { - format = "%.4f"; - } else if (power < 1) { - format = "%.3f"; - } else if (power < 10) { - format = "%.2f"; - } else if (power < 100) { - format = "%.1f"; - } else { - format = "%.0f"; - } - - // Use English locale because this is never used in UI (only in checkin and dump). - return String.format(Locale.ENGLISH, format, power); + return PowerCalculator.formatCharge(power); } /** @@ -384,97 +316,58 @@ public class BatteryStatsHelper { mTotalPower = 0; mUsageList.clear(); - mWifiSippers.clear(); - mBluetoothSippers.clear(); - mUserSippers.clear(); mMobilemsppList.clear(); if (mStats == null) { return; } - if (mCpuPowerCalculator == null) { - mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); - } - mCpuPowerCalculator.reset(); + if (mPowerCalculators == null) { + mPowerCalculators = new ArrayList<>(); - if (mSystemServicePowerCalculator == null) { - mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats); - } - mSystemServicePowerCalculator.reset(); + // Power calculators are applied in the order of registration + mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile)); + if (!mWifiOnly) { + mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile)); + } + mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new SensorPowerCalculator(mPowerProfile, + mContext.getSystemService(SensorManager.class))); + mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new MediaPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile)); + mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile)); + mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile)); - if (mMemoryPowerCalculator == null) { - mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile); + mPowerCalculators.add(new UserPowerCalculator()); } - mMemoryPowerCalculator.reset(); - if (mWakelockPowerCalculator == null) { - mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile); + for (int i = 0, size = mPowerCalculators.size(); i < size; i++) { + mPowerCalculators.get(i).reset(); } - mWakelockPowerCalculator.reset(); - - if (mMobileRadioPowerCalculator == null) { - mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats); - } - mMobileRadioPowerCalculator.reset(mStats); - - // checkHasWifiPowerReporting can change if we get energy data at a later point, so - // always check this field. - final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile); - if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) { - mWifiPowerCalculator = hasWifiPowerReporting ? - new WifiPowerCalculator(mPowerProfile) : - new WifiPowerEstimator(mPowerProfile); - mHasWifiPowerReporting = hasWifiPowerReporting; - } - mWifiPowerCalculator.reset(); - - final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats, - mPowerProfile); - if (mBluetoothPowerCalculator == null || - hasBluetoothPowerReporting != mHasBluetoothPowerReporting) { - mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile); - mHasBluetoothPowerReporting = hasBluetoothPowerReporting; - } - mBluetoothPowerCalculator.reset(); - - mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile, - (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE), - mStats, rawRealtimeUs, statsType); - mSensorPowerCalculator.reset(); - - if (mCameraPowerCalculator == null) { - mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile); - } - mCameraPowerCalculator.reset(); - - if (mFlashlightPowerCalculator == null) { - mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile); - } - mFlashlightPowerCalculator.reset(); - - if (mMediaPowerCalculator == null) { - mMediaPowerCalculator = new MediaPowerCalculator(mPowerProfile); - } - mMediaPowerCalculator.reset(); mStatsType = statsType; mRawUptimeUs = rawUptimeUs; mRawRealtimeUs = rawRealtimeUs; mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs); mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs); - mTypeBatteryUptimeUs = mStats.computeBatteryUptime(rawUptimeUs, mStatsType); - mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs); mChargeTimeRemainingUs = mStats.computeChargeTimeRemaining(rawRealtimeUs); + mStatsPeriod = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType); if (DEBUG) { Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs / 1000) + " uptime=" + (rawUptimeUs / 1000)); Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtimeUs / 1000) + " uptime=" + (mBatteryUptimeUs / 1000)); - Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtimeUs / 1000) + " uptime=" - + (mTypeBatteryUptimeUs / 1000)); + Log.d(TAG, "Battery type time: realtime=" + (mStatsPeriod / 1000) + " uptime=" + + (mStats.computeBatteryUptime(rawRealtimeUs, mStatsType) / 1000)); } mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; @@ -483,36 +376,11 @@ public class BatteryStatsHelper { processAppUsage(asUsers); - // Before aggregating apps in to users, collect all apps to sort by their ms per packet. - for (int i = 0; i < mUsageList.size(); i++) { - BatterySipper bs = mUsageList.get(i); - bs.computeMobilemspp(); - if (bs.mobilemspp != 0) { - mMobilemsppList.add(bs); - } - } - - for (int i = 0; i < mUserSippers.size(); i++) { - List user = mUserSippers.valueAt(i); - for (int j = 0; j < user.size(); j++) { - BatterySipper bs = user.get(j); - bs.computeMobilemspp(); - if (bs.mobilemspp != 0) { - mMobilemsppList.add(bs); - } - } - } - Collections.sort(mMobilemsppList, new Comparator() { - @Override - public int compare(BatterySipper lhs, BatterySipper rhs) { - return Double.compare(rhs.mobilemspp, lhs.mobilemspp); - } - }); - - processMiscUsage(); - Collections.sort(mUsageList); + Collections.sort(mMobilemsppList, + (lhs, rhs) -> Double.compare(rhs.mobilemspp, lhs.mobilemspp)); + // At this point, we've sorted the list so we are guaranteed the max values are at the top. // We have only added real powers so far. if (!mUsageList.isEmpty()) { @@ -524,8 +392,9 @@ public class BatteryStatsHelper { } if (DEBUG) { - Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge=" - + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower)); + Log.d(TAG, "Accuracy: total computed=" + PowerCalculator.formatCharge(mComputedPower) + + ", min discharge=" + PowerCalculator.formatCharge(mMinDrainedPower) + + ", max discharge=" + PowerCalculator.formatCharge(mMaxDrainedPower)); } mTotalPower = mComputedPower; @@ -573,244 +442,43 @@ public class BatteryStatsHelper { } private void processAppUsage(SparseArray asUsers) { - final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); - mStatsPeriod = mTypeBatteryRealtimeUs; - - BatterySipper osSipper = null; final SparseArray uidStats = mStats.getUidStats(); - final int NU = uidStats.size(); - for (int iu = 0; iu < NU; iu++) { + + final ArrayList sippers = new ArrayList<>(uidStats.size()); + + for (int iu = 0, size = uidStats.size(); iu < size; iu++) { final Uid u = uidStats.valueAt(iu); - final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0); + sippers.add(new BatterySipper(DrainType.APP, u, 0)); + } - mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); - mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); - mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); - mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); - mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); - mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); - mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, - mStatsType); + for (int i = 0, size = mPowerCalculators.size(); i < size; i++) { + final PowerCalculator calculator = mPowerCalculators.get(i); + calculator.calculate(sippers, mStats, mRawRealtimeUs, mRawUptimeUs, mStatsType, + asUsers); + } - final double totalPower = app.sumPower(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper sipper = sippers.get(i); + final double totalPower = sipper.sumPower(); if (DEBUG && totalPower != 0) { - Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(), - makemAh(totalPower))); + Log.d(TAG, String.format("UID %d: total power=%s", sipper.getUid(), + PowerCalculator.formatCharge(totalPower))); } - // Add the app to the list if it is consuming power. - if (totalPower != 0 || u.getUid() == 0) { - // - // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list. - // - final int uid = app.getUid(); - final int userId = UserHandle.getUserId(uid); - if (uid == Process.WIFI_UID) { - mWifiSippers.add(app); - } else if (uid == Process.BLUETOOTH_UID) { - mBluetoothSippers.add(app); - } else if (!forAllUsers && asUsers.get(userId) == null - && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) { - // We are told to just report this user's apps as one large entry. - List list = mUserSippers.get(userId); - if (list == null) { - list = new ArrayList<>(); - mUserSippers.put(userId, list); + // Add the sipper to the list if it is consuming power. + if (totalPower != 0 || sipper.getUid() == 0) { + if (sipper.drainType == DrainType.APP) { + sipper.computeMobilemspp(); + if (sipper.mobilemspp != 0) { + mMobilemsppList.add(sipper); } - list.add(app); - } else { - mUsageList.add(app); } - if (uid == 0) { - osSipper = app; + if (!sipper.isAggregated) { + mUsageList.add(sipper); } } } - - if (osSipper != null) { - // The device has probably been awake for longer than the screen on - // time and application wake lock time would account for. Assign - // this remainder to the OS, if possible. - mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs, - mRawUptimeUs, mStatsType); - osSipper.sumPower(); - } - } - - private void addPhoneUsage() { - long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000; - double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - * phoneOnTimeMs / (60 * 60 * 1000); - if (phoneOnPower != 0) { - addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower); - } - } - - /** - * Screen power is the additional power the screen takes while the device is running. - */ - private void addScreenUsage() { - double power = 0; - long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000; - power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); - final double screenFullPower = - mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); - for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { - double screenBinPower = screenFullPower * (i + 0.5f) - / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; - long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType) - / 1000; - double p = screenBinPower * brightnessTime; - if (DEBUG && p != 0) { - Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime - + " power=" + makemAh(p / (60 * 60 * 1000))); - } - power += p; - } - power /= (60 * 60 * 1000); // To hours - if (power != 0) { - addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power); - } - } - - /** - * Ambient display power is the additional power the screen takes while in ambient display/ - * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should - * be hidden {@link #shouldHideSipper(BatterySipper)}, but should not be included in smearing - * {@link #removeHiddenBatterySippers(List)}. - */ - private void addAmbientDisplayUsage() { - long ambientDisplayMs = mStats.getScreenDozeTime(mRawRealtimeUs, mStatsType) / 1000; - double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY) - * ambientDisplayMs / (60 * 60 * 1000); - if (power > 0) { - addEntry(DrainType.AMBIENT_DISPLAY, ambientDisplayMs, power); - } - } - - private void addRadioUsage() { - BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); - mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - radio.sumPower(); - if (radio.totalPowerMah > 0) { - mUsageList.add(radio); - } - } - - private void aggregateSippers(BatterySipper bs, List from, String tag) { - for (int i = 0; i < from.size(); i++) { - BatterySipper wbs = from.get(i); - if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs); - bs.add(wbs); - } - bs.computeMobilemspp(); - bs.sumPower(); - } - - /** - * Calculate the baseline power usage for the device when it is in suspend and idle. - * The device is drawing POWER_CPU_SUSPEND power at its lowest power state. - * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held. - */ - private void addIdleUsage() { - final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) * - mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND); - final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) * - mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); - final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); - if (DEBUG && totalPowerMah != 0) { - Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000) - + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000))); - Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000) - + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000))); - } - - if (totalPowerMah != 0) { - addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah); - } - } - - /** - * We do per-app blaming of WiFi activity. If energy info is reported from the controller, - * then only the WiFi process gets blamed here since we normalize power calculations and - * assign all the power drain to apps. If energy info is not reported, we attribute the - * difference between total running time of WiFi for all apps and the actual running time - * of WiFi to the WiFi subsystem. - */ - private void addWiFiUsage() { - BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0); - mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - aggregateSippers(bs, mWifiSippers, "WIFI"); - if (bs.totalPowerMah > 0) { - mUsageList.add(bs); - } - } - - /** - * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the - * Bluetooth Category. - */ - private void addBluetoothUsage() { - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); - mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); - if (bs.totalPowerMah > 0) { - mUsageList.add(bs); - } - } - - private void addUserUsage() { - for (int i = 0; i < mUserSippers.size(); i++) { - final int userId = mUserSippers.keyAt(i); - BatterySipper bs = new BatterySipper(DrainType.USER, null, 0); - bs.userId = userId; - aggregateSippers(bs, mUserSippers.valueAt(i), "User"); - mUsageList.add(bs); - } - } - - private void addMemoryUsage() { - BatterySipper memory = new BatterySipper(DrainType.MEMORY, null, 0); - mMemoryPowerCalculator.calculateRemaining(memory, mStats, mRawRealtimeUs, mRawUptimeUs, - mStatsType); - memory.sumPower(); - if (memory.totalPowerMah > 0) { - mUsageList.add(memory); - } - } - - private void processMiscUsage() { - addUserUsage(); - addPhoneUsage(); - addScreenUsage(); - addAmbientDisplayUsage(); - addWiFiUsage(); - addBluetoothUsage(); - addMemoryUsage(); - addIdleUsage(); // Not including cellular idle power - // Don't compute radio usage if it's a wifi-only device - if (!mWifiOnly) { - addRadioUsage(); - } - } - - private BatterySipper addEntry(DrainType drainType, long time, double power) { - BatterySipper bs = new BatterySipper(drainType, null, 0); - bs.usagePowerMah = power; - bs.usageTimeMs = time; - bs.sumPower(); - mUsageList.add(bs); - return bs; } @UnsupportedAppUsage diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 2f383eacbac02..11099bee576eb 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -16,14 +16,20 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.Process; +import android.os.UserHandle; import android.util.Log; +import android.util.SparseArray; + +import java.util.List; public class BluetoothPowerCalculator extends PowerCalculator { private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private static final String TAG = "BluetoothPowerCalculator"; + private static final String TAG = "BluetoothPowerCalc"; private final double mIdleMa; private final double mRxMa; private final double mTxMa; + private final boolean mHasBluetoothPowerController; private double mAppTotalPowerMah = 0; private long mAppTotalTimeMs = 0; @@ -31,10 +37,36 @@ public class BluetoothPowerCalculator extends PowerCalculator { mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE); mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX); mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX); + mHasBluetoothPowerController = mIdleMa != 0 && mRxMa != 0 && mTxMa != 0; } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) { + return; + } + + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); + calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + + for (int i = sippers.size() - 1; i >= 0; i--) { + BatterySipper app = sippers.get(i); + if (app.getUid() == Process.BLUETOOTH_UID) { + if (DEBUG) Log.d(TAG, "Bluetooth adding sipper " + app + ": cpu=" + app.cpuTimeMs); + app.isAggregated = true; + bs.add(app); + } + } + if (bs.sumPower() > 0) { + sippers.add(bs); + } + } + + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { final BatteryStats.ControllerActivityCounter counter = u.getBluetoothControllerActivity(); @@ -63,8 +95,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { mAppTotalTimeMs += totalTimeMs; } - @Override - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, + private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) { final BatteryStats.ControllerActivityCounter counter = stats.getBluetoothControllerActivity(); @@ -87,7 +118,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { if (DEBUG && powerMah != 0) { Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs) - + " power=" + BatteryStatsHelper.makemAh(powerMah)); + + " power=" + formatCharge(powerMah)); } app.bluetoothPowerMah = powerMah; diff --git a/core/java/com/android/internal/os/CameraPowerCalculator.java b/core/java/com/android/internal/os/CameraPowerCalculator.java index 3273080498e9a..0365d9e9d6006 100644 --- a/core/java/com/android/internal/os/CameraPowerCalculator.java +++ b/core/java/com/android/internal/os/CameraPowerCalculator.java @@ -30,7 +30,7 @@ public class CameraPowerCalculator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // Calculate camera power usage. Right now, this is a (very) rough estimate based on the diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 101c321ab2c43..dac62a6786659 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -30,7 +30,7 @@ public class CpuPowerCalculator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; final int numClusters = mProfile.getNumCpuClusters(); @@ -45,7 +45,7 @@ public class CpuPowerCalculator extends PowerCalculator { if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + speed + " timeUs=" + timeUs + " power=" - + BatteryStatsHelper.makemAh(cpuSpeedStepPower / MICROSEC_IN_HR)); + + formatCharge(cpuSpeedStepPower / MICROSEC_IN_HR)); } cpuPowerMaUs += cpuSpeedStepPower; } @@ -62,7 +62,7 @@ public class CpuPowerCalculator extends PowerCalculator { if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs=" + cpuClusterTimes[i] + " power=" - + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR)); + + formatCharge(power / MICROSEC_IN_HR)); } } } else { @@ -74,7 +74,7 @@ public class CpuPowerCalculator extends PowerCalculator { if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) { Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power=" - + BatteryStatsHelper.makemAh(app.cpuPowerMah)); + + formatCharge(app.cpuPowerMah)); } // Keep track of the package with highest drain. diff --git a/core/java/com/android/internal/os/FlashlightPowerCalculator.java b/core/java/com/android/internal/os/FlashlightPowerCalculator.java index fef66ff84428b..330feef8f1179 100644 --- a/core/java/com/android/internal/os/FlashlightPowerCalculator.java +++ b/core/java/com/android/internal/os/FlashlightPowerCalculator.java @@ -28,7 +28,7 @@ public class FlashlightPowerCalculator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // Calculate flashlight power usage. Right now, this is based on the average power draw diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java new file mode 100644 index 0000000000000..44ad34417a4c0 --- /dev/null +++ b/core/java/com/android/internal/os/IdlePowerCalculator.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.BatteryStats; +import android.os.UserHandle; +import android.util.Log; +import android.util.SparseArray; + +import java.util.List; + +/** + * Estimates the amount of power consumed when the device is idle. + */ +public class IdlePowerCalculator extends PowerCalculator { + private static final String TAG = "IdlePowerCalculator"; + private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + private final PowerProfile mPowerProfile; + + public IdlePowerCalculator(PowerProfile powerProfile) { + mPowerProfile = powerProfile; + } + + /** + * Calculate the baseline power usage for the device when it is in suspend and idle. + * The device is drawing POWER_CPU_SUSPEND power at its lowest power state. + * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held. + */ + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + long batteryUptimeUs = batteryStats.computeBatteryUptime(rawUptimeUs, statsType); + long batteryRealtimeUs = batteryStats.computeBatteryRealtime(rawRealtimeUs, statsType); + + if (DEBUG) { + Log.d(TAG, "Battery type time: realtime=" + (batteryRealtimeUs / 1000) + " uptime=" + + (batteryUptimeUs / 1000)); + } + + final double suspendPowerMaMs = (batteryRealtimeUs / 1000) + * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND); + final double idlePowerMaMs = (batteryUptimeUs / 1000) + * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); + final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); + if (DEBUG && totalPowerMah != 0) { + Log.d(TAG, "Suspend: time=" + (batteryRealtimeUs / 1000) + + " power=" + formatCharge(suspendPowerMaMs / (60 * 60 * 1000))); + Log.d(TAG, "Idle: time=" + (batteryUptimeUs / 1000) + + " power=" + formatCharge(idlePowerMaMs / (60 * 60 * 1000))); + } + + if (totalPowerMah != 0) { + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.IDLE, null, 0); + bs.usagePowerMah = totalPowerMah; + bs.usageTimeMs = batteryRealtimeUs / 1000; + bs.sumPower(); + sippers.add(bs); + } + } +} diff --git a/core/java/com/android/internal/os/MediaPowerCalculator.java b/core/java/com/android/internal/os/MediaPowerCalculator.java index a35c1341905bf..e93d93ca7bc17 100644 --- a/core/java/com/android/internal/os/MediaPowerCalculator.java +++ b/core/java/com/android/internal/os/MediaPowerCalculator.java @@ -33,7 +33,7 @@ public class MediaPowerCalculator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // Calculate audio power usage, an estimate based on the average power routed to different // components like speaker, bluetooth, usb-c, earphone, etc. diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java index efd3ab51284b9..10d9b65199929 100644 --- a/core/java/com/android/internal/os/MemoryPowerCalculator.java +++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java @@ -1,8 +1,12 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.UserHandle; import android.util.Log; import android.util.LongSparseArray; +import android.util.SparseArray; + +import java.util.List; public class MemoryPowerCalculator extends PowerCalculator { @@ -22,11 +26,17 @@ public class MemoryPowerCalculator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) {} + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + BatterySipper memory = new BatterySipper(BatterySipper.DrainType.MEMORY, null, 0); + calculateRemaining(memory, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + memory.sumPower(); + if (memory.totalPowerMah > 0) { + sippers.add(memory); + } + } - @Override - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, + private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) { double totalMah = 0; long totalTimeMs = 0; diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java index 7c77d285bfc3f..790d2e51e38dd 100644 --- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java +++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java @@ -16,11 +16,15 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.UserHandle; import android.telephony.CellSignalStrength; import android.util.Log; +import android.util.SparseArray; + +import java.util.List; public class MobileRadioPowerCalculator extends PowerCalculator { - private static final String TAG = "MobileRadioPowerController"; + private static final String TAG = "MobRadioPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private final double mPowerRadioOn; private final double[] mPowerBins = new double[CellSignalStrength.getNumSignalStrengthLevels()]; @@ -44,12 +48,12 @@ public class MobileRadioPowerCalculator extends PowerCalculator { final long radioDataUptimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0) - ? (mobileData / (double)radioDataUptimeMs) - : (((double)MOBILE_BPS) / 8 / 2048); - return (MOBILE_POWER / mobilePps) / (60*60); + ? (mobileData / (double) radioDataUptimeMs) + : (((double) MOBILE_BPS) / 8 / 2048); + return (MOBILE_POWER / mobilePps) / (60 * 60); } - public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) { + public MobileRadioPowerCalculator(PowerProfile profile) { double temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1); if (temp != -1) { @@ -64,7 +68,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator { } temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1); - if (temp != -1 ) { + if (temp != -1) { for (int i = 0; i < mPowerBins.length; i++) { mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); } @@ -77,12 +81,25 @@ public class MobileRadioPowerCalculator extends PowerCalculator { } mPowerScan = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0); - mStats = stats; } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + mStats = batteryStats; + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + + BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); + calculateRemaining(radio, mStats, rawRealtimeUs, rawUptimeUs, statsType); + radio.sumPower(); + if (radio.totalPowerMah > 0) { + sippers.add(radio); + } + } + + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { // Add cost of mobile traffic. app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, statsType); @@ -99,7 +116,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator { // We are tracking when the radio is up, so can use the active time to // determine power use. mTotalAppMobileActiveMs += app.mobileActive; - app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60); + app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000 * 60 * 60); } else { // We are not tracking when the radio is up, so must approximate power use // based on the number of packets. @@ -110,23 +127,22 @@ public class MobileRadioPowerCalculator extends PowerCalculator { Log.d(TAG, "UID " + u.getUid() + ": mobile packets " + (app.mobileRxPackets + app.mobileTxPackets) + " active time " + app.mobileActive - + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah)); + + " power=" + formatCharge(app.mobileRadioPowerMah)); } } - @Override - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, + long rawUptimeUs, int statsType) { double power = 0; long signalTimeMs = 0; long noCoverageTimeMs = 0; for (int i = 0; i < mPowerBins.length; i++) { long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType) / 1000; - final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000); + final double p = (strengthTimeMs * mPowerBins[i]) / (60 * 60 * 1000); if (DEBUG && p != 0) { Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" - + BatteryStatsHelper.makemAh(p)); + + formatCharge(p)); } power += p; signalTimeMs += strengthTimeMs; @@ -137,16 +153,15 @@ public class MobileRadioPowerCalculator extends PowerCalculator { final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType) / 1000; - final double p = (scanningTimeMs * mPowerScan) / (60*60*1000); + final double p = (scanningTimeMs * mPowerScan) / (60 * 60 * 1000); if (DEBUG && p != 0) { - Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs - + " power=" + BatteryStatsHelper.makemAh(p)); + Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(p)); } power += p; long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000; long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs; if (remainingActiveTimeMs > 0) { - power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60); + power += (mPowerRadioOn * remainingActiveTimeMs) / (1000 * 60 * 60); } if (power != 0) { @@ -162,10 +177,6 @@ public class MobileRadioPowerCalculator extends PowerCalculator { @Override public void reset() { mTotalAppMobileActiveMs = 0; - } - - public void reset(BatteryStats stats) { - reset(); - mStats = stats; + mStats = null; } } diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java new file mode 100644 index 0000000000000..b68899edd81d0 --- /dev/null +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.BatteryStats; +import android.os.UserHandle; +import android.util.SparseArray; + +import java.util.List; + +/** + * Estimates power consumed by telephony. + */ +public class PhonePowerCalculator extends PowerCalculator { + private final PowerProfile mPowerProfile; + + public PhonePowerCalculator(PowerProfile powerProfile) { + mPowerProfile = powerProfile; + } + + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, statsType) / 1000; + double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) + * phoneOnTimeMs / (60 * 60 * 1000); + if (phoneOnPower != 0) { + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.PHONE, null, 0); + bs.usagePowerMah = phoneOnPower; + bs.usageTimeMs = phoneOnTimeMs; + bs.sumPower(); + sippers.add(bs); + } + } +} diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java index e31c9ded5a425..f171b0d99acc8 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -16,11 +16,44 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.UserHandle; +import android.util.SparseArray; + +import java.util.List; +import java.util.Locale; /** * Calculates power use of a device subsystem for an app. */ public abstract class PowerCalculator { + + /** + * Attributes the total amount of power used by this subsystem to various consumers such + * as apps. + * + * @param sippers A list of battery sippers that contains battery attribution data. + * The calculator may modify the list. + * @param batteryStats The recorded battery stats. + * @param rawRealtimeUs The raw system realtime in microseconds. + * @param rawUptimeUs The raw system uptime in microseconds. + * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this + * can only be {@link BatteryStats#STATS_SINCE_CHARGED}, since + * {@link BatteryStats#STATS_CURRENT} and + * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated. + * @param asUsers An array of users for which the attribution is requested. It may + * contain {@link UserHandle#USER_ALL} to indicate that the attribution + * should be performed for all users. + */ + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateApp(app, app.uidObj, rawRealtimeUs, rawUptimeUs, statsType); + } + } + } + /** * Calculate the amount of power an app used for this subsystem. * @param app The BatterySipper that represents the power use of an app. @@ -32,27 +65,43 @@ public abstract class PowerCalculator { * {@link BatteryStats#STATS_CURRENT} and * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated. */ - public abstract void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType); - - /** - * Calculate the remaining power that can not be attributed to an app. - * @param app The BatterySipper that will represent this remaining power. - * @param stats The BatteryStats object from which to retrieve data. - * @param rawRealtimeUs The raw system realtime in microseconds. - * @param rawUptimeUs The raw system uptime in microseconds. - * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this can - * only be {@link BatteryStats#STATS_SINCE_CHARGED}, since - * {@link BatteryStats#STATS_CURRENT} and - * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated. - */ - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - } + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) {} /** * Reset any state maintained in this calculator. */ public void reset() { } + + /** + * Converts charge in mAh to string. + */ + public static String formatCharge(double power) { + if (power == 0) return "0"; + + final String format; + if (power < .00001) { + format = "%.8f"; + } else if (power < .0001) { + format = "%.7f"; + } else if (power < .001) { + format = "%.6f"; + } else if (power < .01) { + format = "%.5f"; + } else if (power < .1) { + format = "%.4f"; + } else if (power < 1) { + format = "%.3f"; + } else if (power < 10) { + format = "%.2f"; + } else if (power < 100) { + format = "%.1f"; + } else { + format = "%.0f"; + } + + // Use English locale because this is never used in UI (only in checkin and dump). + return String.format(Locale.ENGLISH, format, power); + } } diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java new file mode 100644 index 0000000000000..09fb75bc2d2d9 --- /dev/null +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.BatteryStats; +import android.os.UserHandle; +import android.util.Log; +import android.util.SparseArray; + +import java.util.List; + +/** + * Estimates power consumed by the screen(s) + */ +public class ScreenPowerCalculator extends PowerCalculator { + private static final String TAG = "ScreenPowerCalculator"; + private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + + private final PowerProfile mPowerProfile; + + public ScreenPowerCalculator(PowerProfile powerProfile) { + mPowerProfile = powerProfile; + } + + /** + * Screen power is the additional power the screen takes while the device is running. + */ + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + double power = 0; + final long screenOnTimeMs = batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; + power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); + final double screenFullPower = + mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); + for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { + final double screenBinPower = screenFullPower * (i + 0.5f) + / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; + final long brightnessTime = + batteryStats.getScreenBrightnessTime(i, rawRealtimeUs, statsType) / 1000; + final double p = screenBinPower * brightnessTime; + if (DEBUG && p != 0) { + Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime + + " power=" + formatCharge(p / (60 * 60 * 1000))); + } + power += p; + } + power /= (60 * 60 * 1000); // To hours + if (power != 0) { + final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0); + bs.usagePowerMah = power; + bs.usageTimeMs = screenOnTimeMs; + bs.sumPower(); + sippers.add(bs); + } + } +} diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java index 04cb49a48c265..5002daff8eed1 100644 --- a/core/java/com/android/internal/os/SensorPowerCalculator.java +++ b/core/java/com/android/internal/os/SensorPowerCalculator.java @@ -18,6 +18,7 @@ package com.android.internal.os; import android.hardware.Sensor; import android.hardware.SensorManager; import android.os.BatteryStats; +import android.os.UserHandle; import android.util.SparseArray; import com.android.internal.location.gnssmetrics.GnssMetrics; @@ -25,17 +26,24 @@ import com.android.internal.location.gnssmetrics.GnssMetrics; import java.util.List; public class SensorPowerCalculator extends PowerCalculator { + private final PowerProfile mPowerProfile; private final List mSensors; - private final double mGpsPower; + private double mGpsPower; - public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager, - BatteryStats stats, long rawRealtimeUs, int statsType) { + public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) { + mPowerProfile = profile; mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL); - mGpsPower = getAverageGpsPower(profile, stats, rawRealtimeUs, statsType); } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + mGpsPower = getAverageGpsPower(batteryStats, rawRealtimeUs, statsType); + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + } + + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { // Process Sensor usage final SparseArray sensorStats = u.getSensorStats(); @@ -49,14 +57,14 @@ public class SensorPowerCalculator extends PowerCalculator { switch (sensorHandle) { case BatteryStats.Uid.Sensor.GPS: app.gpsTimeMs = sensorTime; - app.gpsPowerMah = (app.gpsTimeMs * mGpsPower) / (1000*60*60); + app.gpsPowerMah = (app.gpsTimeMs * mGpsPower) / (1000 * 60 * 60); break; default: final int sensorsCount = mSensors.size(); for (int i = 0; i < sensorsCount; i++) { final Sensor s = mSensors.get(i); if (s.getHandle() == sensorHandle) { - app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60); + app.sensorPowerMah += (sensorTime * s.getPower()) / (1000 * 60 * 60); break; } } @@ -65,10 +73,10 @@ public class SensorPowerCalculator extends PowerCalculator { } } - private double getAverageGpsPower(PowerProfile profile, BatteryStats stats, long rawRealtimeUs, + private double getAverageGpsPower(BatteryStats stats, long rawRealtimeUs, int statsType) { double averagePower = - profile.getAveragePowerOrDefault(PowerProfile.POWER_GPS_ON, -1); + mPowerProfile.getAveragePowerOrDefault(PowerProfile.POWER_GPS_ON, -1); if (averagePower != -1) { return averagePower; } @@ -78,8 +86,9 @@ public class SensorPowerCalculator extends PowerCalculator { for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType); totalTime += timePerLevel; - totalPower += profile.getAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i) - * timePerLevel; + totalPower += + mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i) + * timePerLevel; } if (totalTime != 0) { averagePower = totalPower / totalTime; diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index fc36e50950e9f..b15dff67b6199 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -17,9 +17,12 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.UserHandle; import android.util.Log; +import android.util.SparseArray; import java.util.Arrays; +import java.util.List; /** * Estimates the amount of power consumed by the System Server handling requests from @@ -32,39 +35,40 @@ public class SystemServicePowerCalculator extends PowerCalculator { private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; private final PowerProfile mPowerProfile; - private final BatteryStats mBatteryStats; + // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds // Data organized like this: // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...} private double[] mSystemServicePowerMaUs; - public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) { + public SystemServicePowerCalculator(PowerProfile powerProfile) { mPowerProfile = powerProfile; - mBatteryStats = batteryStats; } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, + SparseArray asUsers) { + updateSystemServicePower(batteryStats); + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + } + + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { final double proportionalUsage = u.getProportionalSystemServiceUsage(); - if (proportionalUsage > 0) { - if (mSystemServicePowerMaUs == null) { - updateSystemServicePower(); + if (proportionalUsage > 0 && mSystemServicePowerMaUs != null) { + double cpuPowerMaUs = 0; + for (int i = 0; i < mSystemServicePowerMaUs.length; i++) { + cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage; } - if (mSystemServicePowerMaUs != null) { - double cpuPowerMaUs = 0; - for (int i = 0; i < mSystemServicePowerMaUs.length; i++) { - cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage; - } - - app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; - } + app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; } } - private void updateSystemServicePower() { - final long[] systemServiceTimeAtCpuSpeeds = mBatteryStats.getSystemServiceTimeAtCpuSpeeds(); + private void updateSystemServicePower(BatteryStats batteryStats) { + final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); if (systemServiceTimeAtCpuSpeeds == null) { return; } diff --git a/core/java/com/android/internal/os/UserPowerCalculator.java b/core/java/com/android/internal/os/UserPowerCalculator.java new file mode 100644 index 0000000000000..53f85154330c6 --- /dev/null +++ b/core/java/com/android/internal/os/UserPowerCalculator.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.BatteryStats; +import android.os.Process; +import android.os.UserHandle; +import android.util.SparseArray; + +import java.util.List; + +/** + * Computes power consumed by Users + */ +public class UserPowerCalculator extends PowerCalculator { + + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); + if (forAllUsers) { + return; + } + + SparseArray userSippers = new SparseArray<>(); + + for (int i = sippers.size() - 1; i >= 0; i--) { + BatterySipper sipper = sippers.get(i); + final int uid = sipper.getUid(); + final int userId = UserHandle.getUserId(uid); + if (asUsers.get(userId) == null + && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) { + // We are told to just report this user's apps as one accumulated entry. + BatterySipper userSipper = userSippers.get(userId); + if (userSipper == null) { + userSipper = new BatterySipper(BatterySipper.DrainType.USER, null, 0); + userSipper.userId = userId; + userSippers.put(userId, userSipper); + } + userSipper.add(sipper); + sipper.isAggregated = true; + } + } + + for (int i = 0; i < userSippers.size(); i++) { + BatterySipper sipper = userSippers.valueAt(i); + if (sipper.sumPower() > 0) { + sippers.add(sipper); + } + } + } +} diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java index 486b5842400c3..3f68597dc1cc3 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java @@ -16,8 +16,12 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; + +import java.util.List; public class WakelockPowerCalculator extends PowerCalculator { private static final String TAG = "WakelockPowerCalculator"; @@ -30,8 +34,31 @@ public class WakelockPowerCalculator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + + // The device has probably been awake for longer than the screen on + // time and application wake lock time would account for. Assign + // this remainder to the OS, if possible. + BatterySipper osSipper = null; + for (int i = sippers.size() - 1; i >= 0; i--) { + BatterySipper app = sippers.get(i); + if (app.getUid() == 0) { + osSipper = app; + break; + } + } + + if (osSipper != null) { + calculateRemaining(osSipper, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + osSipper.sumPower(); + } + } + + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { long wakeLockTimeUs = 0; final ArrayMap wakelockStats = u.getWakelockStats(); @@ -50,24 +77,22 @@ public class WakelockPowerCalculator extends PowerCalculator { mTotalAppWakelockTimeMs += app.wakeLockTimeMs; // Add cost of holding a wake lock. - app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60); + app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000 * 60 * 60); if (DEBUG && app.wakeLockPowerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs - + " power=" + BatteryStatsHelper.makemAh(app.wakeLockPowerMah)); + + " power=" + formatCharge(app.wakeLockPowerMah)); } } - @Override - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, + long rawUptimeUs, int statsType) { long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000; wakeTimeMillis -= mTotalAppWakelockTimeMs + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000); if (wakeTimeMillis > 0) { - final double power = (wakeTimeMillis * mPowerWakelock) / (1000*60*60); + final double power = (wakeTimeMillis * mPowerWakelock) / (1000 * 60 * 60); if (DEBUG) { - Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " - + BatteryStatsHelper.makemAh(power)); + Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power)); } app.wakeLockTimeMs += wakeTimeMillis; app.wakeLockPowerMah += power; diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index b4470397c67bc..63763f76c6ed0 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -16,7 +16,12 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.Process; +import android.os.UserHandle; import android.util.Log; +import android.util.SparseArray; + +import java.util.List; /** * WiFi power calculator for when BatteryStats supports energy reporting @@ -28,18 +33,65 @@ public class WifiPowerCalculator extends PowerCalculator { private final double mIdleCurrentMa; private final double mTxCurrentMa; private final double mRxCurrentMa; + private final PowerProfile mPowerProfile; + private final boolean mHasWifiPowerController; private double mTotalAppPowerDrain = 0; private long mTotalAppRunningTime = 0; + private WifiPowerEstimator mWifiPowerEstimator; + private boolean mHasWifiPowerReporting; public WifiPowerCalculator(PowerProfile profile) { + mPowerProfile = profile; + mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE); mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX); mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX); + + mHasWifiPowerController = mIdleCurrentMa != 0 && mTxCurrentMa != 0 && mRxCurrentMa != 0; + mWifiPowerEstimator = new WifiPowerEstimator(mPowerProfile); + } + + /** + * We do per-app blaming of WiFi activity. If energy info is reported from the controller, + * then only the WiFi process gets blamed here since we normalize power calculations and + * assign all the power drain to apps. If energy info is not reported, we attribute the + * difference between total running time of WiFi for all apps and the actual running time + * of WiFi to the WiFi subsystem. + */ + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + + // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, + // so always check this field. + mHasWifiPowerReporting = mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); + + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); + calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + + for (int i = sippers.size() - 1; i >= 0; i--) { + BatterySipper app = sippers.get(i); + if (app.getUid() == Process.WIFI_UID) { + if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); + app.isAggregated = true; + bs.add(app); + } + } + if (bs.sumPower() > 0) { + sippers.add(bs); + } } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + if (!mHasWifiPowerReporting) { + mWifiPowerEstimator.calculateApp(app, u, rawRealtimeUs, rawUptimeUs, statsType); + return; + } + final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); if (counter == null) { return; @@ -53,7 +105,7 @@ public class WifiPowerCalculator extends PowerCalculator { app.wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) - / (1000*60*60); + / (1000 * 60 * 60); mTotalAppPowerDrain += app.wifiPowerMah; app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, @@ -67,13 +119,18 @@ public class WifiPowerCalculator extends PowerCalculator { if (DEBUG && app.wifiPowerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" + - txTime + "ms power=" + BatteryStatsHelper.makemAh(app.wifiPowerMah)); + txTime + "ms power=" + formatCharge(app.wifiPowerMah)); } } - @Override - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + if (!mHasWifiPowerReporting) { + mWifiPowerEstimator.calculateRemaining(app, stats, rawRealtimeUs, rawUptimeUs, + statsType); + return; + } + final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity(); final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); @@ -84,16 +141,16 @@ public class WifiPowerCalculator extends PowerCalculator { (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime); double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType) - / (double)(1000*60*60); + / (double) (1000 * 60 * 60); if (powerDrainMah == 0) { // Some controllers do not report power drain, so we can calculate it here. powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) - + (rxTimeMs * mRxCurrentMa)) / (1000*60*60); + + (rxTimeMs * mRxCurrentMa)) / (1000 * 60 * 60); } app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain); if (DEBUG) { - Log.d(TAG, "left over WiFi power: " + BatteryStatsHelper.makemAh(app.wifiPowerMah)); + Log.d(TAG, "left over WiFi power: " + formatCharge(app.wifiPowerMah)); } } @@ -101,5 +158,6 @@ public class WifiPowerCalculator extends PowerCalculator { public void reset() { mTotalAppPowerDrain = 0; mTotalAppRunningTime = 0; + mWifiPowerEstimator.reset(); } } diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java index d175202163b27..d0a105cfd9585 100644 --- a/core/java/com/android/internal/os/WifiPowerEstimator.java +++ b/core/java/com/android/internal/os/WifiPowerEstimator.java @@ -16,7 +16,12 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.Process; +import android.os.UserHandle; import android.util.Log; +import android.util.SparseArray; + +import java.util.List; /** * Estimates WiFi power usage based on timers in BatteryStats. @@ -37,6 +42,27 @@ public class WifiPowerEstimator extends PowerCalculator { mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN); } + @Override + public void calculate(List sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { + super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); + calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + + for (int i = sippers.size() - 1; i >= 0; i--) { + BatterySipper app = sippers.get(i); + if (app.getUid() == Process.WIFI_UID) { + if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); + app.isAggregated = true; + bs.add(app); + } + } + if (bs.sumPower() > 0) { + sippers.add(bs); + } + } + /** * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. */ @@ -48,7 +74,7 @@ public class WifiPowerEstimator extends PowerCalculator { } @Override - public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, statsType); @@ -79,13 +105,11 @@ public class WifiPowerEstimator extends PowerCalculator { app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower; if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": power=" + - BatteryStatsHelper.makemAh(app.wifiPowerMah)); + Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(app.wifiPowerMah)); } } - @Override - public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, + void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) { final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java index c592ab6a16aef..4bd5724ed9ea7 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java @@ -33,6 +33,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + @RunWith(AndroidJUnit4.class) @SmallTest public class PowerCalculatorTest extends TestCase { @@ -61,11 +63,9 @@ public class PowerCalculatorTest extends TestCase { when(videoTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(1L * US_IN_HR); MediaPowerCalculator mediaPowerCalculator = new MediaPowerCalculator(mPowerProfile); - BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, null, 0); + BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0); - mediaPowerCalculator.calculateApp(app, u, 2L * US_IN_HR, 2L * US_IN_HR, 0); + mediaPowerCalculator.calculate(List.of(app), null, 2L * US_IN_HR, 2L * US_IN_HR, 0, null); assertEquals(49.0, app.sumPower()); } - - } diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index bd0bbe99ee2f2..dbb36fbd1650b 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -35,6 +35,7 @@ import org.junit.runner.RunWith; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) @@ -58,8 +59,7 @@ public class SystemServicePowerCalculatorTest { .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) .setUserInfoProvider(new MockUserInfoProvider()); mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); - mSystemServicePowerCalculator = - new SystemServicePowerCalculator(mProfile, mMockBatteryStats); + mSystemServicePowerCalculator = new SystemServicePowerCalculator(mProfile); } @Test @@ -105,14 +105,12 @@ public class SystemServicePowerCalculatorTest { BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP, mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0); - mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0, - BatteryStats.STATS_SINCE_CHARGED); - assertEquals(0.00016269, app1.systemServiceCpuPowerMah, 0.0000001); - BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP, mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0); - mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0, - BatteryStats.STATS_SINCE_CHARGED); + mSystemServicePowerCalculator.calculate(List.of(app1, app2), mMockBatteryStats, 0, 0, + BatteryStats.STATS_SINCE_CHARGED, null); + + assertEquals(0.00016269, app1.systemServiceCpuPowerMah, 0.0000001); assertEquals(0.00146426, app2.systemServiceCpuPowerMah, 0.0000001); }