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 17 package com.android.server.power.stats; 18 19 import android.annotation.CurrentTimeMillisLong; 20 import android.annotation.DurationMillisLong; 21 import android.annotation.NonNull; 22 import android.os.BatteryStats; 23 import android.os.UserHandle; 24 import android.text.format.DateFormat; 25 import android.util.IndentingPrintWriter; 26 import android.util.Slog; 27 import android.util.TimeUtils; 28 29 import com.android.internal.os.PowerStats; 30 import com.android.modules.utils.TypedXmlPullParser; 31 import com.android.modules.utils.TypedXmlSerializer; 32 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.IOException; 37 import java.io.StringWriter; 38 import java.text.SimpleDateFormat; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Date; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Set; 45 import java.util.TimeZone; 46 47 /** 48 * This class represents aggregated power stats for a variety of power components (CPU, WiFi, 49 * etc) covering a specific period of power usage history. 50 */ 51 class AggregatedPowerStats { 52 private static final String TAG = "AggregatedPowerStats"; 53 private static final int MAX_CLOCK_UPDATES = 100; 54 private static final String XML_TAG_AGGREGATED_POWER_STATS = "agg-power-stats"; 55 56 private final PowerComponentAggregatedPowerStats[] mPowerComponentStats; 57 58 static class ClockUpdate { 59 public long monotonicTime; 60 @CurrentTimeMillisLong public long currentTime; 61 } 62 63 private final List<ClockUpdate> mClockUpdates = new ArrayList<>(); 64 65 @DurationMillisLong 66 private long mDurationMs; 67 AggregatedPowerStats(AggregatedPowerStatsConfig aggregatedPowerStatsConfig)68 AggregatedPowerStats(AggregatedPowerStatsConfig aggregatedPowerStatsConfig) { 69 List<AggregatedPowerStatsConfig.PowerComponent> configs = 70 aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs(); 71 mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()]; 72 for (int i = 0; i < configs.size(); i++) { 73 mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(this, configs.get(i)); 74 } 75 } 76 77 /** 78 * Records a mapping of monotonic time to wall-clock time. Since wall-clock time can change, 79 * there may be multiple clock updates in one set of aggregated stats. 80 * 81 * @param monotonicTime monotonic time in milliseconds, see 82 * {@link com.android.internal.os.MonotonicClock} 83 * @param currentTime current time in milliseconds, see {@link System#currentTimeMillis()} 84 */ addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime)85 void addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime) { 86 ClockUpdate clockUpdate = new ClockUpdate(); 87 clockUpdate.monotonicTime = monotonicTime; 88 clockUpdate.currentTime = currentTime; 89 if (mClockUpdates.size() < MAX_CLOCK_UPDATES) { 90 mClockUpdates.add(clockUpdate); 91 } else { 92 Slog.i(TAG, "Too many clock updates. Replacing the previous update with " 93 + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime)); 94 mClockUpdates.set(mClockUpdates.size() - 1, clockUpdate); 95 } 96 } 97 98 /** 99 * Start time according to {@link com.android.internal.os.MonotonicClock} 100 */ getStartTime()101 long getStartTime() { 102 if (mClockUpdates.isEmpty()) { 103 return 0; 104 } else { 105 return mClockUpdates.get(0).monotonicTime; 106 } 107 } 108 getClockUpdates()109 List<ClockUpdate> getClockUpdates() { 110 return mClockUpdates; 111 } 112 setDuration(long durationMs)113 void setDuration(long durationMs) { 114 mDurationMs = durationMs; 115 } 116 117 @DurationMillisLong getDuration()118 public long getDuration() { 119 return mDurationMs; 120 } 121 getPowerComponentStats(int powerComponentId)122 PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) { 123 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 124 if (stats.powerComponentId == powerComponentId) { 125 return stats; 126 } 127 } 128 return null; 129 } 130 setDeviceState(@ggregatedPowerStatsConfig.TrackedState int stateId, int state, long time)131 void setDeviceState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, 132 long time) { 133 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 134 stats.setState(stateId, state, time); 135 } 136 } 137 setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time)138 void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, 139 long time) { 140 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 141 stats.setUidState(uid, stateId, state, time); 142 } 143 } 144 isCompatible(PowerStats powerStats)145 boolean isCompatible(PowerStats powerStats) { 146 int powerComponentId = powerStats.descriptor.powerComponentId; 147 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 148 if (stats.powerComponentId == powerComponentId && !stats.isCompatible(powerStats)) { 149 return false; 150 } 151 } 152 return true; 153 } 154 addPowerStats(PowerStats powerStats, long time)155 void addPowerStats(PowerStats powerStats, long time) { 156 int powerComponentId = powerStats.descriptor.powerComponentId; 157 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 158 if (stats.powerComponentId == powerComponentId) { 159 stats.getConfig().getProcessor().addPowerStats(stats, powerStats, time); 160 } 161 } 162 } 163 noteStateChange(BatteryStats.HistoryItem item)164 public void noteStateChange(BatteryStats.HistoryItem item) { 165 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 166 stats.getConfig().getProcessor().noteStateChange(stats, item); 167 } 168 } 169 reset()170 void reset() { 171 mClockUpdates.clear(); 172 mDurationMs = 0; 173 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 174 stats.reset(); 175 } 176 } 177 writeXml(TypedXmlSerializer serializer)178 public void writeXml(TypedXmlSerializer serializer) throws IOException { 179 serializer.startTag(null, XML_TAG_AGGREGATED_POWER_STATS); 180 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 181 stats.writeXml(serializer); 182 } 183 serializer.endTag(null, XML_TAG_AGGREGATED_POWER_STATS); 184 serializer.flush(); 185 } 186 187 @NonNull createFromXml( TypedXmlPullParser parser, AggregatedPowerStatsConfig aggregatedPowerStatsConfig)188 public static AggregatedPowerStats createFromXml( 189 TypedXmlPullParser parser, AggregatedPowerStatsConfig aggregatedPowerStatsConfig) 190 throws XmlPullParserException, IOException { 191 AggregatedPowerStats stats = new AggregatedPowerStats(aggregatedPowerStatsConfig); 192 boolean inElement = false; 193 boolean skipToEnd = false; 194 int eventType = parser.getEventType(); 195 while (eventType != XmlPullParser.END_DOCUMENT 196 && !(eventType == XmlPullParser.END_TAG 197 && parser.getName().equals(XML_TAG_AGGREGATED_POWER_STATS))) { 198 if (!skipToEnd && eventType == XmlPullParser.START_TAG) { 199 switch (parser.getName()) { 200 case XML_TAG_AGGREGATED_POWER_STATS: 201 inElement = true; 202 break; 203 case PowerComponentAggregatedPowerStats.XML_TAG_POWER_COMPONENT: 204 if (!inElement) { 205 break; 206 } 207 208 int powerComponentId = parser.getAttributeInt(null, 209 PowerComponentAggregatedPowerStats.XML_ATTR_ID); 210 for (PowerComponentAggregatedPowerStats powerComponent : 211 stats.mPowerComponentStats) { 212 if (powerComponent.powerComponentId == powerComponentId) { 213 if (!powerComponent.readFromXml(parser)) { 214 skipToEnd = true; 215 } 216 break; 217 } 218 } 219 break; 220 } 221 } 222 eventType = parser.next(); 223 } 224 return stats; 225 } 226 dump(IndentingPrintWriter ipw)227 void dump(IndentingPrintWriter ipw) { 228 StringBuilder sb = new StringBuilder(); 229 long baseTime = 0; 230 for (int i = 0; i < mClockUpdates.size(); i++) { 231 ClockUpdate clockUpdate = mClockUpdates.get(i); 232 sb.setLength(0); 233 if (i == 0) { 234 baseTime = clockUpdate.monotonicTime; 235 sb.append("Start time: ") 236 .append(formatDateTime(clockUpdate.currentTime)) 237 .append(" (") 238 .append(baseTime) 239 .append(") duration: ") 240 .append(mDurationMs); 241 ipw.println(sb); 242 } else { 243 sb.setLength(0); 244 sb.append("Clock update: "); 245 TimeUtils.formatDuration( 246 clockUpdate.monotonicTime - baseTime, sb, 247 TimeUtils.HUNDRED_DAY_FIELD_LEN + 3); 248 sb.append(" ").append(formatDateTime(clockUpdate.currentTime)); 249 ipw.increaseIndent(); 250 ipw.println(sb); 251 ipw.decreaseIndent(); 252 } 253 } 254 255 ipw.println("Device"); 256 ipw.increaseIndent(); 257 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 258 stats.dumpDevice(ipw); 259 } 260 ipw.decreaseIndent(); 261 262 Set<Integer> uids = new HashSet<>(); 263 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 264 stats.collectUids(uids); 265 } 266 267 Integer[] allUids = uids.toArray(new Integer[uids.size()]); 268 Arrays.sort(allUids); 269 for (int uid : allUids) { 270 ipw.println(UserHandle.formatUid(uid)); 271 ipw.increaseIndent(); 272 for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) { 273 stats.dumpUid(ipw, uid); 274 } 275 ipw.decreaseIndent(); 276 } 277 } 278 formatDateTime(long timeInMillis)279 private static String formatDateTime(long timeInMillis) { 280 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); 281 format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT")); 282 return format.format(new Date(timeInMillis)); 283 } 284 285 @Override toString()286 public String toString() { 287 StringWriter sw = new StringWriter(); 288 dump(new IndentingPrintWriter(sw)); 289 return sw.toString(); 290 } 291 } 292