1 /* 2 * Copyright (C) 2023 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.server.power.stats; 17 18 import android.os.BatteryStats; 19 import android.util.SparseArray; 20 21 import com.android.internal.os.BatteryStatsHistory; 22 import com.android.internal.os.BatteryStatsHistoryIterator; 23 24 import java.util.function.Consumer; 25 26 /** 27 * Power stats aggregator. It reads through portions of battery stats history, finds 28 * relevant items (state changes, power stats etc) and produces one or more 29 * {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history. 30 */ 31 public class PowerStatsAggregator { 32 private static final long UNINITIALIZED = -1; 33 private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig; 34 private final BatteryStatsHistory mHistory; 35 private final SparseArray<PowerStatsProcessor> mProcessors = new SparseArray<>(); 36 private AggregatedPowerStats mStats; 37 private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY; 38 private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; 39 PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig, BatteryStatsHistory history)40 public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig, 41 BatteryStatsHistory history) { 42 mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig; 43 mHistory = history; 44 for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig : 45 aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) { 46 PowerStatsProcessor processor = powerComponentsConfig.getProcessor(); 47 mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor); 48 } 49 } 50 getConfig()51 AggregatedPowerStatsConfig getConfig() { 52 return mAggregatedPowerStatsConfig; 53 } 54 55 /** 56 * Iterates of the battery history and aggregates power stats between the specified times. 57 * The start and end are specified in the battery-stats monotonic time, which is the 58 * adjusted elapsed time found in HistoryItem.time. 59 * <p> 60 * The aggregated stats are sent to the consumer. One aggregation pass may produce 61 * multiple sets of aggregated stats if there was an incompatible change that occurred in the 62 * middle of the recorded battery history. 63 * <p> 64 * Note: the AggregatedPowerStats object is reused, so the consumer should fully consume 65 * the stats in the <code>accept</code> method and never cache it. 66 */ aggregatePowerStats(long startTimeMs, long endTimeMs, Consumer<AggregatedPowerStats> consumer)67 public void aggregatePowerStats(long startTimeMs, long endTimeMs, 68 Consumer<AggregatedPowerStats> consumer) { 69 synchronized (this) { 70 if (mStats == null) { 71 mStats = new AggregatedPowerStats(mAggregatedPowerStatsConfig); 72 } 73 74 start(mStats, startTimeMs); 75 76 boolean clockUpdateAdded = false; 77 long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED; 78 long lastTime = 0; 79 int lastStates = 0xFFFFFFFF; 80 int lastStates2 = 0xFFFFFFFF; 81 try (BatteryStatsHistoryIterator iterator = mHistory.iterate(startTimeMs, endTimeMs)) { 82 while (iterator.hasNext()) { 83 BatteryStats.HistoryItem item = iterator.next(); 84 85 if (!clockUpdateAdded) { 86 mStats.addClockUpdate(item.time, item.currentTime); 87 if (baseTime == UNINITIALIZED) { 88 baseTime = item.time; 89 } 90 clockUpdateAdded = true; 91 } else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME 92 || item.cmd == BatteryStats.HistoryItem.CMD_RESET) { 93 mStats.addClockUpdate(item.time, item.currentTime); 94 } 95 96 lastTime = item.time; 97 98 int batteryState = 99 (item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0 100 ? AggregatedPowerStatsConfig.POWER_STATE_OTHER 101 : AggregatedPowerStatsConfig.POWER_STATE_BATTERY; 102 if (batteryState != mCurrentBatteryState) { 103 mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState, 104 item.time); 105 mCurrentBatteryState = batteryState; 106 } 107 108 int screenState = 109 (item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0 110 ? AggregatedPowerStatsConfig.SCREEN_STATE_ON 111 : AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; 112 if (screenState != mCurrentScreenState) { 113 mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState, 114 item.time); 115 mCurrentScreenState = screenState; 116 } 117 118 if ((item.states 119 & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES) 120 != lastStates 121 || (item.states2 122 & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2) 123 != lastStates2) { 124 mStats.noteStateChange(item); 125 lastStates = item.states 126 & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES; 127 lastStates2 = item.states2 128 & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2; 129 } 130 131 if (item.processStateChange != null) { 132 mStats.setUidState(item.processStateChange.uid, 133 AggregatedPowerStatsConfig.STATE_PROCESS_STATE, 134 item.processStateChange.processState, item.time); 135 } 136 137 if (item.powerStats != null) { 138 if (!mStats.isCompatible(item.powerStats)) { 139 if (lastTime > baseTime) { 140 mStats.setDuration(lastTime - baseTime); 141 finish(mStats, lastTime); 142 consumer.accept(mStats); 143 } 144 mStats.reset(); 145 mStats.addClockUpdate(item.time, item.currentTime); 146 baseTime = lastTime = item.time; 147 } 148 mStats.addPowerStats(item.powerStats, item.time); 149 } 150 } 151 } 152 if (lastTime > baseTime) { 153 mStats.setDuration(lastTime - baseTime); 154 finish(mStats, lastTime); 155 consumer.accept(mStats); 156 } 157 158 mStats.reset(); // to free up memory 159 } 160 } 161 start(AggregatedPowerStats stats, long timestampMs)162 private void start(AggregatedPowerStats stats, long timestampMs) { 163 for (int i = 0; i < mProcessors.size(); i++) { 164 PowerComponentAggregatedPowerStats component = 165 stats.getPowerComponentStats(mProcessors.keyAt(i)); 166 if (component != null) { 167 mProcessors.valueAt(i).start(component, timestampMs); 168 } 169 } 170 } 171 finish(AggregatedPowerStats stats, long timestampMs)172 private void finish(AggregatedPowerStats stats, long timestampMs) { 173 for (int i = 0; i < mProcessors.size(); i++) { 174 PowerComponentAggregatedPowerStats component = 175 stats.getPowerComponentStats(mProcessors.keyAt(i)); 176 if (component != null) { 177 mProcessors.valueAt(i).finish(component, timestampMs); 178 } 179 } 180 } 181 182 /** 183 * Reset to prepare for a new aggregation session. 184 */ reset()185 public void reset() { 186 synchronized (this) { 187 mStats = null; 188 } 189 } 190 } 191