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