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.os.UserHandle; 25 import android.util.ArrayMap; 26 import android.util.Log; 27 import android.util.SparseArray; 28 29 import java.util.List; 30 31 public class WakelockPowerCalculator extends PowerCalculator { 32 private static final String TAG = "WakelockPowerCalculator"; 33 private static final boolean DEBUG = BatteryStatsHelper.DEBUG; 34 private final UsageBasedPowerEstimator mPowerEstimator; 35 36 private static class PowerAndDuration { 37 public long durationMs; 38 public double powerMah; 39 } 40 WakelockPowerCalculator(PowerProfile profile)41 public WakelockPowerCalculator(PowerProfile profile) { 42 mPowerEstimator = new UsageBasedPowerEstimator( 43 profile.getAveragePower(PowerProfile.POWER_CPU_IDLE)); 44 } 45 46 @Override calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)47 public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, 48 long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { 49 final PowerAndDuration result = new PowerAndDuration(); 50 UidBatteryConsumer.Builder osBatteryConsumer = null; 51 double osPowerMah = 0; 52 long osDurationMs = 0; 53 long totalAppDurationMs = 0; 54 double appPowerMah = 0; 55 final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = 56 builder.getUidBatteryConsumerBuilders(); 57 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { 58 final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); 59 calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs, 60 BatteryStats.STATS_SINCE_CHARGED); 61 app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs) 62 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); 63 totalAppDurationMs += result.durationMs; 64 appPowerMah += result.powerMah; 65 66 if (app.getUid() == Process.ROOT_UID) { 67 osBatteryConsumer = app; 68 osDurationMs = result.durationMs; 69 osPowerMah = result.powerMah; 70 } 71 } 72 73 // The device has probably been awake for longer than the screen on 74 // time and application wake lock time would account for. Assign 75 // this remainder to the OS, if possible. 76 calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, 77 BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs); 78 final double remainingPowerMah = result.powerMah; 79 if (osBatteryConsumer != null) { 80 osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 81 result.durationMs) 82 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, remainingPowerMah); 83 } 84 85 long wakeTimeMs = calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs); 86 if (wakeTimeMs < 0) { 87 wakeTimeMs = 0; 88 } 89 builder.getAggregateBatteryConsumerBuilder( 90 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 91 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 92 wakeTimeMs) 93 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 94 appPowerMah + remainingPowerMah); 95 builder.getAggregateBatteryConsumerBuilder( 96 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) 97 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 98 totalAppDurationMs) 99 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, 100 appPowerMah); 101 } 102 103 @Override calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers)104 public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, 105 long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { 106 final PowerAndDuration result = new PowerAndDuration(); 107 BatterySipper osSipper = null; 108 double osPowerMah = 0; 109 long osDurationMs = 0; 110 long totalAppDurationMs = 0; 111 for (int i = sippers.size() - 1; i >= 0; i--) { 112 final BatterySipper app = sippers.get(i); 113 if (app.drainType == BatterySipper.DrainType.APP) { 114 calculateApp(result, app.uidObj, rawRealtimeUs, statsType); 115 app.wakeLockTimeMs = result.durationMs; 116 app.wakeLockPowerMah = result.powerMah; 117 totalAppDurationMs += result.durationMs; 118 119 if (app.getUid() == Process.ROOT_UID) { 120 osSipper = app; 121 osPowerMah = result.powerMah; 122 osDurationMs = result.durationMs; 123 } 124 } 125 } 126 127 // The device has probably been awake for longer than the screen on 128 // time and application wake lock time would account for. Assign 129 // this remainder to the OS, if possible. 130 if (osSipper != null) { 131 calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, 132 osPowerMah, osDurationMs, totalAppDurationMs); 133 osSipper.wakeLockTimeMs = result.durationMs; 134 osSipper.wakeLockPowerMah = result.powerMah; 135 osSipper.sumPower(); 136 } 137 } 138 calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs, int statsType)139 private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs, 140 int statsType) { 141 long wakeLockTimeUs = 0; 142 final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = 143 u.getWakelockStats(); 144 final int wakelockStatsCount = wakelockStats.size(); 145 for (int i = 0; i < wakelockStatsCount; i++) { 146 final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i); 147 148 // Only care about partial wake locks since full wake locks 149 // are canceled when the user turns the screen off. 150 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); 151 if (timer != null) { 152 wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType); 153 } 154 } 155 result.durationMs = wakeLockTimeUs / 1000; // convert to millis 156 157 // Add cost of holding a wake lock. 158 result.powerMah = mPowerEstimator.calculatePower(result.durationMs); 159 if (DEBUG && result.powerMah != 0) { 160 Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs 161 + " power=" + formatCharge(result.powerMah)); 162 } 163 } 164 calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs, long totalAppDurationMs)165 private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs, 166 long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs, 167 long totalAppDurationMs) { 168 final long wakeTimeMillis = calculateWakeTimeMillis(stats, rawRealtimeUs, rawUptimeUs) 169 - totalAppDurationMs; 170 if (wakeTimeMillis > 0) { 171 final double power = mPowerEstimator.calculatePower(wakeTimeMillis); 172 if (DEBUG) { 173 Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power)); 174 } 175 result.durationMs = osDurationMs + wakeTimeMillis; 176 result.powerMah = osPowerMah + power; 177 } else { 178 result.durationMs = 0; 179 result.powerMah = 0; 180 } 181 } 182 183 /** 184 * Return on-battery/screen-off time. May be negative if the screen-on time exceeds 185 * the on-battery time. 186 */ calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs)187 private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs, 188 long rawUptimeUs) { 189 final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs); 190 final long screenOnTimeUs = 191 batteryStats.getScreenOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); 192 return (batteryUptimeUs - screenOnTimeUs) / 1000; 193 } 194 } 195