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