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