1 /* 2 * Copyright (C) 2020 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.content.Context; 20 import android.hardware.SensorManager; 21 import android.os.BatteryConsumer; 22 import android.os.BatteryStats; 23 import android.os.BatteryUsageStats; 24 import android.os.BatteryUsageStatsQuery; 25 import android.os.Process; 26 import android.os.UidBatteryConsumer; 27 import android.util.Log; 28 import android.util.Slog; 29 import android.util.SparseArray; 30 import android.util.SparseBooleanArray; 31 32 import com.android.internal.os.Clock; 33 import com.android.internal.os.CpuScalingPolicies; 34 import com.android.internal.os.PowerProfile; 35 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 40 /** 41 * Uses accumulated battery stats data and PowerCalculators to produce power 42 * usage data attributed to subsystems and UIDs. 43 */ 44 public class BatteryUsageStatsProvider { 45 private static final String TAG = "BatteryUsageStatsProv"; 46 private final Context mContext; 47 private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray(); 48 private final PowerStatsExporter mPowerStatsExporter; 49 private final PowerStatsStore mPowerStatsStore; 50 private final PowerProfile mPowerProfile; 51 private final CpuScalingPolicies mCpuScalingPolicies; 52 private final Clock mClock; 53 private final Object mLock = new Object(); 54 private List<PowerCalculator> mPowerCalculators; 55 BatteryUsageStatsProvider(Context context, PowerStatsExporter powerStatsExporter, PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies, PowerStatsStore powerStatsStore, Clock clock)56 public BatteryUsageStatsProvider(Context context, 57 PowerStatsExporter powerStatsExporter, 58 PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies, 59 PowerStatsStore powerStatsStore, Clock clock) { 60 mContext = context; 61 mPowerStatsExporter = powerStatsExporter; 62 mPowerStatsStore = powerStatsStore; 63 mPowerProfile = powerProfile; 64 mCpuScalingPolicies = cpuScalingPolicies; 65 mClock = clock; 66 } 67 getPowerCalculators()68 private List<PowerCalculator> getPowerCalculators() { 69 synchronized (mLock) { 70 if (mPowerCalculators == null) { 71 mPowerCalculators = new ArrayList<>(); 72 73 // Power calculators are applied in the order of registration 74 mPowerCalculators.add(new BatteryChargeCalculator()); 75 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) { 76 mPowerCalculators.add( 77 new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile)); 78 } 79 mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile)); 80 mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile)); 81 if (!BatteryStats.checkWifiOnly(mContext)) { 82 if (!mPowerStatsExporterEnabled.get( 83 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) { 84 mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile)); 85 } 86 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) { 87 mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile)); 88 } 89 } 90 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_WIFI)) { 91 mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile)); 92 } 93 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) { 94 mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile)); 95 } 96 mPowerCalculators.add(new SensorPowerCalculator( 97 mContext.getSystemService(SensorManager.class))); 98 mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile)); 99 mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); 100 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) { 101 mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); 102 } 103 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) { 104 mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile)); 105 } 106 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) { 107 mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile)); 108 } 109 mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile)); 110 mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile)); 111 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile)); 112 mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile)); 113 mPowerCalculators.add(new UserPowerCalculator()); 114 115 if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { 116 // It is important that SystemServicePowerCalculator be applied last, 117 // because it re-attributes some of the power estimated by the other 118 // calculators. 119 mPowerCalculators.add( 120 new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile)); 121 } 122 } 123 } 124 return mPowerCalculators; 125 } 126 127 /** 128 * Returns true if the last update was too long ago for the tolerances specified 129 * by the supplied queries. 130 */ shouldUpdateStats(List<BatteryUsageStatsQuery> queries, long elapsedRealtime, long lastUpdateTimeStampMs)131 public static boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries, 132 long elapsedRealtime, long lastUpdateTimeStampMs) { 133 long allowableStatsAge = Long.MAX_VALUE; 134 for (int i = queries.size() - 1; i >= 0; i--) { 135 BatteryUsageStatsQuery query = queries.get(i); 136 allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge()); 137 } 138 139 return elapsedRealtime - lastUpdateTimeStampMs > allowableStatsAge; 140 } 141 142 /** 143 * Returns snapshots of battery attribution data, one per supplied query. 144 */ getBatteryUsageStats(BatteryStatsImpl stats, List<BatteryUsageStatsQuery> queries)145 public List<BatteryUsageStats> getBatteryUsageStats(BatteryStatsImpl stats, 146 List<BatteryUsageStatsQuery> queries) { 147 ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size()); 148 synchronized (stats) { 149 stats.prepareForDumpLocked(); 150 } 151 final long currentTimeMillis = mClock.currentTimeMillis(); 152 for (int i = 0; i < queries.size(); i++) { 153 results.add(getBatteryUsageStats(stats, queries.get(i), currentTimeMillis)); 154 } 155 156 return results; 157 } 158 159 /** 160 * Returns a snapshot of battery attribution data. 161 */ getBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query)162 public BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats, 163 BatteryUsageStatsQuery query) { 164 return getBatteryUsageStats(stats, query, mClock.currentTimeMillis()); 165 } 166 getBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long currentTimeMs)167 private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats, 168 BatteryUsageStatsQuery query, long currentTimeMs) { 169 if (query.getToTimestamp() == 0) { 170 return getCurrentBatteryUsageStats(stats, query, currentTimeMs); 171 } else { 172 return getAggregatedBatteryUsageStats(stats, query); 173 } 174 } 175 getCurrentBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long currentTimeMs)176 private BatteryUsageStats getCurrentBatteryUsageStats(BatteryStatsImpl stats, 177 BatteryUsageStatsQuery query, long currentTimeMs) { 178 final long realtimeUs = mClock.elapsedRealtime() * 1000; 179 final long uptimeUs = mClock.uptimeMillis() * 1000; 180 181 final boolean includePowerModels = (query.getFlags() 182 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; 183 final boolean includeProcessStateData = ((query.getFlags() 184 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0) 185 && stats.isProcessStateDataAvailable(); 186 final boolean includeVirtualUids = ((query.getFlags() 187 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0); 188 final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold(); 189 190 final BatteryUsageStats.Builder batteryUsageStatsBuilder; 191 long monotonicStartTime, monotonicEndTime; 192 synchronized (stats) { 193 monotonicStartTime = stats.getMonotonicStartTime(); 194 monotonicEndTime = stats.getMonotonicEndTime(); 195 196 batteryUsageStatsBuilder = new BatteryUsageStats.Builder( 197 stats.getCustomEnergyConsumerNames(), includePowerModels, 198 includeProcessStateData, minConsumedPowerThreshold); 199 200 // TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration 201 // of batteryUsageStats sessions to wall-clock adjustments 202 batteryUsageStatsBuilder.setStatsStartTimestamp(stats.getStartClockTime()); 203 batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs); 204 SparseArray<? extends BatteryStats.Uid> uidStats = stats.getUidStats(); 205 for (int i = uidStats.size() - 1; i >= 0; i--) { 206 final BatteryStats.Uid uid = uidStats.valueAt(i); 207 if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) { 208 continue; 209 } 210 211 batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid) 212 .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND, 213 getProcessBackgroundTimeMs(uid, realtimeUs)) 214 .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND, 215 getProcessForegroundTimeMs(uid, realtimeUs)) 216 .setTimeInProcessStateMs( 217 UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 218 getProcessForegroundServiceTimeMs(uid, realtimeUs)); 219 } 220 221 final int[] powerComponents = query.getPowerComponents(); 222 final List<PowerCalculator> powerCalculators = getPowerCalculators(); 223 for (int i = 0, count = powerCalculators.size(); i < count; i++) { 224 PowerCalculator powerCalculator = powerCalculators.get(i); 225 if (powerComponents != null) { 226 boolean include = false; 227 for (int powerComponent : powerComponents) { 228 if (powerCalculator.isPowerComponentSupported(powerComponent)) { 229 include = true; 230 break; 231 } 232 } 233 if (!include) { 234 continue; 235 } 236 } 237 powerCalculator.calculate(batteryUsageStatsBuilder, stats, realtimeUs, uptimeUs, 238 query); 239 } 240 241 if ((query.getFlags() 242 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) { 243 batteryUsageStatsBuilder.setBatteryHistory(stats.copyHistory()); 244 } 245 } 246 247 if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) { 248 mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder, 249 monotonicStartTime, monotonicEndTime); 250 } 251 252 BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build(); 253 if (includeProcessStateData) { 254 verify(batteryUsageStats); 255 } 256 return batteryUsageStats; 257 } 258 259 // STOPSHIP(b/229906525): remove verification before shipping 260 private static boolean sErrorReported; verify(BatteryUsageStats stats)261 private void verify(BatteryUsageStats stats) { 262 if (sErrorReported) { 263 return; 264 } 265 266 final double precision = 2.0; // Allow rounding errors up to 2 mAh 267 final int[] components = 268 {BatteryConsumer.POWER_COMPONENT_CPU, 269 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 270 BatteryConsumer.POWER_COMPONENT_WIFI, 271 BatteryConsumer.POWER_COMPONENT_BLUETOOTH}; 272 final int[] states = 273 {BatteryConsumer.PROCESS_STATE_FOREGROUND, 274 BatteryConsumer.PROCESS_STATE_BACKGROUND, 275 BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 276 BatteryConsumer.PROCESS_STATE_CACHED}; 277 for (UidBatteryConsumer ubc : stats.getUidBatteryConsumers()) { 278 for (int component : components) { 279 double consumedPower = ubc.getConsumedPower(ubc.getKey(component)); 280 double sumStates = 0; 281 for (int state : states) { 282 sumStates += ubc.getConsumedPower(ubc.getKey(component, state)); 283 } 284 if (sumStates > consumedPower + precision) { 285 String error = "Sum of states exceeds total. UID = " + ubc.getUid() + " " 286 + BatteryConsumer.powerComponentIdToString(component) 287 + " total = " + consumedPower + " states = " + sumStates; 288 if (!sErrorReported) { 289 Slog.wtf(TAG, error); 290 sErrorReported = true; 291 } else { 292 Slog.e(TAG, error); 293 } 294 return; 295 } 296 } 297 } 298 } 299 getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs)300 private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { 301 final long topStateDurationUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP, 302 realtimeUs, BatteryStats.STATS_SINCE_CHARGED); 303 long foregroundActivityDurationUs = 0; 304 final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer(); 305 if (foregroundActivityTimer != null) { 306 foregroundActivityDurationUs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs, 307 BatteryStats.STATS_SINCE_CHARGED); 308 } 309 310 // Use the min value of STATE_TOP time and foreground activity time, since both of these 311 // times are imprecise 312 long totalForegroundDurationUs = Math.min(topStateDurationUs, foregroundActivityDurationUs); 313 314 totalForegroundDurationUs += uid.getProcessStateTime( 315 BatteryStats.Uid.PROCESS_STATE_FOREGROUND, realtimeUs, 316 BatteryStats.STATS_SINCE_CHARGED); 317 318 return totalForegroundDurationUs / 1000; 319 } 320 getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs)321 private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { 322 return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 323 realtimeUs, BatteryStats.STATS_SINCE_CHARGED) 324 / 1000; 325 } 326 getProcessForegroundServiceTimeMs(BatteryStats.Uid uid, long realtimeUs)327 private long getProcessForegroundServiceTimeMs(BatteryStats.Uid uid, long realtimeUs) { 328 return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, 329 realtimeUs, BatteryStats.STATS_SINCE_CHARGED) 330 / 1000; 331 } 332 getAggregatedBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query)333 private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats, 334 BatteryUsageStatsQuery query) { 335 final boolean includePowerModels = (query.getFlags() 336 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; 337 final boolean includeProcessStateData = ((query.getFlags() 338 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0) 339 && stats.isProcessStateDataAvailable(); 340 final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold(); 341 342 final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames(); 343 final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( 344 customEnergyConsumerNames, includePowerModels, includeProcessStateData, 345 minConsumedPowerThreshold); 346 if (mPowerStatsStore == null) { 347 Log.e(TAG, "PowerStatsStore is unavailable"); 348 return builder.build(); 349 } 350 351 List<PowerStatsSpan.Metadata> toc = mPowerStatsStore.getTableOfContents(); 352 for (PowerStatsSpan.Metadata spanMetadata : toc) { 353 if (!spanMetadata.getSections().contains(BatteryUsageStatsSection.TYPE)) { 354 continue; 355 } 356 357 // BatteryUsageStatsQuery is expressed in terms of wall-clock time range for the 358 // session end time. 359 // 360 // The following algorithm is correct when there is only one time frame in the span. 361 // When the wall-clock time is adjusted in the middle of an stats span, 362 // constraining it by wall-clock time becomes ambiguous. In this case, the algorithm 363 // only covers some situations, but not others. When using the resulting data for 364 // analysis, we should always pay attention to the full set of included timeframes. 365 // TODO(b/298459065): switch to monotonic clock 366 long minTime = Long.MAX_VALUE; 367 long maxTime = 0; 368 for (PowerStatsSpan.TimeFrame timeFrame : spanMetadata.getTimeFrames()) { 369 long spanEndTime = timeFrame.startTime + timeFrame.duration; 370 minTime = Math.min(minTime, spanEndTime); 371 maxTime = Math.max(maxTime, spanEndTime); 372 } 373 374 // Per BatteryUsageStatsQuery API, the "from" timestamp is *exclusive*, 375 // while the "to" timestamp is *inclusive*. 376 boolean isInRange = 377 (query.getFromTimestamp() == 0 || minTime > query.getFromTimestamp()) 378 && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp()); 379 if (!isInRange) { 380 continue; 381 } 382 383 PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan( 384 spanMetadata.getId(), BatteryUsageStatsSection.TYPE); 385 if (powerStatsSpan == null) { 386 continue; 387 } 388 389 for (PowerStatsSpan.Section section : powerStatsSpan.getSections()) { 390 BatteryUsageStats snapshot = 391 ((BatteryUsageStatsSection) section).getBatteryUsageStats(); 392 if (!Arrays.equals(snapshot.getCustomPowerComponentNames(), 393 customEnergyConsumerNames)) { 394 Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different " 395 + "custom power components: " 396 + Arrays.toString(snapshot.getCustomPowerComponentNames())); 397 continue; 398 } 399 400 if (includeProcessStateData && !snapshot.isProcessStateDataIncluded()) { 401 Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which " 402 + " does not include process state data"); 403 continue; 404 } 405 406 builder.add(snapshot); 407 } 408 } 409 return builder.build(); 410 } 411 412 /** 413 * Specify whether PowerStats based attribution is supported for the specified component. 414 */ setPowerStatsExporterEnabled(int powerComponentId, boolean enabled)415 public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) { 416 mPowerStatsExporterEnabled.put(powerComponentId, enabled); 417 } 418 } 419