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.app.StatsManager; 20 import android.content.Context; 21 import android.hardware.power.stats.Channel; 22 import android.hardware.power.stats.EnergyMeasurement; 23 import android.hardware.power.stats.PowerEntity; 24 import android.hardware.power.stats.State; 25 import android.hardware.power.stats.StateResidency; 26 import android.hardware.power.stats.StateResidencyResult; 27 import android.power.PowerStatsInternal; 28 import android.util.Slog; 29 import android.util.StatsEvent; 30 31 import com.android.internal.util.ConcurrentUtils; 32 import com.android.internal.util.FrameworkStatsLog; 33 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.concurrent.TimeUnit; 38 39 /** 40 * StatsPullAtomCallbackImpl is responsible implementing the stats pullers for 41 * SUBSYSTEM_SLEEP_STATE and ON_DEVICE_POWER_MEASUREMENT statsd atoms. 42 */ 43 public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback { 44 private static final String TAG = StatsPullAtomCallbackImpl.class.getSimpleName(); 45 private Context mContext; 46 private PowerStatsInternal mPowerStatsInternal; 47 private Map<Integer, Channel> mChannels = new HashMap(); 48 private Map<Integer, String> mEntityNames = new HashMap(); 49 private Map<Integer, Map<Integer, String>> mStateNames = new HashMap(); 50 private static final int STATS_PULL_TIMEOUT_MILLIS = 2000; 51 private static final boolean DEBUG = false; 52 53 @Override onPullAtom(int atomTag, List<StatsEvent> data)54 public int onPullAtom(int atomTag, List<StatsEvent> data) { 55 switch (atomTag) { 56 case FrameworkStatsLog.SUBSYSTEM_SLEEP_STATE: 57 return pullSubsystemSleepState(atomTag, data); 58 case FrameworkStatsLog.ON_DEVICE_POWER_MEASUREMENT: 59 return pullOnDevicePowerMeasurement(atomTag, data); 60 default: 61 throw new UnsupportedOperationException("Unknown tagId=" + atomTag); 62 } 63 } 64 initPullOnDevicePowerMeasurement()65 private boolean initPullOnDevicePowerMeasurement() { 66 Channel[] channels = mPowerStatsInternal.getEnergyMeterInfo(); 67 if (channels == null || channels.length == 0) { 68 Slog.e(TAG, "Failed to init OnDevicePowerMeasurement puller"); 69 return false; 70 } 71 72 for (int i = 0; i < channels.length; i++) { 73 final Channel channel = channels[i]; 74 mChannels.put(channel.id, channel); 75 } 76 77 return true; 78 } 79 pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events)80 private int pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events) { 81 final EnergyMeasurement[] energyMeasurements; 82 try { 83 energyMeasurements = mPowerStatsInternal.readEnergyMeterAsync(new int[0]) 84 .get(STATS_PULL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 85 } catch (Exception e) { 86 Slog.e(TAG, "Failed to readEnergyMeterAsync", e); 87 return StatsManager.PULL_SKIP; 88 } 89 90 if (energyMeasurements == null) return StatsManager.PULL_SKIP; 91 92 for (int i = 0; i < energyMeasurements.length; i++) { 93 // Only report energy measurements that have been accumulated since boot 94 final EnergyMeasurement energyMeasurement = energyMeasurements[i]; 95 if (energyMeasurement.durationMs == energyMeasurement.timestampMs) { 96 events.add(FrameworkStatsLog.buildStatsEvent( 97 atomTag, 98 mChannels.get(energyMeasurement.id).subsystem, 99 mChannels.get(energyMeasurement.id).name, 100 energyMeasurement.durationMs, 101 energyMeasurement.energyUWs)); 102 } 103 } 104 105 return StatsManager.PULL_SUCCESS; 106 } 107 initSubsystemSleepState()108 private boolean initSubsystemSleepState() { 109 PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo(); 110 if (entities == null || entities.length == 0) { 111 Slog.e(TAG, "Failed to init SubsystemSleepState puller"); 112 return false; 113 } 114 115 for (int i = 0; i < entities.length; i++) { 116 final PowerEntity entity = entities[i]; 117 Map<Integer, String> states = new HashMap(); 118 for (int j = 0; j < entity.states.length; j++) { 119 final State state = entity.states[j]; 120 states.put(state.id, state.name); 121 } 122 123 mEntityNames.put(entity.id, entity.name); 124 mStateNames.put(entity.id, states); 125 } 126 127 return true; 128 } 129 pullSubsystemSleepState(int atomTag, List<StatsEvent> events)130 private int pullSubsystemSleepState(int atomTag, List<StatsEvent> events) { 131 final StateResidencyResult[] results; 132 try { 133 results = mPowerStatsInternal.getStateResidencyAsync(new int[0]) 134 .get(STATS_PULL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 135 } catch (Exception e) { 136 Slog.e(TAG, "Failed to getStateResidencyAsync", e); 137 return StatsManager.PULL_SKIP; 138 } 139 140 if (results == null) return StatsManager.PULL_SKIP; 141 142 for (int i = 0; i < results.length; i++) { 143 final StateResidencyResult result = results[i]; 144 for (int j = 0; j < result.stateResidencyData.length; j++) { 145 final StateResidency stateResidency = result.stateResidencyData[j]; 146 events.add(FrameworkStatsLog.buildStatsEvent( 147 atomTag, 148 mEntityNames.get(result.id), 149 mStateNames.get(result.id).get(stateResidency.id), 150 stateResidency.totalStateEntryCount, 151 stateResidency.totalTimeInStateMs)); 152 } 153 } 154 155 return StatsManager.PULL_SUCCESS; 156 } 157 StatsPullAtomCallbackImpl(Context context, PowerStatsInternal powerStatsInternal)158 public StatsPullAtomCallbackImpl(Context context, PowerStatsInternal powerStatsInternal) { 159 if (DEBUG) Slog.d(TAG, "Starting PowerStatsService statsd pullers"); 160 161 mContext = context; 162 mPowerStatsInternal = powerStatsInternal; 163 164 if (powerStatsInternal == null) { 165 Slog.e(TAG, "Failed to start PowerStatsService statsd pullers"); 166 return; 167 } 168 169 StatsManager manager = mContext.getSystemService(StatsManager.class); 170 171 if (initPullOnDevicePowerMeasurement()) { 172 manager.setPullAtomCallback( 173 FrameworkStatsLog.ON_DEVICE_POWER_MEASUREMENT, 174 null, // use default PullAtomMetadata values 175 ConcurrentUtils.DIRECT_EXECUTOR, 176 this); 177 } 178 179 if (initSubsystemSleepState()) { 180 manager.setPullAtomCallback( 181 FrameworkStatsLog.SUBSYSTEM_SLEEP_STATE, 182 null, // use default PullAtomMetadata values 183 ConcurrentUtils.DIRECT_EXECUTOR, 184 this); 185 } 186 } 187 } 188