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.powerstats; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.hardware.power.stats.Channel; 22 import android.hardware.power.stats.EnergyConsumer; 23 import android.hardware.power.stats.EnergyConsumerResult; 24 import android.hardware.power.stats.EnergyMeasurement; 25 import android.hardware.power.stats.PowerEntity; 26 import android.hardware.power.stats.StateResidencyResult; 27 import android.os.Binder; 28 import android.os.Environment; 29 import android.os.Handler; 30 import android.os.HandlerThread; 31 import android.os.Looper; 32 import android.os.UserHandle; 33 import android.power.PowerStatsInternal; 34 import android.util.IndentingPrintWriter; 35 import android.util.Slog; 36 37 import com.android.internal.annotations.GuardedBy; 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.util.DumpUtils; 40 import com.android.internal.util.function.pooled.PooledLambda; 41 import com.android.server.SystemService; 42 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; 43 import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils; 44 import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerUtils; 45 import com.android.server.powerstats.ProtoStreamUtils.PowerEntityUtils; 46 47 import java.io.File; 48 import java.io.FileDescriptor; 49 import java.io.PrintWriter; 50 import java.util.concurrent.CompletableFuture; 51 52 /** 53 * This class provides a system service that estimates system power usage 54 * per subsystem (modem, wifi, gps, display, etc) and provides those power 55 * estimates to subscribers. 56 */ 57 public class PowerStatsService extends SystemService { 58 private static final String TAG = PowerStatsService.class.getSimpleName(); 59 private static final boolean DEBUG = false; 60 private static final String DATA_STORAGE_SUBDIR = "powerstats"; 61 private static final int DATA_STORAGE_VERSION = 0; 62 private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION; 63 private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION; 64 private static final String RESIDENCY_FILENAME = 65 "log.powerstats.residency." + DATA_STORAGE_VERSION; 66 private static final String METER_CACHE_FILENAME = "meterCache"; 67 private static final String MODEL_CACHE_FILENAME = "modelCache"; 68 private static final String RESIDENCY_CACHE_FILENAME = "residencyCache"; 69 70 private final Injector mInjector; 71 private File mDataStoragePath; 72 73 private Context mContext; 74 @Nullable 75 private PowerStatsLogger mPowerStatsLogger; 76 @Nullable 77 private BatteryTrigger mBatteryTrigger; 78 @Nullable 79 private TimerTrigger mTimerTrigger; 80 @Nullable 81 private StatsPullAtomCallbackImpl mPullAtomCallback; 82 @Nullable 83 private PowerStatsInternal mPowerStatsInternal; 84 @Nullable 85 @GuardedBy("this") 86 private Looper mLooper; 87 @Nullable 88 @GuardedBy("this") 89 private EnergyConsumer[] mEnergyConsumers = null; 90 91 @VisibleForTesting 92 static class Injector { 93 @GuardedBy("this") 94 private IPowerStatsHALWrapper mPowerStatsHALWrapper; 95 createDataStoragePath()96 File createDataStoragePath() { 97 return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), 98 DATA_STORAGE_SUBDIR); 99 } 100 createMeterFilename()101 String createMeterFilename() { 102 return METER_FILENAME; 103 } 104 createModelFilename()105 String createModelFilename() { 106 return MODEL_FILENAME; 107 } 108 createResidencyFilename()109 String createResidencyFilename() { 110 return RESIDENCY_FILENAME; 111 } 112 createMeterCacheFilename()113 String createMeterCacheFilename() { 114 return METER_CACHE_FILENAME; 115 } 116 createModelCacheFilename()117 String createModelCacheFilename() { 118 return MODEL_CACHE_FILENAME; 119 } 120 createResidencyCacheFilename()121 String createResidencyCacheFilename() { 122 return RESIDENCY_CACHE_FILENAME; 123 } 124 createPowerStatsHALWrapperImpl()125 IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { 126 return PowerStatsHALWrapper.getPowerStatsHalImpl(); 127 } 128 getPowerStatsHALWrapperImpl()129 IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() { 130 synchronized (this) { 131 if (mPowerStatsHALWrapper == null) { 132 mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl(); 133 } 134 return mPowerStatsHALWrapper; 135 } 136 } 137 createPowerStatsLogger(Context context, Looper looper, File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper)138 PowerStatsLogger createPowerStatsLogger(Context context, Looper looper, 139 File dataStoragePath, String meterFilename, String meterCacheFilename, 140 String modelFilename, String modelCacheFilename, 141 String residencyFilename, String residencyCacheFilename, 142 IPowerStatsHALWrapper powerStatsHALWrapper) { 143 return new PowerStatsLogger(context, looper, dataStoragePath, 144 meterFilename, meterCacheFilename, 145 modelFilename, modelCacheFilename, 146 residencyFilename, residencyCacheFilename, 147 powerStatsHALWrapper); 148 } 149 createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger)150 BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) { 151 return new BatteryTrigger(context, powerStatsLogger, true /* trigger enabled */); 152 } 153 createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger)154 TimerTrigger createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger) { 155 return new TimerTrigger(context, powerStatsLogger, true /* trigger enabled */); 156 } 157 createStatsPullerImpl(Context context, PowerStatsInternal powerStatsInternal)158 StatsPullAtomCallbackImpl createStatsPullerImpl(Context context, 159 PowerStatsInternal powerStatsInternal) { 160 return new StatsPullAtomCallbackImpl(context, powerStatsInternal); 161 } 162 } 163 164 private final class BinderService extends Binder { 165 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)166 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 167 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 168 169 if (mPowerStatsLogger == null) { 170 Slog.e(TAG, "PowerStats HAL is not initialized. No data available."); 171 } else { 172 if (args.length > 0 && "--proto".equals(args[0])) { 173 if ("model".equals(args[1])) { 174 mPowerStatsLogger.writeModelDataToFile(fd); 175 } else if ("meter".equals(args[1])) { 176 mPowerStatsLogger.writeMeterDataToFile(fd); 177 } else if ("residency".equals(args[1])) { 178 mPowerStatsLogger.writeResidencyDataToFile(fd); 179 } 180 } else { 181 IndentingPrintWriter ipw = new IndentingPrintWriter(pw); 182 ipw.println("PowerStatsService dumpsys: available PowerEntities"); 183 PowerEntity[] powerEntity = getPowerStatsHal().getPowerEntityInfo(); 184 ipw.increaseIndent(); 185 PowerEntityUtils.dumpsys(powerEntity, ipw); 186 ipw.decreaseIndent(); 187 188 ipw.println("PowerStatsService dumpsys: available Channels"); 189 Channel[] channel = getPowerStatsHal().getEnergyMeterInfo(); 190 ipw.increaseIndent(); 191 ChannelUtils.dumpsys(channel, ipw); 192 ipw.decreaseIndent(); 193 194 ipw.println("PowerStatsService dumpsys: available EnergyConsumers"); 195 EnergyConsumer[] energyConsumer = getPowerStatsHal().getEnergyConsumerInfo(); 196 ipw.increaseIndent(); 197 EnergyConsumerUtils.dumpsys(energyConsumer, ipw); 198 ipw.decreaseIndent(); 199 200 ipw.println("PowerStatsService dumpsys: PowerStatsLogger stats"); 201 ipw.increaseIndent(); 202 mPowerStatsLogger.dump(ipw); 203 ipw.decreaseIndent(); 204 205 } 206 } 207 } 208 } 209 210 @Override onBootPhase(int phase)211 public void onBootPhase(int phase) { 212 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 213 onSystemServicesReady(); 214 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { 215 onBootCompleted(); 216 } 217 } 218 219 @Override onStart()220 public void onStart() { 221 if (getPowerStatsHal().isInitialized()) { 222 mPowerStatsInternal = new LocalService(); 223 publishLocalService(PowerStatsInternal.class, mPowerStatsInternal); 224 } 225 publishBinderService(Context.POWER_STATS_SERVICE, new BinderService()); 226 } 227 onSystemServicesReady()228 private void onSystemServicesReady() { 229 mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, mPowerStatsInternal); 230 } 231 232 @VisibleForTesting getDeleteMeterDataOnBoot()233 public boolean getDeleteMeterDataOnBoot() { 234 return mPowerStatsLogger.getDeleteMeterDataOnBoot(); 235 } 236 237 @VisibleForTesting getDeleteModelDataOnBoot()238 public boolean getDeleteModelDataOnBoot() { 239 return mPowerStatsLogger.getDeleteModelDataOnBoot(); 240 } 241 242 @VisibleForTesting getDeleteResidencyDataOnBoot()243 public boolean getDeleteResidencyDataOnBoot() { 244 return mPowerStatsLogger.getDeleteResidencyDataOnBoot(); 245 } 246 onBootCompleted()247 private void onBootCompleted() { 248 if (getPowerStatsHal().isInitialized()) { 249 if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers"); 250 mDataStoragePath = mInjector.createDataStoragePath(); 251 252 // Only start logger and triggers if initialization is successful. 253 mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(), 254 mDataStoragePath, mInjector.createMeterFilename(), 255 mInjector.createMeterCacheFilename(), mInjector.createModelFilename(), 256 mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(), 257 mInjector.createResidencyCacheFilename(), getPowerStatsHal()); 258 mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); 259 mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); 260 } else { 261 Slog.e(TAG, "Failed to start PowerStatsService loggers"); 262 } 263 } 264 getPowerStatsHal()265 private IPowerStatsHALWrapper getPowerStatsHal() { 266 return mInjector.getPowerStatsHALWrapperImpl(); 267 } 268 getLooper()269 private Looper getLooper() { 270 synchronized (this) { 271 if (mLooper == null) { 272 HandlerThread thread = new HandlerThread(TAG); 273 thread.start(); 274 return thread.getLooper(); 275 } 276 return mLooper; 277 } 278 } 279 getEnergyConsumerInfo()280 private EnergyConsumer[] getEnergyConsumerInfo() { 281 synchronized (this) { 282 if (mEnergyConsumers == null) { 283 mEnergyConsumers = getPowerStatsHal().getEnergyConsumerInfo(); 284 } 285 return mEnergyConsumers; 286 } 287 } 288 PowerStatsService(Context context)289 public PowerStatsService(Context context) { 290 this(context, new Injector()); 291 } 292 293 @VisibleForTesting PowerStatsService(Context context, Injector injector)294 public PowerStatsService(Context context, Injector injector) { 295 super(context); 296 mContext = context; 297 mInjector = injector; 298 } 299 300 private final class LocalService extends PowerStatsInternal { 301 private final Handler mHandler; 302 LocalService()303 LocalService() { 304 mHandler = new Handler(getLooper()); 305 } 306 307 308 @Override getEnergyConsumerInfo()309 public EnergyConsumer[] getEnergyConsumerInfo() { 310 return getPowerStatsHal().getEnergyConsumerInfo(); 311 } 312 313 @Override getEnergyConsumedAsync( int[] energyConsumerIds)314 public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( 315 int[] energyConsumerIds) { 316 final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture<>(); 317 mHandler.sendMessage( 318 PooledLambda.obtainMessage(PowerStatsService.this::getEnergyConsumedAsync, 319 future, energyConsumerIds)); 320 return future; 321 } 322 323 @Override getPowerEntityInfo()324 public PowerEntity[] getPowerEntityInfo() { 325 return getPowerStatsHal().getPowerEntityInfo(); 326 } 327 328 @Override getStateResidencyAsync( int[] powerEntityIds)329 public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync( 330 int[] powerEntityIds) { 331 final CompletableFuture<StateResidencyResult[]> future = new CompletableFuture<>(); 332 mHandler.sendMessage( 333 PooledLambda.obtainMessage(PowerStatsService.this::getStateResidencyAsync, 334 future, powerEntityIds)); 335 return future; 336 } 337 338 @Override getEnergyMeterInfo()339 public Channel[] getEnergyMeterInfo() { 340 return getPowerStatsHal().getEnergyMeterInfo(); 341 } 342 343 @Override readEnergyMeterAsync( int[] channelIds)344 public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync( 345 int[] channelIds) { 346 final CompletableFuture<EnergyMeasurement[]> future = new CompletableFuture<>(); 347 mHandler.sendMessage( 348 PooledLambda.obtainMessage(PowerStatsService.this::readEnergyMeterAsync, 349 future, channelIds)); 350 return future; 351 } 352 } 353 getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future, int[] energyConsumerIds)354 private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future, 355 int[] energyConsumerIds) { 356 EnergyConsumerResult[] results = getPowerStatsHal().getEnergyConsumed(energyConsumerIds); 357 358 // STOPSHIP(253292374): Remove once missing EnergyConsumer results issue is resolved. 359 EnergyConsumer[] energyConsumers = getEnergyConsumerInfo(); 360 if (energyConsumers != null) { 361 final int expectedLength; 362 if (energyConsumerIds.length == 0) { 363 // Empty request is a request for all available EnergyConsumers. 364 expectedLength = energyConsumers.length; 365 } else { 366 expectedLength = energyConsumerIds.length; 367 } 368 369 if (results == null || expectedLength != results.length) { 370 // Mismatch in requested/received energy consumer data. 371 StringBuilder sb = new StringBuilder(); 372 sb.append("Requested ids:"); 373 if (energyConsumerIds.length == 0) { 374 sb.append("ALL"); 375 } 376 sb.append("["); 377 for (int i = 0; i < energyConsumerIds.length; i++) { 378 final int id = energyConsumerIds[i]; 379 sb.append(id); 380 sb.append("(type:"); 381 sb.append(energyConsumers[id].type); 382 sb.append(",ord:"); 383 sb.append(energyConsumers[id].ordinal); 384 sb.append(",name:"); 385 sb.append(energyConsumers[id].name); 386 sb.append(")"); 387 if (i != expectedLength - 1) { 388 sb.append(", "); 389 } 390 } 391 sb.append("]"); 392 393 sb.append(", Received result ids:"); 394 if (results == null) { 395 sb.append("null"); 396 } else { 397 sb.append("["); 398 final int resultLength = results.length; 399 for (int i = 0; i < resultLength; i++) { 400 final int id = results[i].id; 401 sb.append(id); 402 sb.append("(type:"); 403 sb.append(energyConsumers[id].type); 404 sb.append(",ord:"); 405 sb.append(energyConsumers[id].ordinal); 406 sb.append(",name:"); 407 sb.append(energyConsumers[id].name); 408 sb.append(")"); 409 if (i != resultLength - 1) { 410 sb.append(", "); 411 } 412 } 413 sb.append("]"); 414 } 415 Slog.wtf(TAG, "Missing result from getEnergyConsumedAsync call. " + sb); 416 } 417 } 418 future.complete(results); 419 } 420 getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future, int[] powerEntityIds)421 private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future, 422 int[] powerEntityIds) { 423 future.complete(getPowerStatsHal().getStateResidency(powerEntityIds)); 424 } 425 readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future, int[] channelIds)426 private void readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future, 427 int[] channelIds) { 428 future.complete(getPowerStatsHal().readEnergyMeter(channelIds)); 429 } 430 } 431