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.processor; 18 19 import android.annotation.CheckResult; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.BatteryStats; 23 import android.os.UserHandle; 24 import android.util.IndentingPrintWriter; 25 import android.util.IntArray; 26 import android.util.Slog; 27 import android.util.SparseArray; 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.util.Arrays; 39 import java.util.function.IntConsumer; 40 41 /** 42 * Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class 43 * treats stats as arrays of nonspecific longs. Subclasses contain specific logic to interpret those 44 * longs and use them for calculations such as power attribution. They may use meta-data supplied 45 * as part of the {@link PowerStats.Descriptor}. 46 */ 47 class PowerComponentAggregatedPowerStats { 48 private static final String TAG = "AggregatePowerStats"; 49 static final String XML_TAG_POWER_COMPONENT = "power_component"; 50 static final String XML_ATTR_ID = "id"; 51 private static final String XML_TAG_DEVICE_STATS = "device-stats"; 52 private static final String XML_TAG_STATE_STATS = "state-stats"; 53 private static final String XML_ATTR_KEY = "key"; 54 private static final String XML_TAG_UID_STATS = "uid-stats"; 55 private static final String XML_ATTR_UID = "uid"; 56 private static final long UNKNOWN = -1; 57 58 public final int powerComponentId; 59 @NonNull 60 private final AggregatedPowerStats mAggregatedPowerStats; 61 @NonNull 62 private final AggregatedPowerStatsConfig.PowerComponent mConfig; 63 private final MultiStateStats.States[] mDeviceStateConfig; 64 private final MultiStateStats.States[] mUidStateConfig; 65 private final int[] mDeviceStates; 66 67 private PowerStatsProcessor mProcessor; 68 private MultiStateStats.Factory mStatsFactory; 69 private MultiStateStats.Factory mStateStatsFactory; 70 private MultiStateStats.Factory mUidStatsFactory; 71 private PowerStats.Descriptor mPowerStatsDescriptor; 72 private long mPowerStatsTimestamp; 73 private MultiStateStats mDeviceStats; 74 private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>(); 75 private final SparseArray<UidStats> mUidStats = new SparseArray<>(); 76 77 private static class UidStats { 78 public int[] states; 79 public MultiStateStats stats; 80 public boolean hasPowerStats; 81 public boolean updated; 82 } 83 PowerComponentAggregatedPowerStats(@onNull AggregatedPowerStats aggregatedPowerStats, @NonNull AggregatedPowerStatsConfig.PowerComponent config)84 PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats, 85 @NonNull AggregatedPowerStatsConfig.PowerComponent config) { 86 mAggregatedPowerStats = aggregatedPowerStats; 87 mConfig = config; 88 powerComponentId = config.getPowerComponentId(); 89 mDeviceStateConfig = config.getDeviceStateConfig(); 90 mUidStateConfig = config.getUidStateConfig(); 91 mDeviceStates = new int[mDeviceStateConfig.length]; 92 mPowerStatsTimestamp = UNKNOWN; 93 } 94 95 @NonNull getAggregatedPowerStats()96 AggregatedPowerStats getAggregatedPowerStats() { 97 return mAggregatedPowerStats; 98 } 99 100 @NonNull getConfig()101 public AggregatedPowerStatsConfig.PowerComponent getConfig() { 102 return mConfig; 103 } 104 105 @Nullable getPowerStatsDescriptor()106 public PowerStats.Descriptor getPowerStatsDescriptor() { 107 return mPowerStatsDescriptor; 108 } 109 setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor)110 public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) { 111 mPowerStatsDescriptor = powerStatsDescriptor; 112 } 113 start(long timestampMs)114 void start(long timestampMs) { 115 if (mProcessor == null) { 116 mProcessor = mConfig.createProcessor(); 117 } 118 mProcessor.start(this, timestampMs); 119 } 120 finish(long timestampMs)121 void finish(long timestampMs) { 122 mProcessor.finish(this, timestampMs); 123 } 124 noteStateChange(BatteryStats.HistoryItem item)125 void noteStateChange(BatteryStats.HistoryItem item) { 126 mProcessor.noteStateChange(this, item); 127 } 128 noteBatteryLevel(int batteryLevel, int batteryChargeUah, long timestampMs)129 public void noteBatteryLevel(int batteryLevel, int batteryChargeUah, long timestampMs) { 130 mProcessor.noteBatteryLevel(batteryLevel, batteryChargeUah, timestampMs); 131 } 132 setState(@ggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)133 void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, 134 long timestampMs) { 135 if (mDeviceStats == null) { 136 createDeviceStats(timestampMs); 137 } 138 139 mDeviceStates[stateId] = state; 140 141 if (mDeviceStateConfig[stateId].isTracked()) { 142 if (mDeviceStats != null) { 143 mDeviceStats.setState(stateId, state, timestampMs); 144 } 145 for (int i = mStateStats.size() - 1; i >= 0; i--) { 146 MultiStateStats stateStats = mStateStats.valueAt(i); 147 stateStats.setState(stateId, state, timestampMs); 148 } 149 } 150 151 int uidStateId = MultiStateStats.States 152 .findTrackedStateByName(mUidStateConfig, mDeviceStateConfig[stateId].getName()); 153 if (uidStateId != MultiStateStats.STATE_DOES_NOT_EXIST 154 && mUidStateConfig[uidStateId].isTracked()) { 155 for (int i = mUidStats.size() - 1; i >= 0; i--) { 156 PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i); 157 if (uidStats.stats == null) { 158 createUidStats(uidStats, timestampMs); 159 } 160 161 uidStats.states[uidStateId] = state; 162 if (uidStats.stats != null) { 163 uidStats.stats.setState(uidStateId, state, timestampMs); 164 } 165 } 166 } 167 } 168 setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)169 void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, 170 long timestampMs) { 171 mProcessor.setUidState(this, uid, stateId, state, timestampMs); 172 } 173 setProcessedUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state, long timestampMs)174 void setProcessedUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, 175 int state, long timestampMs) { 176 if (!mUidStateConfig[stateId].isTracked()) { 177 return; 178 } 179 180 UidStats uidStats = getUidStats(uid); 181 if (uidStats.stats == null) { 182 createUidStats(uidStats, timestampMs); 183 } 184 185 uidStats.states[stateId] = state; 186 187 if (uidStats.stats != null) { 188 uidStats.stats.setState(stateId, state, timestampMs); 189 } 190 } 191 setDeviceStats(int[] states, long[] values)192 void setDeviceStats(int[] states, long[] values) { 193 if (mDeviceStats == null) { 194 createDeviceStats(0); 195 } 196 mDeviceStats.setStats(states, values); 197 } 198 setUidStats(int uid, int[] states, long[] values)199 void setUidStats(int uid, int[] states, long[] values) { 200 UidStats uidStats = getUidStats(uid); 201 if (uidStats.stats == null) { 202 createUidStats(uidStats, mPowerStatsTimestamp); 203 } 204 uidStats.hasPowerStats = true; 205 uidStats.stats.setStats(states, values); 206 } 207 isCompatible(PowerStats powerStats)208 boolean isCompatible(PowerStats powerStats) { 209 return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor); 210 } 211 addPowerStats(PowerStats powerStats, long timestampMs)212 void addPowerStats(PowerStats powerStats, long timestampMs) { 213 // Should call powerStats.addProcessedPowerStats 214 mProcessor.addPowerStats(this, powerStats, timestampMs); 215 } 216 217 /** 218 * Should be called ONLY by PowerStatsProcessor.processPowerStats. 219 */ addProcessedPowerStats(PowerStats powerStats, long timestampMs)220 void addProcessedPowerStats(PowerStats powerStats, long timestampMs) { 221 mPowerStatsDescriptor = powerStats.descriptor; 222 223 if (mDeviceStats == null) { 224 createDeviceStats(timestampMs); 225 } 226 227 for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) { 228 int key = powerStats.stateStats.keyAt(i); 229 MultiStateStats stateStats = mStateStats.get(key); 230 if (stateStats == null) { 231 stateStats = createStateStats(key, timestampMs); 232 } 233 stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs); 234 } 235 mDeviceStats.increment(powerStats.stats, timestampMs); 236 237 for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) { 238 int uid = powerStats.uidStats.keyAt(i); 239 PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid); 240 if (uidStats.stats == null) { 241 createUidStats(uidStats, timestampMs); 242 } 243 uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs); 244 uidStats.updated = true; 245 uidStats.hasPowerStats = true; 246 } 247 248 // For UIDs not mentioned in the PowerStats object, we must assume a 0 increment. 249 // It is essential to call `stats.increment(zero)` in order to record the new 250 // timestamp, which will ensure correct proportional attribution across all UIDs 251 for (int i = mUidStats.size() - 1; i >= 0; i--) { 252 PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i); 253 if (!uidStats.updated && uidStats.stats != null) { 254 // Null stands for an array of zeros 255 uidStats.stats.increment(null, timestampMs); 256 } 257 uidStats.updated = false; 258 } 259 260 mPowerStatsTimestamp = timestampMs; 261 } 262 reset()263 void reset() { 264 mStatsFactory = null; 265 mUidStatsFactory = null; 266 mDeviceStats = null; 267 mStateStats.clear(); 268 for (int i = mUidStats.size() - 1; i >= 0; i--) { 269 mUidStats.valueAt(i).stats = null; 270 mUidStats.valueAt(i).hasPowerStats = false; 271 } 272 } 273 getUidStats(int uid)274 private UidStats getUidStats(int uid) { 275 UidStats uidStats = mUidStats.get(uid); 276 if (uidStats == null) { 277 uidStats = new UidStats(); 278 uidStats.states = new int[mUidStateConfig.length]; 279 for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) { 280 if (mUidStateConfig[stateId].isTracked()) { 281 int deviceStateId = MultiStateStats.States.findTrackedStateByName( 282 mDeviceStateConfig, mUidStateConfig[stateId].getName()); 283 if (deviceStateId != MultiStateStats.STATE_DOES_NOT_EXIST 284 && mDeviceStateConfig[deviceStateId].isTracked()) { 285 uidStats.states[stateId] = mDeviceStates[deviceStateId]; 286 } 287 } 288 } 289 mUidStats.put(uid, uidStats); 290 } 291 return uidStats; 292 } 293 getUids()294 IntArray getUids() { 295 IntArray uids = new IntArray(mUidStats.size()); 296 for (int i = mUidStats.size() - 1; i >= 0; i--) { 297 UidStats uidStats = mUidStats.valueAt(i); 298 if (uidStats.stats != null) { 299 uids.add(mUidStats.keyAt(i)); 300 } 301 } 302 return uids; 303 } 304 getActiveUids()305 IntArray getActiveUids() { 306 IntArray uids = new IntArray(mUidStats.size()); 307 for (int i = mUidStats.size() - 1; i >= 0; i--) { 308 UidStats uidStats = mUidStats.valueAt(i); 309 if (uidStats.hasPowerStats) { 310 uids.add(mUidStats.keyAt(i)); 311 } 312 } 313 return uids; 314 } 315 316 /** 317 * Populates outValues with the stats for the specified states. If the stats are all 0, 318 * returns false, leaving outValues unchanged. 319 */ 320 @CheckResult getDeviceStats(long[] outValues, int[] deviceStates)321 boolean getDeviceStats(long[] outValues, int[] deviceStates) { 322 if (deviceStates.length != mDeviceStateConfig.length) { 323 throw new IllegalArgumentException( 324 "Invalid number of tracked states: " + deviceStates.length 325 + " expected: " + mDeviceStateConfig.length); 326 } 327 if (mDeviceStats != null) { 328 return mDeviceStats.getStats(outValues, deviceStates); 329 } 330 return false; 331 } 332 333 /** 334 * Populates outValues with the stats for the specified key and device states. If the stats 335 * are all 0, returns false, leaving outValues unchanged. 336 */ 337 @CheckResult getStateStats(long[] outValues, int key, int[] deviceStates)338 boolean getStateStats(long[] outValues, int key, int[] deviceStates) { 339 if (deviceStates.length != mDeviceStateConfig.length) { 340 throw new IllegalArgumentException( 341 "Invalid number of tracked states: " + deviceStates.length 342 + " expected: " + mDeviceStateConfig.length); 343 } 344 MultiStateStats stateStats = mStateStats.get(key); 345 if (stateStats != null) { 346 return stateStats.getStats(outValues, deviceStates); 347 } 348 return false; 349 } 350 forEachStateStatsKey(IntConsumer consumer)351 void forEachStateStatsKey(IntConsumer consumer) { 352 for (int i = mStateStats.size() - 1; i >= 0; i--) { 353 consumer.accept(mStateStats.keyAt(i)); 354 } 355 } 356 357 /** 358 * Populates outValues with the stats for the specified UID and UID states. If the stats are 359 * all 0, returns false, leaving outValues unchanged. 360 */ 361 @CheckResult getUidStats(long[] outValues, int uid, int[] uidStates)362 boolean getUidStats(long[] outValues, int uid, int[] uidStates) { 363 if (uidStates.length != mUidStateConfig.length) { 364 throw new IllegalArgumentException( 365 "Invalid number of tracked states: " + uidStates.length 366 + " expected: " + mUidStateConfig.length); 367 } 368 UidStats uidStats = mUidStats.get(uid); 369 if (uidStats != null && uidStats.stats != null) { 370 return uidStats.stats.getStats(outValues, uidStates); 371 } 372 return false; 373 } 374 createDeviceStats(long timestampMs)375 private void createDeviceStats(long timestampMs) { 376 if (mStatsFactory == null) { 377 if (mPowerStatsDescriptor == null) { 378 return; 379 } 380 mStatsFactory = new MultiStateStats.Factory( 381 mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig); 382 } 383 384 mDeviceStats = mStatsFactory.create(); 385 if (mPowerStatsTimestamp != UNKNOWN) { 386 timestampMs = mPowerStatsTimestamp; 387 } 388 if (timestampMs != UNKNOWN) { 389 for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) { 390 int state = mDeviceStates[stateId]; 391 mDeviceStats.setState(stateId, state, timestampMs); 392 for (int i = mStateStats.size() - 1; i >= 0; i--) { 393 MultiStateStats stateStats = mStateStats.valueAt(i); 394 stateStats.setState(stateId, state, timestampMs); 395 } 396 } 397 } 398 } 399 createStateStats(int key, long timestampMs)400 private MultiStateStats createStateStats(int key, long timestampMs) { 401 if (mStateStatsFactory == null) { 402 if (mPowerStatsDescriptor == null) { 403 return null; 404 } 405 mStateStatsFactory = new MultiStateStats.Factory( 406 mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig); 407 } 408 409 MultiStateStats stateStats = mStateStatsFactory.create(); 410 mStateStats.put(key, stateStats); 411 if (mDeviceStats != null) { 412 stateStats.copyStatesFrom(mDeviceStats); 413 } 414 415 return stateStats; 416 } 417 createUidStats(UidStats uidStats, long timestampMs)418 private void createUidStats(UidStats uidStats, long timestampMs) { 419 if (mUidStatsFactory == null) { 420 if (mPowerStatsDescriptor == null) { 421 return; 422 } 423 mUidStatsFactory = new MultiStateStats.Factory( 424 mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig); 425 } 426 427 uidStats.stats = mUidStatsFactory.create(); 428 429 if (mPowerStatsTimestamp != UNKNOWN) { 430 timestampMs = mPowerStatsTimestamp; 431 } 432 if (timestampMs != UNKNOWN) { 433 for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) { 434 uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs); 435 } 436 } 437 } 438 copyStatesFrom(PowerComponentAggregatedPowerStats source)439 void copyStatesFrom(PowerComponentAggregatedPowerStats source) { 440 if (source.mDeviceStates.length == mDeviceStates.length) { 441 System.arraycopy(source.mDeviceStates, 0, mDeviceStates, 0, mDeviceStates.length); 442 if (source.mDeviceStats != null) { 443 createDeviceStats(0); 444 if (mDeviceStats != null) { 445 mDeviceStats.copyStatesFrom(source.mDeviceStats); 446 } 447 } 448 } else { 449 Slog.wtf(TAG, "State configurations have different lengths: " 450 + source.mDeviceStates.length + " vs " + mDeviceStates.length); 451 } 452 for (int i = source.mUidStats.size() - 1; i >= 0; i--) { 453 int uid = source.mUidStats.keyAt(i); 454 UidStats sourceUidStats = source.mUidStats.valueAt(i); 455 if (sourceUidStats.states == null) { 456 continue; 457 } 458 UidStats uidStats = new UidStats(); 459 uidStats.states = Arrays.copyOf(sourceUidStats.states, sourceUidStats.states.length); 460 if (sourceUidStats.stats != null) { 461 createUidStats(uidStats, 0); 462 if (uidStats.stats != null) { 463 uidStats.stats.copyStatesFrom(sourceUidStats.stats); 464 } 465 } 466 mUidStats.put(uid, uidStats); 467 } 468 } 469 writeXml(TypedXmlSerializer serializer)470 public void writeXml(TypedXmlSerializer serializer) throws IOException { 471 // No stats aggregated - can skip writing XML altogether 472 if (mPowerStatsDescriptor == null) { 473 return; 474 } 475 476 serializer.startTag(null, XML_TAG_POWER_COMPONENT); 477 serializer.attributeInt(null, XML_ATTR_ID, powerComponentId); 478 mPowerStatsDescriptor.writeXml(serializer); 479 480 if (mDeviceStats != null) { 481 serializer.startTag(null, XML_TAG_DEVICE_STATS); 482 mDeviceStats.writeXml(serializer); 483 serializer.endTag(null, XML_TAG_DEVICE_STATS); 484 } 485 486 for (int i = 0; i < mStateStats.size(); i++) { 487 serializer.startTag(null, XML_TAG_STATE_STATS); 488 serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i)); 489 mStateStats.valueAt(i).writeXml(serializer); 490 serializer.endTag(null, XML_TAG_STATE_STATS); 491 } 492 493 for (int i = mUidStats.size() - 1; i >= 0; i--) { 494 int uid = mUidStats.keyAt(i); 495 UidStats uidStats = mUidStats.valueAt(i); 496 if (uidStats.stats != null) { 497 serializer.startTag(null, XML_TAG_UID_STATS); 498 serializer.attributeInt(null, XML_ATTR_UID, uid); 499 uidStats.stats.writeXml(serializer); 500 serializer.endTag(null, XML_TAG_UID_STATS); 501 } 502 } 503 504 serializer.endTag(null, XML_TAG_POWER_COMPONENT); 505 serializer.flush(); 506 } 507 readFromXml(TypedXmlPullParser parser)508 public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException, 509 IOException { 510 String outerTag = parser.getName(); 511 int eventType = parser.getEventType(); 512 while (eventType != XmlPullParser.END_DOCUMENT 513 && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) { 514 if (eventType == XmlPullParser.START_TAG) { 515 switch (parser.getName()) { 516 case PowerStats.Descriptor.XML_TAG_DESCRIPTOR: 517 mPowerStatsDescriptor = PowerStats.Descriptor.createFromXml(parser); 518 if (mPowerStatsDescriptor == null) { 519 return false; 520 } 521 break; 522 case XML_TAG_DEVICE_STATS: 523 if (mDeviceStats == null) { 524 createDeviceStats(UNKNOWN); 525 } 526 if (!mDeviceStats.readFromXml(parser)) { 527 return false; 528 } 529 break; 530 case XML_TAG_STATE_STATS: 531 int key = parser.getAttributeInt(null, XML_ATTR_KEY); 532 MultiStateStats stats = mStateStats.get(key); 533 if (stats == null) { 534 stats = createStateStats(key, UNKNOWN); 535 } 536 if (!stats.readFromXml(parser)) { 537 return false; 538 } 539 break; 540 case XML_TAG_UID_STATS: 541 int uid = parser.getAttributeInt(null, XML_ATTR_UID); 542 UidStats uidStats = getUidStats(uid); 543 if (uidStats.stats == null) { 544 createUidStats(uidStats, UNKNOWN); 545 } 546 uidStats.hasPowerStats = true; 547 if (!uidStats.stats.readFromXml(parser)) { 548 return false; 549 } 550 break; 551 } 552 } 553 eventType = parser.next(); 554 } 555 return true; 556 } 557 dumpDevice(IndentingPrintWriter ipw)558 void dumpDevice(IndentingPrintWriter ipw) { 559 if (mDeviceStats != null) { 560 dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null, 561 mPowerStatsDescriptor.getDeviceStatsFormatter()); 562 } 563 564 if (mStateStats.size() != 0) { 565 ipw.increaseIndent(); 566 String header = mPowerStatsDescriptor.name + " states"; 567 PowerStats.PowerStatsFormatter formatter = 568 mPowerStatsDescriptor.getStateStatsFormatter(); 569 for (int i = 0; i < mStateStats.size(); i++) { 570 int key = mStateStats.keyAt(i); 571 String stateLabel = mPowerStatsDescriptor.getStateLabel(key); 572 MultiStateStats stateStats = mStateStats.valueAt(i); 573 dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter); 574 } 575 ipw.decreaseIndent(); 576 } 577 } 578 dumpUid(IndentingPrintWriter ipw, int uid)579 void dumpUid(IndentingPrintWriter ipw, int uid) { 580 UidStats uidStats = mUidStats.get(uid); 581 if (uidStats != null && uidStats.stats != null) { 582 dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null, 583 mPowerStatsDescriptor.getUidStatsFormatter()); 584 } 585 } 586 dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats, String header, String additionalLabel, PowerStats.PowerStatsFormatter statsFormatter)587 private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats, 588 String header, String additionalLabel, 589 PowerStats.PowerStatsFormatter statsFormatter) { 590 boolean[] firstLine = new boolean[]{true}; 591 long[] values = new long[stats.getDimensionCount()]; 592 MultiStateStats.States[] stateInfo = stats.getStates(); 593 MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> { 594 if (!stats.getStats(values, states)) { 595 return; 596 } 597 598 if (firstLine[0]) { 599 ipw.println(header); 600 ipw.increaseIndent(); 601 } 602 firstLine[0] = false; 603 StringBuilder sb = new StringBuilder(); 604 sb.append("("); 605 boolean first = true; 606 for (int i = 0; i < states.length; i++) { 607 if (stateInfo[i].isTracked()) { 608 if (!first) { 609 sb.append(" "); 610 } 611 first = false; 612 sb.append(stateInfo[i].getLabels()[states[i]]); 613 } 614 } 615 if (additionalLabel != null) { 616 sb.append(" ").append(additionalLabel); 617 } 618 sb.append(") ").append(statsFormatter.format(values)); 619 ipw.println(sb); 620 }); 621 if (!firstLine[0]) { 622 ipw.decreaseIndent(); 623 } 624 } 625 626 @Override toString()627 public String toString() { 628 StringWriter sw = new StringWriter(); 629 IndentingPrintWriter ipw = new IndentingPrintWriter(sw); 630 ipw.increaseIndent(); 631 dumpDevice(ipw); 632 ipw.decreaseIndent(); 633 634 int[] uids = new int[mUidStats.size()]; 635 for (int i = uids.length - 1; i >= 0; i--) { 636 uids[i] = mUidStats.keyAt(i); 637 } 638 Arrays.sort(uids); 639 for (int uid : uids) { 640 ipw.println(UserHandle.formatUid(uid)); 641 ipw.increaseIndent(); 642 dumpUid(ipw, uid); 643 ipw.decreaseIndent(); 644 } 645 646 ipw.flush(); 647 648 return sw.toString(); 649 } 650 } 651