1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.internal.os; 17 18 import android.os.BatteryConsumer; 19 import android.os.BatteryStats; 20 import android.os.BatteryUsageStats; 21 import android.os.BatteryUsageStatsQuery; 22 import android.os.Process; 23 import android.os.UidBatteryConsumer; 24 import android.util.ArrayMap; 25 import android.util.Log; 26 import android.util.SparseArray; 27 28 public class WakelockPowerCalculator extends PowerCalculator { 29 private static final String TAG = "WakelockPowerCalculator"; 30 private static final boolean DEBUG = PowerCalculator.DEBUG; 31 private final UsageBasedPowerEstimator mPowerEstimator; 32 33 private static class PowerAndDuration { 34 public long durationMs; 35 public double powerMah; 36 } 37 WakelockPowerCalculator(PowerProfile profile)38 public WakelockPowerCalculator(PowerProfile profile) { 39 mPowerEstimator = new UsageBasedPowerEstimator( 40 profile.getAveragePower(PowerProfile.POWER_CPU_IDLE)); 41 } 42 43 @Override isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)44 public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) { 45 return powerComponent == BatteryConsumer.POWER_COMPONENT_WAKELOCK; 46 } 47 48 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)49 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 50 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 51 final PowerAndDuration result = new PowerAndDuration(); 52 UidBatteryConsumer.Builder osBatteryConsumer = null; 53 double osPowerMah = 0; 54 long osDurationMs = 0; 55 long totalAppDurationMs = 0; 56 double appPowerMah = 0; 57 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 58 builder.getUidBatteryConsumerBuilders(); 59 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 60 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 61 calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs, 62 BatteryStats.STATS_SINCE_CHARGED); 63 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs) 64 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); 65 if (!app.isVirtualUid()) { 66 totalAppDurationMs += result.durationMs; 67 appPowerMah += result.powerMah; 68 } 69 70 if (app.getUid() == Process.ROOT_UID) { 71 osBatteryConsumer = app; 72 osDurationMs = result.durationMs; 73 osPowerMah = result.powerMah; 74 } 75 } 76 77 // The device has probably been awake for longer than the screen on 78 // time and application wake lock time would account for. Assign 79 // this remainder to the OS, if possible. 80 calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, 81 BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs); 82 final double remainingPowerMah = result.powerMah; 83 if (osBatteryConsumer != null) { 84 osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 85 result.durationMs) 86 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, remainingPowerMah); 87 } 88 89 long wakeTimeMs = calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs); 90 if (wakeTimeMs < 0) { 91 wakeTimeMs = 0; 92 } 93 builder.getAggregateBatteryConsumerBuilder( 94 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 95 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 96 wakeTimeMs) 97 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 98 appPowerMah + remainingPowerMah); 99 builder.getAggregateBatteryConsumerBuilder( 100 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 101 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 102 totalAppDurationMs) 103 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 104 appPowerMah); 105 } 106 calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs, int statsType)107 private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs, 108 int statsType) { 109 long wakeLockTimeUs = 0; 110 final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = 111 u.getWakelockStats(); 112 final int wakelockStatsCount = wakelockStats.size(); 113 for (int i = 0; i < wakelockStatsCount; i++) { 114 final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i); 115 116 // Only care about partial wake locks since full wake locks 117 // are canceled when the user turns the screen off. 118 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); 119 if (timer != null) { 120 wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType); 121 } 122 } 123 result.durationMs = wakeLockTimeUs / 1000; // convert to millis 124 125 // Add cost of holding a wake lock. 126 result.powerMah = mPowerEstimator.calculatePower(result.durationMs); 127 if (DEBUG && result.powerMah != 0) { 128 Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs 129 + " power=" + BatteryStats.formatCharge(result.powerMah)); 130 } 131 } 132 calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs, long totalAppDurationMs)133 private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs, 134 long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs, 135 long totalAppDurationMs) { 136 final long wakeTimeMillis = calculateWakeTimeMillis(stats, rawRealtimeUs, rawUptimeUs) 137 - totalAppDurationMs; 138 if (wakeTimeMillis > 0) { 139 final double power = mPowerEstimator.calculatePower(wakeTimeMillis); 140 if (DEBUG) { 141 Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis 142 + " power " + BatteryStats.formatCharge(power)); 143 } 144 result.durationMs = osDurationMs + wakeTimeMillis; 145 result.powerMah = osPowerMah + power; 146 } else { 147 result.durationMs = 0; 148 result.powerMah = 0; 149 } 150 } 151 152 /** 153 * Return on-battery/screen-off time. May be negative if the screen-on time exceeds 154 * the on-battery time. 155 */ calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs)156 private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs, 157 long rawUptimeUs) { 158 final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs); 159 final long screenOnTimeUs = 160 batteryStats.getScreenOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 161 return (batteryUptimeUs - screenOnTimeUs) / 1000; 162 } 163 } 164