• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.server.power.stats.processor;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.BatteryConsumer;
22 
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.function.Supplier;
28 
29 /**
30  * Configuration that controls how power stats are aggregated.  It determines which state changes
31  * are to be considered as essential dimensions ("tracked states") for each power component (CPU,
32  * WiFi, etc).  Also, it determines which states are tracked globally and which ones on a per-UID
33  * basis.
34  */
35 class AggregatedPowerStatsConfig {
36     public static final int STATE_POWER = 0;
37     public static final int STATE_SCREEN = 1;
38     public static final int STATE_PROCESS_STATE = 2;
39 
40     @IntDef({
41             STATE_POWER,
42             STATE_SCREEN,
43             STATE_PROCESS_STATE,
44     })
45     @Retention(RetentionPolicy.SOURCE)
46     public @interface TrackedState {
47     }
48 
49     static final String STATE_NAME_POWER = "pwr";
50     static final int POWER_STATE_BATTERY = 0;
51     static final int POWER_STATE_OTHER = 1;   // Plugged in, or on wireless charger, etc.
52     static final String[] STATE_LABELS_POWER = {"pwr-battery", "pwr-other"};
53 
54     static final String STATE_NAME_SCREEN = "scr";
55     static final int SCREEN_STATE_ON = 0;
56     static final int SCREEN_STATE_OTHER = 1;  // Off, doze etc
57     static final String[] STATE_LABELS_SCREEN = {"scr-on", "scr-other"};
58 
59     static final String STATE_NAME_PROCESS_STATE = "ps";
60     static final String[] STATE_LABELS_PROCESS_STATE;
61 
62     static {
63         String[] procStateLabels = new String[BatteryConsumer.PROCESS_STATE_COUNT];
64         for (int i = 0; i < BatteryConsumer.PROCESS_STATE_COUNT; i++) {
65             procStateLabels[i] = BatteryConsumer.processStateToString(i);
66         }
67         STATE_LABELS_PROCESS_STATE = procStateLabels;
68     }
69 
70     /**
71      * Configuration for a give power component (CPU, WiFi, etc)
72      */
73     static class PowerComponent {
74         private final int mPowerComponentId;
75         private @TrackedState int[] mTrackedDeviceStates;
76         private @TrackedState int[] mTrackedUidStates;
77         private Supplier<PowerStatsProcessor> mProcessorSupplier;
78 
PowerComponent(int powerComponentId)79         PowerComponent(int powerComponentId) {
80             this.mPowerComponentId = powerComponentId;
81         }
82 
83         /**
84          * Configures which states should be tracked as separate dimensions for the entire device.
85          */
trackDeviceStates(@rackedState int... states)86         public PowerComponent trackDeviceStates(@TrackedState int... states) {
87             if (mTrackedDeviceStates != null) {
88                 throw new IllegalStateException("Component is already configured");
89             }
90             mTrackedDeviceStates = states;
91             return this;
92         }
93 
94         /**
95          * Configures which states should be tracked as separate dimensions on a per-UID basis.
96          */
trackUidStates(@rackedState int... states)97         public PowerComponent trackUidStates(@TrackedState int... states) {
98             if (mTrackedUidStates != null) {
99                 throw new IllegalStateException("Component is already configured");
100             }
101             mTrackedUidStates = states;
102             return this;
103         }
104 
105         /**
106          * A PowerStatsProcessor takes an object that should be invoked for every aggregated
107          * stats span before giving the aggregates stats to consumers. The processor can complete
108          * the aggregation process, for example by computing estimated power usage.
109          */
setProcessorSupplier( @onNull Supplier<PowerStatsProcessor> processorSupplier)110         public PowerComponent setProcessorSupplier(
111                 @NonNull Supplier<PowerStatsProcessor> processorSupplier) {
112             mProcessorSupplier = processorSupplier;
113             return this;
114         }
115 
getPowerComponentId()116         public int getPowerComponentId() {
117             return mPowerComponentId;
118         }
119 
getDeviceStateConfig()120         public MultiStateStats.States[] getDeviceStateConfig() {
121             return new MultiStateStats.States[]{
122                     new MultiStateStats.States(STATE_NAME_POWER,
123                             isTracked(mTrackedDeviceStates, STATE_POWER),
124                             STATE_LABELS_POWER),
125                     new MultiStateStats.States(STATE_NAME_SCREEN,
126                             isTracked(mTrackedDeviceStates, STATE_SCREEN),
127                             STATE_LABELS_SCREEN),
128             };
129         }
130 
getUidStateConfig()131         public MultiStateStats.States[] getUidStateConfig() {
132             return new MultiStateStats.States[]{
133                     new MultiStateStats.States(STATE_NAME_POWER,
134                             isTracked(mTrackedUidStates, STATE_POWER),
135                             AggregatedPowerStatsConfig.STATE_LABELS_POWER),
136                     new MultiStateStats.States(STATE_NAME_SCREEN,
137                             isTracked(mTrackedUidStates, STATE_SCREEN),
138                             AggregatedPowerStatsConfig.STATE_LABELS_SCREEN),
139                     new MultiStateStats.States(STATE_NAME_PROCESS_STATE,
140                             isTracked(mTrackedUidStates, STATE_PROCESS_STATE),
141                             AggregatedPowerStatsConfig.STATE_LABELS_PROCESS_STATE),
142             };
143         }
144 
145         @NonNull
createProcessor()146         PowerStatsProcessor createProcessor() {
147             if (mProcessorSupplier == null) {
148                 return NO_OP_PROCESSOR;
149             }
150             return mProcessorSupplier.get();
151         }
152 
isTracked(int[] trackedStates, int state)153         private boolean isTracked(int[] trackedStates, int state) {
154             if (trackedStates == null) {
155                 return false;
156             }
157 
158             for (int trackedState : trackedStates) {
159                 if (trackedState == state) {
160                     return true;
161                 }
162             }
163             return false;
164         }
165 
166     }
167 
168     private final List<PowerComponent> mPowerComponents = new ArrayList<>();
169     private PowerComponent mCustomPowerComponent;
170     private Supplier<PowerStatsProcessor> mCustomPowerStatsProcessorFactory;
171 
172     /**
173      * Creates a configuration for the specified power component, which may be one of the
174      * standard power component IDs, e.g. {@link BatteryConsumer#POWER_COMPONENT_CPU}, or
175      * a custom power component.
176      */
trackPowerComponent(@atteryConsumer.PowerComponentId int powerComponentId)177     PowerComponent trackPowerComponent(@BatteryConsumer.PowerComponentId int powerComponentId) {
178         PowerComponent builder = new PowerComponent(powerComponentId);
179         mPowerComponents.add(builder);
180         return builder;
181     }
182 
183     /**
184      * Creates a configuration for the specified power component, whose attribution calculation
185      * depends on a different power component.  The tracked states will be the same as the
186      * "dependsOn" component's.
187      */
trackPowerComponent(@atteryConsumer.PowerComponentId int powerComponentId, @BatteryConsumer.PowerComponentId int dependsOnPowerComponentId)188     PowerComponent trackPowerComponent(@BatteryConsumer.PowerComponentId int powerComponentId,
189             @BatteryConsumer.PowerComponentId int dependsOnPowerComponentId) {
190         PowerComponent dependsOnPowerComponent = null;
191         for (int i = 0; i < mPowerComponents.size(); i++) {
192             PowerComponent powerComponent = mPowerComponents.get(i);
193             if (powerComponent.getPowerComponentId() == dependsOnPowerComponentId) {
194                 dependsOnPowerComponent = powerComponent;
195                 break;
196             }
197         }
198 
199         if (dependsOnPowerComponent == null) {
200             throw new IllegalArgumentException(
201                     "Required component " + dependsOnPowerComponentId + " is not configured");
202         }
203 
204         PowerComponent powerComponent = trackPowerComponent(powerComponentId);
205         powerComponent.mTrackedDeviceStates = dependsOnPowerComponent.mTrackedDeviceStates;
206         powerComponent.mTrackedUidStates = dependsOnPowerComponent.mTrackedUidStates;
207         return powerComponent;
208     }
209 
210     /**
211      * Creates a configuration for custom power components, which are yet to be discovered
212      * dynamically through the integration with PowerStatsService.
213      */
trackCustomPowerComponents( Supplier<PowerStatsProcessor> processorFactory)214     PowerComponent trackCustomPowerComponents(
215             Supplier<PowerStatsProcessor> processorFactory) {
216         mCustomPowerStatsProcessorFactory = processorFactory;
217         mCustomPowerComponent = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
218         return mCustomPowerComponent;
219     }
220 
221     /**
222      * Returns configurations for all registered or dynamically discovered power components.
223      */
getPowerComponentsAggregatedStatsConfigs()224     List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
225         return mPowerComponents;
226     }
227 
228     /**
229      * Creates a configuration for a custom power component discovered dynamically through the
230      * integration with PowerStatsService.
231      */
232     @Nullable
createPowerComponent(int powerComponentId)233     PowerComponent createPowerComponent(int powerComponentId) {
234         if (mCustomPowerComponent == null) {
235             return null;
236         }
237 
238         PowerComponent powerComponent = new PowerComponent(powerComponentId);
239         powerComponent.trackDeviceStates(mCustomPowerComponent.mTrackedDeviceStates);
240         powerComponent.trackUidStates(mCustomPowerComponent.mTrackedUidStates);
241 
242         if (mCustomPowerStatsProcessorFactory != null) {
243             powerComponent.setProcessorSupplier(mCustomPowerStatsProcessorFactory);
244         }
245 
246         return powerComponent;
247     }
248 
249     private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
250         @Override
251         void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
252         }
253     };
254 }
255