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