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