• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.power.stats;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.anyDouble;
22 import static org.mockito.ArgumentMatchers.eq;
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.spy;
25 import static org.mockito.Mockito.when;
26 
27 import android.content.Context;
28 import android.content.res.Resources;
29 import android.net.NetworkStats;
30 import android.os.BatteryConsumer;
31 import android.os.BatteryStats;
32 import android.os.BatteryUsageStats;
33 import android.os.BatteryUsageStatsQuery;
34 import android.os.ConditionVariable;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.UidBatteryConsumer;
38 import android.os.UserBatteryConsumer;
39 import android.platform.test.ravenwood.RavenwoodRule;
40 import android.util.SparseArray;
41 import android.util.Xml;
42 
43 import com.android.internal.os.CpuScalingPolicies;
44 import com.android.internal.os.PowerProfile;
45 import com.android.internal.power.EnergyConsumerStats;
46 
47 import org.junit.rules.TestRule;
48 import org.junit.runner.Description;
49 import org.junit.runners.model.Statement;
50 import org.mockito.stubbing.Answer;
51 import org.xmlpull.v1.XmlPullParser;
52 
53 import java.io.File;
54 import java.io.IOException;
55 import java.nio.file.Files;
56 import java.util.Arrays;
57 
58 @SuppressWarnings("SynchronizeOnNonFinalField")
59 public class BatteryUsageStatsRule implements TestRule {
60     public static final BatteryUsageStatsQuery POWER_PROFILE_MODEL_ONLY =
61             new BatteryUsageStatsQuery.Builder()
62                     .powerProfileModeledOnly()
63                     .includePowerModels()
64                     .build();
65 
66     private final PowerProfile mPowerProfile;
67     private final MockClock mMockClock = new MockClock();
68     private String mTestName;
69     private boolean mCreateTempDirectory;
70     private File mHistoryDir;
71     private MockBatteryStatsImpl mBatteryStats;
72     private Handler mHandler;
73 
74     private BatteryUsageStats mBatteryUsageStats;
75     private boolean mScreenOn;
76     private boolean mDefaultCpuScalingPolicy = true;
77     private SparseArray<int[]> mCpusByPolicy = new SparseArray<>();
78     private SparseArray<int[]> mFreqsByPolicy = new SparseArray<>();
79 
80     private int mDisplayCount = -1;
81     private int mPerUidModemModel = -1;
82     private NetworkStats mNetworkStats;
83     private boolean[] mSupportedStandardBuckets;
84     private String[] mCustomPowerComponentNames;
85     private Throwable mThrowable;
86     private final BatteryStatsImpl.BatteryStatsConfig.Builder mBatteryStatsConfigBuilder;
87 
BatteryUsageStatsRule()88     public BatteryUsageStatsRule() {
89         this(0);
90     }
91 
BatteryUsageStatsRule(long currentTime)92     public BatteryUsageStatsRule(long currentTime) {
93         mHandler = mock(Handler.class);
94         mPowerProfile = spy(new PowerProfile());
95         mMockClock.currentTime = currentTime;
96         mCpusByPolicy.put(0, new int[]{0, 1, 2, 3});
97         mCpusByPolicy.put(4, new int[]{4, 5, 6, 7});
98         mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
99         mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
100         mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder()
101                 .setPowerStatsThrottlePeriodMillis(
102                         BatteryConsumer.powerComponentIdToString(
103                                 BatteryConsumer.POWER_COMPONENT_CPU), 10000)
104                 .setPowerStatsThrottlePeriodMillis(
105                         BatteryConsumer.powerComponentIdToString(
106                                 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO), 10000);
107     }
108 
initBatteryStats()109     private void initBatteryStats() {
110         if (mBatteryStats != null) return;
111 
112         if (mCreateTempDirectory) {
113             try {
114                 mHistoryDir = Files.createTempDirectory(mTestName).toFile();
115             } catch (IOException e) {
116                 throw new RuntimeException(e);
117             }
118             clearDirectory();
119         }
120         mBatteryStats = new MockBatteryStatsImpl(mBatteryStatsConfigBuilder.build(),
121                 mMockClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
122         mBatteryStats.setPowerProfile(mPowerProfile);
123         mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
124         synchronized (mBatteryStats) {
125             mBatteryStats.initEnergyConsumerStatsLocked(mSupportedStandardBuckets,
126                     mCustomPowerComponentNames);
127         }
128         mBatteryStats.informThatAllExternalStatsAreFlushed();
129 
130         if (mDisplayCount != -1) {
131             mBatteryStats.setDisplayCountLocked(mDisplayCount);
132         }
133         if (mPerUidModemModel != -1) {
134             synchronized (mBatteryStats) {
135                 mBatteryStats.setPerUidModemModel(mPerUidModemModel);
136             }
137         }
138         if (mNetworkStats != null) {
139             mBatteryStats.setNetworkStats(mNetworkStats);
140         }
141     }
142 
getMockClock()143     public MockClock getMockClock() {
144         return mMockClock;
145     }
146 
getHandler()147     public Handler getHandler() {
148         return mHandler;
149     }
150 
getHistoryDir()151     public File getHistoryDir() {
152         return mHistoryDir;
153     }
154 
createTempDirectory()155     public BatteryUsageStatsRule createTempDirectory() {
156         mCreateTempDirectory = true;
157         return this;
158     }
159 
setTestPowerProfile(String resourceName)160     public BatteryUsageStatsRule setTestPowerProfile(String resourceName) {
161         mPowerProfile.initForTesting(resolveParser(resourceName));
162         return this;
163     }
164 
resolveParser(String resourceName)165     public static XmlPullParser resolveParser(String resourceName) {
166         if (RavenwoodRule.isOnRavenwood()) {
167             try {
168                 return Xml.resolvePullParser(BatteryUsageStatsRule.class.getClassLoader()
169                         .getResourceAsStream("res/xml/" + resourceName + ".xml"));
170             } catch (IOException e) {
171                 throw new RuntimeException(e);
172             }
173         } else {
174             Context context = androidx.test.InstrumentationRegistry.getContext();
175             Resources resources = context.getResources();
176             int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName());
177             return resources.getXml(resId);
178         }
179     }
180 
setCpuScalingPolicy(int policy, int[] relatedCpus, int[] frequencies)181     public BatteryUsageStatsRule setCpuScalingPolicy(int policy, int[] relatedCpus,
182             int[] frequencies) {
183         if (mDefaultCpuScalingPolicy) {
184             mCpusByPolicy.clear();
185             mFreqsByPolicy.clear();
186             mDefaultCpuScalingPolicy = false;
187         }
188         mCpusByPolicy.put(policy, relatedCpus);
189         mFreqsByPolicy.put(policy, frequencies);
190         if (mBatteryStats != null) {
191             mBatteryStats.setCpuScalingPolicies(
192                     new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
193         }
194         return this;
195     }
196 
setAveragePower(String key, double value)197     public BatteryUsageStatsRule setAveragePower(String key, double value) {
198         when(mPowerProfile.getAveragePower(key)).thenReturn(value);
199         when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble())).thenReturn(value);
200         return this;
201     }
202 
setAveragePowerUnspecified(String key)203     public BatteryUsageStatsRule setAveragePowerUnspecified(String key) {
204         when(mPowerProfile.getAveragePower(key)).thenReturn(0.0);
205         when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble()))
206                 .thenAnswer((Answer<Double>) invocation -> (Double) invocation.getArguments()[1]);
207         return this;
208     }
209 
setAveragePower(String key, double[] values)210     public BatteryUsageStatsRule setAveragePower(String key, double[] values) {
211         when(mPowerProfile.getNumElements(key)).thenReturn(values.length);
212         for (int i = 0; i < values.length; i++) {
213             when(mPowerProfile.getAveragePower(key, i)).thenReturn(values[i]);
214         }
215         return this;
216     }
217 
setAveragePowerForCpuScalingPolicy(int policy, double value)218     public BatteryUsageStatsRule setAveragePowerForCpuScalingPolicy(int policy, double value) {
219         when(mPowerProfile.getAveragePowerForCpuScalingPolicy(policy)).thenReturn(value);
220         return this;
221     }
222 
setAveragePowerForCpuScalingStep(int policy, int step, double value)223     public BatteryUsageStatsRule setAveragePowerForCpuScalingStep(int policy, int step,
224             double value) {
225         when(mPowerProfile.getAveragePowerForCpuScalingStep(policy, step)).thenReturn(value);
226         return this;
227     }
228 
229     /**
230      * Mocks the CPU bracket count
231      */
setCpuPowerBracketCount(int count)232     public BatteryUsageStatsRule setCpuPowerBracketCount(int count) {
233         when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(count);
234         return this;
235     }
236 
237     /**
238      * Mocks the CPU bracket for the given CPU scaling policy and step
239      */
setCpuPowerBracket(int policy, int step, int bracket)240     public BatteryUsageStatsRule setCpuPowerBracket(int policy, int step, int bracket) {
241         when(mPowerProfile.getCpuPowerBracketForScalingStep(policy, step)).thenReturn(bracket);
242         return this;
243     }
244 
setAveragePowerForOrdinal(String group, int ordinal, double value)245     public BatteryUsageStatsRule setAveragePowerForOrdinal(String group, int ordinal,
246             double value) {
247         when(mPowerProfile.getAveragePowerForOrdinal(group, ordinal)).thenReturn(value);
248         when(mPowerProfile.getAveragePowerForOrdinal(eq(group), eq(ordinal),
249                 anyDouble())).thenReturn(value);
250         return this;
251     }
252 
setNumDisplays(int value)253     public BatteryUsageStatsRule setNumDisplays(int value) {
254         when(mPowerProfile.getNumDisplays()).thenReturn(value);
255         mDisplayCount = value;
256         if (mBatteryStats != null) {
257             mBatteryStats.setDisplayCountLocked(mDisplayCount);
258         }
259         return this;
260     }
261 
setPerUidModemModel(int perUidModemModel)262     public BatteryUsageStatsRule setPerUidModemModel(int perUidModemModel) {
263         mPerUidModemModel = perUidModemModel;
264         if (mBatteryStats != null) {
265             synchronized (mBatteryStats) {
266                 mBatteryStats.setPerUidModemModel(mPerUidModemModel);
267             }
268         }
269         return this;
270     }
271 
272     /** Call only after setting the power profile information. */
initMeasuredEnergyStatsLocked()273     public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() {
274         return initMeasuredEnergyStatsLocked(new String[0]);
275     }
276 
277     /** Call only after setting the power profile information. */
initMeasuredEnergyStatsLocked( String[] customPowerComponentNames)278     public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(
279             String[] customPowerComponentNames) {
280         mCustomPowerComponentNames = customPowerComponentNames;
281         mSupportedStandardBuckets = new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
282         Arrays.fill(mSupportedStandardBuckets, true);
283         if (mBatteryStats != null) {
284             synchronized (mBatteryStats) {
285                 mBatteryStats.initEnergyConsumerStatsLocked(mSupportedStandardBuckets,
286                         mCustomPowerComponentNames);
287                 mBatteryStats.informThatAllExternalStatsAreFlushed();
288             }
289         }
290         return this;
291     }
292 
setPowerStatsThrottlePeriodMillis(int powerComponent, long throttleMs)293     public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent,
294             long throttleMs) {
295         mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(
296                 BatteryConsumer.powerComponentIdToString(powerComponent), throttleMs);
297         return this;
298     }
299 
startWithScreenOn(boolean screenOn)300     public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
301         mScreenOn = screenOn;
302         return this;
303     }
304 
setNetworkStats(NetworkStats networkStats)305     public void setNetworkStats(NetworkStats networkStats) {
306         mNetworkStats = networkStats;
307         if (mBatteryStats != null) {
308             mBatteryStats.setNetworkStats(mNetworkStats);
309         }
310     }
311 
312     @Override
apply(Statement base, Description description)313     public Statement apply(Statement base, Description description) {
314         mTestName = description.getClassName() + "#" + description.getMethodName();
315         return new Statement() {
316             @Override
317             public void evaluate() throws Throwable {
318                 before();
319                 base.evaluate();
320                 after();
321             }
322         };
323     }
324 
325     private void before() {
326         HandlerThread bgThread = new HandlerThread("bg thread");
327         bgThread.setUncaughtExceptionHandler((thread, throwable)-> {
328             mThrowable = throwable;
329         });
330         bgThread.start();
331         mHandler = new Handler(bgThread.getLooper());
332 
333         initBatteryStats();
334         mBatteryStats.setOnBatteryInternal(true);
335         mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
336         mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
337     }
338 
339     private void after() throws Throwable {
340         waitForBackgroundThread();
341     }
342 
343     public void waitForBackgroundThread() throws Throwable {
344         if (mThrowable != null) {
345             throw mThrowable;
346         }
347 
348         ConditionVariable done = new ConditionVariable();
349         if (mHandler.post(done::open)) {
350             boolean success = done.block(5000);
351             if (mThrowable != null) {
352                 throw mThrowable;
353             }
354             assertThat(success).isTrue();
355         }
356     }
357 
358     public PowerProfile getPowerProfile() {
359         return mPowerProfile;
360     }
361 
362     public CpuScalingPolicies getCpuScalingPolicies() {
363         synchronized (mBatteryStats) {
364             return mBatteryStats.getCpuScalingPolicies();
365         }
366     }
367 
368     public MockBatteryStatsImpl getBatteryStats() {
369         if (mBatteryStats == null) {
370             initBatteryStats();
371         }
372         return mBatteryStats;
373     }
374 
375     public BatteryStatsImpl.Uid getUidStats(int uid) {
376         return mBatteryStats.getUidStatsLocked(uid);
377     }
378 
379     public void setTime(long realtimeMs, long uptimeMs) {
380         mMockClock.currentTime = realtimeMs;
381         mMockClock.realtime = realtimeMs;
382         mMockClock.uptime = uptimeMs;
383     }
384 
385     public void setCurrentTime(long currentTimeMs) {
386         mMockClock.currentTime = currentTimeMs;
387     }
388 
389     BatteryUsageStats apply(PowerCalculator... calculators) {
390         return apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
391                 calculators);
392     }
393 
394     BatteryUsageStats apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) {
395         final String[] customPowerComponentNames = mBatteryStats.getCustomEnergyConsumerNames();
396         final boolean includePowerModels = (query.getFlags()
397                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
398         final boolean includeProcessStateData = (query.getFlags()
399                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0;
400         final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
401         BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
402                 customPowerComponentNames, includePowerModels, includeProcessStateData,
403                 minConsumedPowerThreshold);
404         SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
405         for (int i = 0; i < uidStats.size(); i++) {
406             builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
407         }
408 
409         for (PowerCalculator calculator : calculators) {
410             calculator.calculate(builder, mBatteryStats, mMockClock.realtime * 1000,
411                     mMockClock.uptime * 1000, query);
412         }
413 
414         mBatteryUsageStats = builder.build();
415         return mBatteryUsageStats;
416     }
417 
418     public BatteryConsumer getDeviceBatteryConsumer() {
419         return mBatteryUsageStats.getAggregateBatteryConsumer(
420                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
421     }
422 
423     public BatteryConsumer getAppsBatteryConsumer() {
424         return mBatteryUsageStats.getAggregateBatteryConsumer(
425                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
426     }
427 
428     public UidBatteryConsumer getUidBatteryConsumer(int uid) {
429         for (UidBatteryConsumer ubc : mBatteryUsageStats.getUidBatteryConsumers()) {
430             if (ubc.getUid() == uid) {
431                 return ubc;
432             }
433         }
434         return null;
435     }
436 
437     public UserBatteryConsumer getUserBatteryConsumer(int userId) {
438         for (UserBatteryConsumer ubc : mBatteryUsageStats.getUserBatteryConsumers()) {
439             if (ubc.getUserId() == userId) {
440                 return ubc;
441             }
442         }
443         return null;
444     }
445 
446     public void clearDirectory() {
447         clearDirectory(mHistoryDir);
448     }
449 
450     private void clearDirectory(File dir) {
451         if (dir.exists()) {
452             for (File child : dir.listFiles()) {
453                 if (child.isDirectory()) {
454                     clearDirectory(child);
455                 }
456                 child.delete();
457             }
458         }
459     }
460 }
461