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